using System.Net;
using System.Text.Json;
using SolutionErp.Application.Common.Exceptions;

namespace SolutionErp.Api.Middleware;

public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;

    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleAsync(context, ex);
        }
    }

    private async Task HandleAsync(HttpContext context, Exception ex)
    {
        var (status, type, title) = ex switch
        {
            ValidationException => ((int)HttpStatusCode.BadRequest, "https://tools.ietf.org/html/rfc9110#section-15.5.1", "Dữ liệu không hợp lệ"),
            NotFoundException => ((int)HttpStatusCode.NotFound, "https://tools.ietf.org/html/rfc9110#section-15.5.5", "Không tìm thấy"),
            UnauthorizedException => ((int)HttpStatusCode.Unauthorized, "https://tools.ietf.org/html/rfc9110#section-15.5.2", "Chưa xác thực"),
            ForbiddenException => ((int)HttpStatusCode.Forbidden, "https://tools.ietf.org/html/rfc9110#section-15.5.4", "Bị từ chối"),
            ConflictException => ((int)HttpStatusCode.Conflict, "https://tools.ietf.org/html/rfc9110#section-15.5.10", "Xung đột dữ liệu"),
            _ => ((int)HttpStatusCode.InternalServerError, "https://tools.ietf.org/html/rfc9110#section-15.6.1", "Lỗi hệ thống"),
        };

        if (status >= 500)
            _logger.LogError(ex, "Unhandled exception: {Message}", ex.Message);
        else
            _logger.LogWarning(ex, "Handled exception: {Message}", ex.Message);

        var problem = new
        {
            type,
            title,
            status,
            detail = ex.Message,
            errors = (ex as ValidationException)?.Errors,
            traceId = context.TraceIdentifier,
        };

        context.Response.ContentType = "application/problem+json";
        context.Response.StatusCode = status;
        await context.Response.WriteAsync(JsonSerializer.Serialize(problem, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        }));
    }
}
