using System.Net;
using System.Text.Json;
using MyNewProjectName.Contracts.DTOs.Responses;
using MyNewProjectName.Domain.Exceptions;
using Serilog;

namespace MyNewProjectName.AdminAPI.Middleware;

/// <summary>
/// Global exception handling middleware
/// Catches all unhandled exceptions, logs them to Serilog with full details,
/// and returns a standardized error response without exposing stack traces
/// </summary>
public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly Serilog.ILogger _logger;

    public ExceptionHandlingMiddleware(RequestDelegate next, Serilog.ILogger logger)
    {
        _next = next;
        _logger = logger;
    }

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

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        // Get correlation ID from header (set by CorrelationIdMiddleware)
        var correlationId = context.Request.Headers["X-Correlation-ID"].FirstOrDefault() ?? "unknown";

        // Log full exception details to Serilog (including stack trace)
        // This will be searchable by CorrelationId
        _logger.Error(
            exception,
            "Unhandled exception occurred. CorrelationId: {CorrelationId}, Path: {Path}, Method: {Method}",
            correlationId,
            context.Request.Path,
            context.Request.Method
        );

        // Safety check: If response has already started, we cannot modify headers/status code
        // This prevents InvalidOperationException: "Headers are read-only, response has already started"
        if (context.Response.HasStarted)
        {
            _logger.Warning(
                "Response has already started. Cannot modify HTTP status code or headers. CorrelationId: {CorrelationId}",
                correlationId
            );
            return;
        }

        var response = context.Response;
        
        // Clear any existing response data
        response.Clear();
        
        response.ContentType = "application/json";

        // Determine status code and user-friendly message
        var (statusCode, message, errors) = exception switch
        {
            ValidationException validationEx => (
                HttpStatusCode.BadRequest,
                "Validation failed",
                validationEx.Errors.SelectMany(e => e.Value).ToList()),

            NotFoundException => (
                HttpStatusCode.NotFound,
                exception.Message,
                new List<string>()),

            DomainException => (
                HttpStatusCode.BadRequest,
                exception.Message,
                new List<string>()),

            UnauthorizedAccessException => (
                HttpStatusCode.Unauthorized,
                "Unauthorized",
                new List<string>()),

            _ => (
                HttpStatusCode.InternalServerError,
                "An error occurred while processing your request",
                new List<string>())
        };

        response.StatusCode = (int)statusCode;

        // Return standardized error response (NO stack trace exposed to client)
        var result = JsonSerializer.Serialize(new ApiResponse
        {
            Success = false,
            Message = message,
            Errors = errors.Any() ? errors : null
        }, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        });

        await response.WriteAsync(result);
    }
}
