package com.banesco.common.application.helper; import com.banesco.common.domain.exception.HttpStatusCodeException; import com.banesco.common.domain.model.ApiResponse; import com.banesco.common.domain.model.ErrorMapping; import com.banesco.common.domain.model.StatusResponse; import com.banesco.common.infrastructure.config.MessagesConfig; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkus.runtime.annotations.RegisterForReflection; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.ws.rs.core.Response; import lombok.extern.slf4j.Slf4j; import java.io.InputStream; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @Slf4j @ApplicationScoped @RegisterForReflection public class MessageHelper { private final MessagesConfig messagesConfig; private final Map errorMappings; private static final String ERROR_DEFAULT = "default"; private static final String SUCCESS_DEFAULT = "200"; private static final String ERROR_FILE_PATH = "errors-mapping/errors.json"; private final ObjectMapper objectMapper; @Inject public MessageHelper( ObjectMapper objectMapper, MessagesConfig messagesConfig ) { this.objectMapper = objectMapper; this.messagesConfig = messagesConfig; this.errorMappings = initializeErrorMappings(); } public Response handleException(HttpStatusCodeException exception) { log.error( "Error interno controlado: {} -> {}", exception.getStatusCode(), exception.getErrorCode() ); return buildErrorResponse(exception); } public Response handleGenericException(Exception exception) { log.error("Error interno no controlado: {}", exception.getMessage()); return buildErrorResponse(HttpStatusCodeException.internalServer("500")); } public StatusResponse createStatusResponse(String code) { ErrorMapping successMapping = getError(code); return StatusResponse.builder() .statusCode(successMapping.getStatusCode()) .message(successMapping.getDescription()) .build(); } public StatusResponse createError( ErrorMapping mapping, String fieldPath ) { String message = mapping.getDescription(); if (fieldPath != null && message != null && message.contains("%s")) { message = String.format(message, fieldPath); } return StatusResponse.builder() .statusCode(mapping.getStatusCode()) .message(message) .build(); } public ApiResponse buildServiceUnavailableResponse() { return new ApiResponse<>(createStatusResponse("503")); } private ErrorMapping getError(String errorCode) { return errorMappings.getOrDefault( errorCode, errorMappings.getOrDefault(ERROR_DEFAULT, createDefaultMapping()) ); } private Response buildErrorResponse(HttpStatusCodeException exception) { ErrorMapping mapping = errorMappings.getOrDefault( exception.getErrorCode(), errorMappings.getOrDefault( String.valueOf(exception.getStatusCode()), createDefaultMapping() ) ); StatusResponse status = createError(mapping, exception.getFieldPath()); log.error( "[{}] Message {} -> {}", exception.getExceptionType(), exception.getErrorCode(), status.getMessage() ); return Response.status(mapping.getHttpCode()) .entity(new ApiResponse<>(status)) .build(); } private Map initializeErrorMappings() { try { String json; if (isReadingFromProps()) { json = messagesConfig.getErrorMessagesJson(); log.info("Cargando mensajes de errores desde properties"); } else { json = loadFromJsonFile(); log.info("Cargando mensajes de errores desde archivo JSON"); } if (json == null || json.isEmpty()) { log.warn("No se encontro JSON de errores"); return createDefaultMappings(); } List mappings = objectMapper.readValue(json, new TypeReference>() {}); Map result = new ConcurrentHashMap<>(); for (ErrorMapping mapping : mappings) { if (mapping.getBackendCode() != null && !mapping.getBackendCode().trim().isEmpty()) { if (result.containsKey(mapping.getBackendCode())) { log.warn("Clave duplicada encontrada en mappings de errores: {}", mapping.getBackendCode()); } else { result.put(mapping.getBackendCode(), mapping); } } else { log.warn("Ignorando mapping sin backendCode valido: {}", mapping.getDescription()); } } log.info("Mappings de errores cargados exitosamente, total validos: {}", result.size()); return result; } catch (Exception e) { log.error("Error cargando mappings de errores: {}", e.getMessage()); return createDefaultMappings(); } } private String loadFromJsonFile() { try (InputStream is = MessageHelper.class.getClassLoader().getResourceAsStream(ERROR_FILE_PATH)) { if (is == null) { log.warn("No se encontro el archivo de errores: {}", ERROR_FILE_PATH); return ""; } return new String(is.readAllBytes()); } catch (Exception e) { log.error("Error leyendo archivo de errores: {}", e.getMessage()); return ""; } } private Map createDefaultMappings() { Map defaults = new ConcurrentHashMap<>(); defaults.put(ERROR_DEFAULT, createDefaultMapping()); defaults.put(SUCCESS_DEFAULT, createSuccessMapping()); return defaults; } private ErrorMapping createDefaultMapping() { ErrorMapping mapping = new ErrorMapping(); mapping.setBackendCode(ERROR_DEFAULT); mapping.setHttpCode(409); mapping.setStatusCode("409"); mapping.setDescription("Conflicto"); return mapping; } private ErrorMapping createSuccessMapping() { ErrorMapping mapping = new ErrorMapping(); mapping.setBackendCode(SUCCESS_DEFAULT); mapping.setHttpCode(200); mapping.setStatusCode("200"); mapping.setDescription("Operacion exitosa"); return mapping; } public boolean isReadingFromProps() { return messagesConfig.isReadFromProps(); } public boolean isSuccessStatusCode(StatusResponse statusResponse) { if (statusResponse == null || statusResponse.getStatusCode() == null) { return false; } try { int statusCode = Integer.parseInt(statusResponse.getStatusCode()); return statusCode >= 200 && statusCode < 300; } catch (NumberFormatException e) { return false; } } }