using Microsoft.AspNetCore.Diagnostics;
using PARR.API.Contracts.V1.Responses.Base;
using PARR.Domain.Exceptions;

namespace PARR.API.Infrastructure.Middleware
{
    /// <summary>
    /// Глобальная обработка ошибок согласно моих кастомных Exception
    /// </summary>
    public class GlobalExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<GlobalExceptionHandler> logger;

        public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
        {
            this.logger = logger;
        }

        public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
        {
            logger.LogError(exception, "Ошибка во время запроса {TraceId}: {Message}", httpContext.TraceIdentifier, exception.Message);

            // определяем статус код, в зависимости от типа исключения

            var statusCode = exception switch
            {
                NotFoundException => StatusCodes.Status404NotFound,
                DbErrorException => StatusCodes.Status502BadGateway,
                ExternalServiceException => StatusCodes.Status502BadGateway,
                AppValidationException => StatusCodes.Status400BadRequest,
                FluentValidation.ValidationException => StatusCodes.Status400BadRequest,
                AlreadyExistsException => StatusCodes.Status409Conflict,
                UnauthorizedException => StatusCodes.Status401Unauthorized,
                ForbiddenException => StatusCodes.Status403Forbidden,
                NextRunException => StatusCodes.Status422UnprocessableEntity,
                //todo: ---------- другие ошибки ----------
                _ => StatusCodes.Status500InternalServerError
            };

            // Для критических серверных ошибок (5xx) пишем детальный лог
            if (statusCode >= 500)
            {
                logger.LogError(exception, "Критическая ошибка сервера: {Message}", exception.Message);
            }

            //var errorMessage = exception.Message;
            var errors = new List<ErrorModel>();

            // Если это ошибка FluentValidatioun, вытаскиваем детали по каждому полю
            if (exception is FluentValidation.ValidationException fluentEx)
            {
                // ошибка валидации, вернем по каждому полю
                foreach (var error in fluentEx.Errors)
                {
                    errors.Add(new ErrorModel { FieldName = error.PropertyName, Message = error.ErrorMessage });
                }
            }
            else
            {
                // это не ошибка валидации, вернем сообщение
                errors.Add(new ErrorModel { Message = exception.Message });
            }

            httpContext.Response.StatusCode = statusCode;

            var response = new Response<object?>(null, false, errors, exception.Message);

            await httpContext.Response.WriteAsJsonAsync(response, cancellationToken);

            // ошибка обработана
            return true;
        }
    }
}
