using System.Text.Json.Serialization;
using System.Text.Json;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using Microsoft.AspNetCore.Mvc;

namespace Common.WebApi.Middlewares;

public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IHostEnvironment _host;
    private readonly ILogger _logger;
    private readonly JsonSerializerOptions _serializerOptions;

    public ExceptionMiddleware(RequestDelegate next, IHostEnvironment host, ILogger<ExceptionMiddleware> logger)
    {
        _logger = logger;
        _next = next;
        _host = host;
        _serializerOptions = new JsonSerializerOptions
        {
            Converters = {
                    new JsonStringEnumConverter()
                },
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
        };
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        try
        {
            //if (httpContext.Request.Method == "POST")
            //{
            //    using var streamreader = new System.IO.StreamReader(httpContext.Request.Body);
            //    var body = await streamreader.ReadToEndAsync();
            //}
            await _next(httpContext);
        }
        catch (Exception exc)
        {
            var title = exc.Message;
            var detail = "Unexpected error occured";
            if (!_host.IsProduction()) detail = GetExceptionDetail(exc, false, true);
            if (exc is MongoException dbUpdateException)
            {
                title = "Database error";
                if (!_host.IsProduction()) detail = GetExceptionDetail(dbUpdateException, true, true);
            }
            else if (exc.Source?.Contains("Mongo") == true)
            {
                title = "Database error";
                if (!_host.IsProduction()) detail = GetExceptionDetail(exc, true, true);
            }

            var problemDetail = new ProblemDetails
            {
                Title = title,
                Detail = detail,
                Instance = httpContext.Request.Path,
                Status = StatusCodes.Status400BadRequest
            };

            _logger.LogError("Необработанная ошибка: {Message}", exc.Message);
            httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
            httpContext.Response.ContentType = "application/problem+json";
            await httpContext.Response.WriteAsync(JsonSerializer.Serialize(problemDetail, _serializerOptions));
        }
    }

    private string GetExceptionDetail(Exception exc, bool includeMessage, bool includeInner)
    {
        var sb = new StringBuilder();
        if (!string.IsNullOrWhiteSpace(exc.Message) && includeMessage) sb.Append(exc.Message);
        if (!string.IsNullOrWhiteSpace(exc.InnerException?.Message) && includeInner)
        {
            if (sb.Length > 0) sb.AppendLine();
            sb.AppendLine("Inner:");
            sb.Append(exc.InnerException.Message);
        }
        if (sb.Length > 0) sb.AppendLine();
        sb.Append(exc.StackTrace);
        return sb.ToString();
    }
}