package infrastructure.exception;

import domain.dto.response.CustomerResponse;
import domain.dto.response.StatusResponse;
import infrastructure.context.TraceIdContext;
import infrastructure.service.StatusDescriptionResolver;
import jakarta.inject.Inject;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import lombok.extern.slf4j.Slf4j;
import org.jboss.logging.Logger;

import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Provider
public class ConstraintViolationExceptionHandler implements ExceptionMapper<ConstraintViolationException> {

    private static final Logger LOG = Logger.getLogger(ConstraintViolationExceptionHandler.class);

    @Inject
    TraceIdContext traceIdContext;

    @Inject
    StatusDescriptionResolver statusDescriptionResolver;

    @Override
    public Response toResponse(ConstraintViolationException exception) {
        var traceId = traceIdContext.getTraceId();
        var violations = exception.getConstraintViolations();

        if (violations.isEmpty()) {
            return buildGenericErrorResponse(traceId, exception);
        }

        // Se busca la violación con la mayor prioridad según la lista definida.
        var highestPriorityViolation = findHighestPriorityViolation(violations);

        // Extraer el backendCode directamente del mensaje de la anotación.
        var backendCode = highestPriorityViolation.getMessage();
        var fieldPath = highestPriorityViolation.getPropertyPath().toString();

        // Obtenemos el nombre simple del campo para el mensaje.
        var jsonProperty = fieldPath.substring(fieldPath.lastIndexOf('.') + 1);

        LOG.warnf("[traceId: %s] Error de validacion en el campo '%s'. Mensaje/Codigo: '%s'", traceId, fieldPath, backendCode);

        // Obtener los detalles del error.
        var resolvedCode = statusDescriptionResolver.getCode(backendCode);

        var statusResponse = StatusResponse.builder()
                .status("error")
                .statusCode(resolvedCode.statusCode())
                .message(resolvedCode.description() + ": " + jsonProperty)
                .traceId(traceId)
                .build();

        var response = new CustomerResponse(statusResponse);

        return Response.status(Response.Status.BAD_REQUEST)
                .entity(response)
                .build();
    }

    private ConstraintViolation<?> findHighestPriorityViolation(Set<ConstraintViolation<?>> violations) {
        return violations.stream().findFirst().get();
    }

    /**
     * Construye una respuesta de error genérica en caso de que no se encuentren violaciones específicas.
     */
    private Response buildGenericErrorResponse(String traceId, ConstraintViolationException exception) {
        var detailsForLog = exception.getConstraintViolations()
                .stream()
                .map(cv -> cv.getPropertyPath() + ": " + cv.getMessage())
                .collect(Collectors.joining("; "));

        LOG.warnf("[traceId: %s] Error de validación genérico detectado, %s", traceId, detailsForLog);

        var statusResponse = StatusResponse.builder()
                .status("error")
                .statusCode("VDE-400")
                .message("Error en los datos de entrada. Verifique la información enviada")
                .traceId(traceId)
                .build();

        var response = new CustomerResponse(statusResponse);

        return Response.status(Response.Status.BAD_REQUEST)
                .entity(response)
                .build();
    }
}