using DocumentOperator.Domain.Common.Exceptions;
using DocumentOperator.Domain.Exceptions;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using System.Net;
using System.Text.Json;

namespace DocumentOperator.API.Middleware;

/// <summary>
/// Central exception handling middleware
/// Maps exceptions to HTTP status codes and RFC 7807 Problem Details
/// </summary>
public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionHandlingMiddleware> _logger;

    /// <summary>
    /// Initializes a new instance of the <see cref="ExceptionHandlingMiddleware"/> class.
    /// </summary>
    /// <param name="next">The next middleware in the pipeline.</param>
    /// <param name="logger">The logger instance for exception logging.</param>
    public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    /// <summary>
    /// Invokes the middleware to handle incoming HTTP requests and catch exceptions.
    /// </summary>
    /// <param name="context">The HTTP context for the current request.</param>
    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled exception: {ExceptionMessage}", ex.Message);
            await HandleExceptionAsync(context, ex);
        }
    }

    private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var (statusCode, problemDetails) = MapExceptionToProblemDetails(exception, context);

        context.Response.StatusCode = (int)statusCode;
        context.Response.ContentType = "application/problem+json";

        var options = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        };

        await context.Response.WriteAsync(JsonSerializer.Serialize(problemDetails, options));
    }

    private static (HttpStatusCode StatusCode, ProblemDetails ProblemDetails) MapExceptionToProblemDetails(
        Exception exception,
        HttpContext context)
    {
        return exception switch
        {
            // FluentValidation (400 Bad Request)
            ValidationException validationEx => (
                HttpStatusCode.BadRequest,
                new ProblemDetails
                {
                    Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
                    Title = "Validation Error",
                    Status = (int)HttpStatusCode.BadRequest,
                    Detail = string.Join("; ", validationEx.Errors.Select(e => e.ErrorMessage)),
                    Instance = context.Request.Path
                }
            ),

            // Domain Validation Exception (400 Bad Request)
            DomainValidationException domainEx => (
                HttpStatusCode.BadRequest,
                new ProblemDetails
                {
                    Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
                    Title = "Domain Validation Error",
                    Status = (int)HttpStatusCode.BadRequest,
                    Detail = domainEx.Message,
                    Instance = context.Request.Path
                }
            ),

            // Not Found Exception (404 Not Found)
            NotFoundException notFoundEx => (
                HttpStatusCode.NotFound,
                new ProblemDetails
                {
                    Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
                    Title = "Resource Not Found",
                    Status = (int)HttpStatusCode.NotFound,
                    Detail = notFoundEx.Message,
                    Instance = context.Request.Path
                }
            ),

            // Swiss QR Code Not Found Exception (404 Not Found)
            SwissQrCodeNotFoundException qrNotFoundEx => (
                HttpStatusCode.NotFound,
                new ProblemDetails
                {
                    Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
                    Title = "Swiss QR Code Not Found",
                    Status = (int)HttpStatusCode.NotFound,
                    Detail = qrNotFoundEx.Message,
                    Instance = context.Request.Path
                }
            ),

            // PDF Processing Exception (500 Internal Server Error)
            PdfProcessingException pdfEx => (
                HttpStatusCode.InternalServerError,
                new ProblemDetails
                {
                    Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1",
                    Title = "PDF Processing Error",
                    Status = (int)HttpStatusCode.InternalServerError,
                    Detail = pdfEx.Message,
                    Instance = context.Request.Path
                }
            ),

            // Generic Exception (500 Internal Server Error)
            _ => (
                HttpStatusCode.InternalServerError,
                new ProblemDetails
                {
                    Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1",
                    Title = "Internal Server Error",
                    Status = (int)HttpStatusCode.InternalServerError,
                    Detail = "An unexpected error occurred. Please contact support.",
                    Instance = context.Request.Path
                }
            )
        };
    }
}
