package infrastructure.client.otp.adapter;

import application.exception.GatewayServiceException;
import application.exception.InvalidTokenException;
import application.port.input.command.ValidateOtpCommand;
import com.banesco.common.infraestructure.helpers.LoggerHelper;
import domain.repository.IOtpPort;
import infrastructure.client.otp.api.OtpClient;
import infrastructure.mapper.OtpMapper;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.reactive.RestResponse;

@Slf4j
@ApplicationScoped
public class OtpAdapter implements IOtpPort {

    private final OtpClient client;

    @Inject
    public OtpAdapter(@RestClient OtpClient client) {
        this.client = client;
    }

    /**
     * Implementa la comunicación con la API externa para validar un OTP.
     * Se encarga de mapear el DTO, llamar al cliente REST y TRADUCIR errores
     * técnicos (de red) o respuestas HTTP inesperadas en excepciones de la aplicación.
     *
     * @param command El comando de dominio con los datos de validación.
     * @param requestId El ID de la solicitud para trazabilidad.
     * @return Un objeto {@link RestResponse} que contiene la respuesta HTTP completa de la API.
     * @throws GatewayServiceException si ocurre un error de red o la API responde con un error 5xx.
     */
    @Override
    public RestResponse<Void> validateOtp(ValidateOtpCommand command, String requestId) {
        try {
            log.debug(LoggerHelper.buildInfoPrivateRequest(requestId, "Adaptador: Mapeando Command a DTO para la API privada de validate otp"));
            var requestDto = OtpMapper.toRequestDto(command);
            log.info(LoggerHelper.buildInfoPrivateRequest(requestId, "Llamando al servicio de validate otp", requestDto));

            var response = client.validateOtp(
                    requestId,
                    requestDto.getOtpToken(),
                    requestDto.getAppOrigen(),
                    requestDto.getLogin(),
                    requestDto.getIpAddress(),
                    requestDto.getCanal(),
                    requestDto.getServicio()
            );

            log.info(LoggerHelper.buildInfoPrivateResponse(requestId, "Respuesta del servicio de otp", response.getStatus()));

            return response;
        } catch(WebApplicationException ex) {
            var errorResponse = ex.getResponse();
            var status = errorResponse.getStatus();
            String errorDetails;

            // OTP inválido (respuesta de la privada)
            if(status == 403) {
                log.error(LoggerHelper.buildError(requestId, String.format("token %s invalido, respuesta HTTP API privada de otp: %s", command.otpToken(), status)));
                throw new InvalidTokenException("Token inválido");
            }

            // Verifica si el Content-Type es HTML
            if (errorResponse.getMediaType() != null && errorResponse.getMediaType().toString().contains("text/html")) {
                errorDetails = "Se recibio una respuesta HTML no esperada del servicio.";
                log.error(LoggerHelper.buildError(requestId,
                        String.format("Error HTTP %s del servicio de otp. %s", status, errorDetails)));
            } else {
                // Si no es HTML, probablemente es un error JSON que sí queremos ver.
                var errorBody = errorResponse.readEntity(String.class);

                // Se trunca por si es el error es muy largo
                errorDetails = errorBody.substring(0, Math.min(errorBody.length(), 1000));
                log.error(LoggerHelper.buildError(requestId,
                        String.format("Error HTTP no manejado del servicio de otp. Status: %s. Body: %s", status, errorDetails)));
            }

            throw new GatewayServiceException("Error en el servicio de otp: " + errorDetails);
        }  catch(RuntimeException ex) {
            // Error de red (Timeout, no se puede conectar, etc.)
            log.error(LoggerHelper.buildError(requestId, String.format("Error de comunicacion con el servicio de otp %s", ex)));
            throw new GatewayServiceException("No se pudo comunicar con el servicio externo de otp.");
        }
    }
}