package com.banesco.module.security_notification.infrastructure.client;

import com.banesco.common.application.helper.SerializationHelper;
import com.banesco.common.application.usecase.HttpClientUseCase;
import com.banesco.common.domain.exception.HttpStatusCodeException;
import com.banesco.common.domain.exception.ApiPrivateException;
import com.banesco.common.domain.model.*;
import com.banesco.common.infrastructure.config.RestClientConfig;
import com.banesco.module.security_notification.application.usecase.SecurityNotificationUseCase;
import com.banesco.module.security_notification.domain.dto.request.SecurityNotificationRequest;
import com.banesco.module.security_notification.domain.model.Operation;
import com.banesco.module.security_notification.domain.model.SecurityNotificationConfig;
import io.netty.util.internal.StringUtil;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.Map;
import java.util.Objects;

@Slf4j
@ApplicationScoped
public class SecurityNotificationClient implements SecurityNotificationUseCase {
    private final SerializationHelper serializationHelper;
    private final HttpClientUseCase httpClientUseCase;
    private final SecurityNotificationConfig securityNotificationConfig;

    private static final String EMPTY_MESSAGE = "EMPTY_MESSAGE";

    @Inject
    public SecurityNotificationClient(
        SerializationHelper serializationHelper,
        HttpClientUseCase httpClientUseCase,
        RestClientConfig restClientConfig
    ) {
        this.serializationHelper = serializationHelper;
        this.httpClientUseCase = httpClientUseCase;
        this.securityNotificationConfig = restClientConfig.getSecurityNotificationConfig();
        log.info("Configuracion cargada para payment-status: {}", securityNotificationConfig);
    }

    @Override
    public <T> T execute(
        SecurityNotificationRequest params,
        Class<T> responseType
    ) {
        String fintechId = params.getFintechId();
        String recipientIdentification = params.getOperation().getRecipientIdentification();
        Map<String, Object> payload = getPayload(params);
        HttpRequest request = HttpRequest.forApiPrivateResponse(
                securityNotificationConfig.getUrl(),
                securityNotificationConfig.getStatusSuccess(),
                HttpRequest.HttpMethod.POST,
                responseType
            )
            .withBody(payload)
            .withHeaders(Map.of("fintechId", fintechId))
            .withTimeout(
                securityNotificationConfig.getTimeout().getConnect(),
                securityNotificationConfig.getTimeout().getResponse()
            );

        try {
            ApiPrivateResponse<Either<T, ApiPrivateError>> response =
                httpClientUseCase.executeApiPrivateResponse(request);
            Either<T, ApiPrivateError> detail = response.getDetalle();

            if(detail == null) {
                log.error(
                    "API retorno exitoso pero con detalle vacio. Codigo: {}, Mensaje: {}",
                    response.getEstatus(),
                    response.getMensaje()
                );

                throw ApiPrivateException.builder()
                    .statusCode("PIBE-00")
                    .message(EMPTY_MESSAGE)
                    .build();
            }

            if (detail.isLeft()) {
                if(detail.getLeft() == null) {
                    throw ApiPrivateException.builder()
                        .statusCode("PIBE-00")
                        .message(EMPTY_MESSAGE)
                        .build();
                }
                
                return detail.getLeft();
            }

            ApiPrivateError error = detail.getRight();

            log.error(
                "API privada retorno error. Codigo: {}, Mensaje: {}, Error Interno: {} -> {}",
                response.getEstatus(),
                response.getMensaje(),
                error.getCodError(),
                error.getMensajeError()
            );

            throw ApiPrivateException.builder()
                .statusCode("PIBE-01")
                .message((error.getCodError() != null)
                    ? error.getCodError() + ":" + error.getMensajeError()
                    : EMPTY_MESSAGE
                )
                .build();
        } catch (ApiPrivateException e) {
            throw e;
        } catch (HttpStatusCodeException e) {
            log.error(
                "Error HTTP reportando del cliente {}: {} - {}",
                recipientIdentification,
                e.getStatusCode(),
                e.getMessage()
            );
            throw e;
        } catch (Exception e) {
            log.error("Error reportando del cliente {}: {}", recipientIdentification, e.getMessage());
            throw HttpStatusCodeException.serviceUnavailable("503");
        }
    }

    private Map<String, Object> getPayload(SecurityNotificationRequest params) {
        Map<String, Object> securityNotificationRequest = serializationHelper.toMap(params);
        Operation paramsOperation = Operation.builder()
            .codeAlert(params.getOperation().getCodeAlert())
            .recipientIdentification(params.getOperation().getRecipientIdentification())
            .amount((!Objects.isNull(params.getOperation().getAmount()))
                ? params.getOperation().getAmount()
                : BigDecimal.ZERO
            )
            .currency((!StringUtil.isNullOrEmpty(params.getOperation().getCurrency()))
                ? params.getOperation().getCurrency()
                : securityNotificationConfig.getValueDefault().getCurrency()
            )
            .expirationDate((!StringUtil.isNullOrEmpty(params.getOperation().getExpirationDate()))
                ? params.getOperation().getExpirationDate()
                : ""
            )
            .expirationTime((!Objects.isNull(params.getOperation().getExpirationTime()))
                ? params.getOperation().getExpirationTime()
                : securityNotificationConfig.getValueDefault().getExpirationTime()
            )
            .build();
        Map<String, Object> operation = serializationHelper.toMap(paramsOperation);
        String signature = serializationHelper.generateSignature(
            paramsOperation, params.getChannelOrigin()
        );

        securityNotificationRequest.put("signature", signature);
        securityNotificationRequest.put("operation", operation);

        securityNotificationRequest.remove("fintechId");

        log.info("3. Firma generada: {}", signature);
        log.info("4. Parametros de la solicitud: {}", securityNotificationRequest);
        log.info("5. Solicitud final: {}", securityNotificationRequest);

        return securityNotificationRequest;
    }
}