using GestionIntegral.Api.Services.Contables; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System; using System.Text.Json; using System.Threading.Tasks; namespace GestionIntegral.Api.Middleware { // Centraliza el mapeo de excepciones semánticas a HTTP responses con cuerpo JSON estandarizado. // Va PRIMERO en el pipeline para catchear cualquier excepción que escape de los controllers/services. public class ExceptionHandlerMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; public ExceptionHandlerMiddleware(RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (BloqueoPorPeriodoCerradoException ex) { _logger.LogWarning( "Bloqueo por período cerrado: cierre #{IdCierre} FechaCorte={FechaCorte:yyyy-MM-dd}. Path={Path}", ex.IdCierre, ex.FechaCorte, context.Request.Path); await WriteJsonAsync(context, StatusCodes.Status409Conflict, new { codigo = "PERIODO_CERRADO_BLOQUEO_OPERACION", mensaje = ex.Message, idCierre = ex.IdCierre, fechaCorte = ex.FechaCorte.ToString("yyyy-MM-dd") }); } catch (Exception ex) { _logger.LogError(ex, "Excepción no manejada. Path={Path}", context.Request.Path); await WriteJsonAsync(context, StatusCodes.Status500InternalServerError, new { codigo = "ERROR_INTERNO", mensaje = "Ocurrió un error inesperado al procesar la solicitud." }); } } private static Task WriteJsonAsync(HttpContext context, int statusCode, object body) { if (context.Response.HasStarted) { // Si los headers ya se enviaron no podemos re-escribir el response. Solo loguear y salir. return Task.CompletedTask; } context.Response.Clear(); context.Response.StatusCode = statusCode; context.Response.ContentType = "application/json; charset=utf-8"; return context.Response.WriteAsync(JsonSerializer.Serialize(body, JsonOptions)); } } }