using System.Text.Json;
using Microsoft.AspNetCore.Mvc;

namespace ROLAC.API.Middleware;

/// <summary>
/// Catches any unhandled exception from the downstream pipeline, logs it (which flows through
/// the DB sink into SystemLogs at Error level with full stack + StatusCode 500), and returns a
/// clean RFC7807 problem+json response. Stack traces are never leaked to the client outside
/// Development. Registered as the FIRST middleware so it also catches auth/authorization faults.
/// </summary>
public sealed class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionHandlingMiddleware> _logger;
    private readonly IHostEnvironment _env;

    public ExceptionHandlingMiddleware(
        RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger, IHostEnvironment env)
    {
        _next   = next;
        _logger = logger;
        _env    = env;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (OperationCanceledException) when (context.RequestAborted.IsCancellationRequested)
        {
            // The client went away — not a server error; don't log as 500.
            return;
        }
        catch (Exception ex)
        {
            await HandleAsync(context, ex);
        }
    }

    private async Task HandleAsync(HttpContext context, Exception exception)
    {
        // Logged here → picked up by the DB sink (Error ≥ Warning floor) with full ex.ToString().
        _logger.LogError(
            exception,
            "Unhandled exception for {Method} {Path} (traceId {TraceId})",
            context.Request.Method, context.Request.Path, context.TraceIdentifier);

        if (context.Response.HasStarted)
        {
            // Too late to write a clean body; the log row above is still captured.
            return;
        }

        var problem = new ProblemDetails
        {
            Status = StatusCodes.Status500InternalServerError,
            Title  = "An unexpected error occurred.",
            Type   = "https://tools.ietf.org/html/rfc7231#section-6.6.1",
        };
        problem.Extensions["traceId"] = context.TraceIdentifier;
        if (_env.IsDevelopment())
            problem.Detail = exception.ToString();

        context.Response.Clear();
        context.Response.StatusCode  = StatusCodes.Status500InternalServerError;
        context.Response.ContentType = "application/problem+json";

        await context.Response.WriteAsync(JsonSerializer.Serialize(problem, JsonSerializerOptions));
    }

    private static readonly JsonSerializerOptions JsonSerializerOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    };
}
