using System.Diagnostics;
using FluentValidation;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ColaFlow.Modules.ProjectManagement.Domain.Exceptions;

namespace ColaFlow.API.Handlers;

/// <summary>
/// Global exception handler using IExceptionHandler (.NET 8+)
/// Handles all unhandled exceptions and converts them to ProblemDetails responses
/// </summary>
public sealed class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger) : IExceptionHandler
{
    private readonly ILogger<GlobalExceptionHandler> _logger = logger ?? throw new ArgumentNullException(nameof(logger));

    public async ValueTask<bool> TryHandleAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
        // Log with appropriate level based on exception type
        if (exception is ValidationException or DomainException or NotFoundException or UnauthorizedAccessException or ArgumentException)
        {
            _logger.LogWarning(exception, "Client error occurred: {Message}", exception.Message);
        }
        else
        {
            _logger.LogError(exception, "Internal server error occurred: {Message}", exception.Message);
        }

        var problemDetails = exception switch
        {
            ValidationException validationEx => CreateValidationProblemDetails(httpContext, validationEx),
            DomainException domainEx => CreateDomainProblemDetails(httpContext, domainEx),
            NotFoundException notFoundEx => CreateNotFoundProblemDetails(httpContext, notFoundEx),
            UnauthorizedAccessException unauthorizedEx => CreateUnauthorizedProblemDetails(httpContext, unauthorizedEx),
            ArgumentException argumentEx => CreateBadRequestProblemDetails(httpContext, argumentEx),
            InvalidOperationException invalidOpEx => CreateBadRequestProblemDetails(httpContext, invalidOpEx),
            DbUpdateException dbUpdateEx when IsDuplicateKeyViolation(dbUpdateEx) => CreateConflictProblemDetails(httpContext, dbUpdateEx),
            _ => CreateInternalServerErrorProblemDetails(httpContext, exception)
        };

        httpContext.Response.StatusCode = problemDetails.Status ?? StatusCodes.Status500InternalServerError;
        await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken);

        return true; // Exception handled
    }

    private static bool IsDuplicateKeyViolation(DbUpdateException exception)
    {
        // Check for duplicate key violation in SQL Server or PostgreSQL
        var innerException = exception.InnerException?.Message ?? string.Empty;
        return innerException.Contains("duplicate key", StringComparison.OrdinalIgnoreCase) ||
               innerException.Contains("unique constraint", StringComparison.OrdinalIgnoreCase) ||
               innerException.Contains("IX_", StringComparison.OrdinalIgnoreCase);
    }

    private static ProblemDetails CreateValidationProblemDetails(
        HttpContext context,
        ValidationException exception)
    {
        var errors = exception.Errors
            .GroupBy(e => e.PropertyName)
            .ToDictionary(
                g => g.Key,
                g => g.Select(e => e.ErrorMessage).ToArray()
            );

        return new ProblemDetails
        {
            Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1",
            Title = "Validation Error",
            Status = StatusCodes.Status400BadRequest,
            Detail = "One or more validation errors occurred.",
            Instance = context.Request.Path,
            Extensions =
            {
                ["traceId"] = Activity.Current?.Id ?? context.TraceIdentifier,
                ["errors"] = errors
            }
        };
    }

    private static ProblemDetails CreateDomainProblemDetails(
        HttpContext context,
        DomainException exception)
    {
        return new ProblemDetails
        {
            Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1",
            Title = "Domain Error",
            Status = StatusCodes.Status400BadRequest,
            Detail = exception.Message,
            Instance = context.Request.Path,
            Extensions =
            {
                ["traceId"] = Activity.Current?.Id ?? context.TraceIdentifier
            }
        };
    }

    private static ProblemDetails CreateNotFoundProblemDetails(
        HttpContext context,
        NotFoundException exception)
    {
        return new ProblemDetails
        {
            Type = "https://tools.ietf.org/html/rfc7231#section-6.5.4",
            Title = "Resource Not Found",
            Status = StatusCodes.Status404NotFound,
            Detail = exception.Message,
            Instance = context.Request.Path,
            Extensions =
            {
                ["traceId"] = Activity.Current?.Id ?? context.TraceIdentifier
            }
        };
    }

    private static ProblemDetails CreateUnauthorizedProblemDetails(
        HttpContext context,
        UnauthorizedAccessException exception)
    {
        return new ProblemDetails
        {
            Type = "https://tools.ietf.org/html/rfc7235#section-3.1",
            Title = "Unauthorized",
            Status = StatusCodes.Status401Unauthorized,
            Detail = exception.Message,
            Instance = context.Request.Path,
            Extensions =
            {
                ["traceId"] = Activity.Current?.Id ?? context.TraceIdentifier
            }
        };
    }

    private static ProblemDetails CreateBadRequestProblemDetails(
        HttpContext context,
        Exception exception)
    {
        return new ProblemDetails
        {
            Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1",
            Title = "Bad Request",
            Status = StatusCodes.Status400BadRequest,
            Detail = exception.Message,
            Instance = context.Request.Path,
            Extensions =
            {
                ["traceId"] = Activity.Current?.Id ?? context.TraceIdentifier
            }
        };
    }

    private static ProblemDetails CreateConflictProblemDetails(
        HttpContext context,
        DbUpdateException exception)
    {
        return new ProblemDetails
        {
            Type = "https://tools.ietf.org/html/rfc7231#section-6.5.8",
            Title = "Conflict",
            Status = StatusCodes.Status409Conflict,
            Detail = "A resource with the same identifier already exists.",
            Instance = context.Request.Path,
            Extensions =
            {
                ["traceId"] = Activity.Current?.Id ?? context.TraceIdentifier
            }
        };
    }

    private static ProblemDetails CreateInternalServerErrorProblemDetails(
        HttpContext context,
        Exception exception)
    {
        return new ProblemDetails
        {
            Type = "https://tools.ietf.org/html/rfc7231#section-6.6.1",
            Title = "Internal Server Error",
            Status = StatusCodes.Status500InternalServerError,
            Detail = "An unexpected error occurred. Please contact support if the problem persists.",
            Instance = context.Request.Path,
            Extensions =
            {
                ["traceId"] = Activity.Current?.Id ?? context.TraceIdentifier
            }
        };
    }
}
