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<String, ErrorMapping> 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 handleSuccess(Object data, String statusCode) {
        log.info(
            "Respuesta exitosa controlada: {}",
            statusCode
        );
        return buildResponse(data, statusCode);
    }

    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 <T> ApiResponse<T> 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 Response buildResponse(Object data, String statusCode) {
        ErrorMapping mapping = errorMappings.getOrDefault(
            statusCode, errorMappings.getOrDefault(
                statusCode, createDefaultMapping()
            )
        );
        StatusResponse status = createError(mapping, null);

        log.error(
            "[Success] Message {} -> {}",
            statusCode,
            status.getMessage()
        );

        return Response.status(mapping.getHttpCode())
            .entity(new ApiResponse<>(data, status))
            .build();
    }

    private Map<String, ErrorMapping> 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<ErrorMapping> mappings = objectMapper.readValue(json, new TypeReference<List<ErrorMapping>>() {});
            Map<String, ErrorMapping> 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<String, ErrorMapping> createDefaultMappings() {
        Map<String, ErrorMapping> 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;
        }
    }
}