using Indotalent.ConfigBackEnd.Exceptions;
using Indotalent.Shared.Consts;
using Indotalent.Shared.Models;
using System.Net;
using System.Text.Json;

namespace Indotalent.ConfigBackEnd.Middleware;

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

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

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

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var code = HttpStatusCode.InternalServerError;
        var message = "An unexpected error occurred on the server.";
        var errors = new List<string>();

        switch (exception)
        {
            case ValidationException validationException:
                code = HttpStatusCode.BadRequest;
                message = "One or more validation failures have occurred.";
                foreach (var error in validationException.Errors)
                {
                    errors.AddRange(error.Value);
                }
                break;

            case NotFoundException notFoundException:
                code = HttpStatusCode.NotFound;
                message = notFoundException.Message;
                errors.Add(notFoundException.Message);
                break;

            case AlreadyExistsException alreadyExistsException:
                code = HttpStatusCode.Conflict;
                message = alreadyExistsException.Message;
                errors.Add(alreadyExistsException.Message);
                break;

            case MismatchException mismatchException:
                code = HttpStatusCode.BadRequest;
                message = mismatchException.Message;
                errors.Add(mismatchException.Message);
                break;

            case ForbiddenAccessException:
                code = HttpStatusCode.Forbidden;
                message = "You do not have permission to access this resource.";
                errors.Add("Access denied to the requested resource.");
                break;

            case UnauthorizedAccessException:
                code = HttpStatusCode.Unauthorized;
                message = "Authentication is required to access this resource.";
                errors.Add("Session invalid or expired.");
                break;

            default:
                if (exception.StackTrace != null && exception.StackTrace.Contains(GlobalConsts.BehaviourError))
                {
                    _logger.LogError(exception, GlobalConsts.GlobalError + " {Message} on {Path}",
                        exception.Message, context.Request.Path);
                }
                message = "An unexpected error occurred on the HRM server.";
                errors.Add(exception.Message);
                break;
        }

        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)code;

        var response = new ApiResponse<object>
        {
            IsSuccess = false,
            StatusCode = (int)code,
            Message = message,
            Errors = errors,
            ServerTime = DateTime.UtcNow
        };

        var jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
        await context.Response.WriteAsync(JsonSerializer.Serialize(response, jsonOptions));
    }
}