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

import com.banesco.common.application.usecase.HttpClientUseCase;
import com.banesco.common.domain.exception.HttpStatusCodeException;
import com.banesco.common.domain.model.ApiResponse;
import com.banesco.common.domain.model.HttpRequest;
import com.banesco.common.infrastructure.config.RestClientConfig;
import com.banesco.module.payment_instruction_request.domain.dto.request.PaymentInstructionRequestRequest;
import com.banesco.module.payment_instruction_request.domain.dto.response.PaymentInstructionRequestResponse;
import com.banesco.module.security_trace.application.usecase.SecurityTraceUseCase;
import com.banesco.module.security_trace.domain.dto.request.SecurityTraceRequest;
import com.banesco.module.security_trace.domain.model.SecurityTraceConfig;
import com.banesco.module.security_trace.domain.model.SecurityTraceObject;
import io.netty.util.internal.StringUtil;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import java.util.Collections;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.Map;
import java.util.Objects;

@Slf4j
@ApplicationScoped
public class SecurityTraceClient implements SecurityTraceUseCase {
    private final HttpClientUseCase httpClientUseCase;
    private final RestClientConfig restClientConfig;
    private final SecurityTraceConfig securityTraceConfig;

    private static final String PAYMENT_INSTRUCTION_TRANSACTION_KEY = "paymentInstructionTransaction";

    @Inject
    public SecurityTraceClient(
        HttpClientUseCase httpClientUseCase,
        RestClientConfig restClientConfig
    ) {
        this.httpClientUseCase = httpClientUseCase;
        this.restClientConfig = restClientConfig;
        this.securityTraceConfig = restClientConfig.getSecurityTraceConfig();
        log.info("Configuracion cargada para security-trace: {}", securityTraceConfig);
    }

    @Override
    public <T> T execute(
        PaymentInstructionRequestRequest apiRequest,
        Response apiResponse,
        long startTime,
        long endTime,
        Class<T> responseType
    ) {
        SecurityTraceRequest body = getRequest(
            apiRequest, apiResponse,
            startTime, endTime
        );
        HttpRequest request = HttpRequest.forDirectResponse(
                securityTraceConfig.getUrl(),
                HttpRequest.HttpMethod.POST,
                responseType
            )
            .withBody(body)
            .withTimeout(
                securityTraceConfig.getTimeout().getConnect(),
                securityTraceConfig.getTimeout().getResponse()
            );

        try {
            T response = httpClientUseCase.execute(request);

            log.info(
                "Solicitud de traza de seguridad exitosa: {}",
                response.toString().contains("msg=Message sende")
            );

            return response;
        } catch (HttpStatusCodeException e) {
            log.error(
                "Error HTTP ejecutando traza de seguridad: {} - {}",
                e.getStatusCode(),
                e.getMessage()
            );

            throw e;
        } catch (Exception e) {
            log.error("Error ejecutando traza de seguridad: {}", e.getMessage());
            throw HttpStatusCodeException.serviceUnavailable("503");
        }
    }

    private SecurityTraceRequest getRequest(
        PaymentInstructionRequestRequest apiRequest,
        Response apiResponse,
        long startTime,
        long endTime
    ) {
        long executedTime = (endTime - startTime);
        int statusHttp = apiResponse.getStatus();
        String payeeId = apiRequest.getRecipientId();
        String payeePhone = apiRequest.getRecipientPhone();
        ApiResponse<PaymentInstructionRequestResponse> apiResponseData =
            (ApiResponse<PaymentInstructionRequestResponse>) apiResponse.getEntity();

        return SecurityTraceRequest.builder()
            .sp(securityTraceConfig.getRequest().getSp())
            .codBan(securityTraceConfig.getRequest().getCodBan())
            .codMon(securityTraceConfig.getRequest().getCodMon())
            .codEve(securityTraceConfig.getRequest().getCodEve())
            .codEve2(securityTraceConfig.getRequest().getCodEve2())
            .login(apiRequest.getChannelCode())
            .fecHor(new Date())
            .nacCli("")
            .cedRifCli(0)
            .nacBen(payeeId.substring(0, 1).toUpperCase())
            .cedBen(getPartyIdNumber(payeeId.substring(1)))
            .productoBen(payeePhone)
            .monto(apiRequest.getAmount())
            .desPago(apiRequest.getPurpose())
            .objeto(getObject(apiRequest, apiResponseData))
            .tipoRespuesta(statusHttp)
            .msgRespuesta(getStatusMessage(apiResponseData, statusHttp))
            .tiempoRespuesta(executedTime)
            .codFintech(apiRequest.getCustomerReferenceFintechId())
            .nombreFintech(apiRequest.getChannelCode())
            .tipoDispositivo(apiRequest.getDevice().getDeviceType())
            .desDispositivo(apiRequest.getDevice().getDeviceDescription())
            .ipCli(apiRequest.getDevice().getDeviceIp())
            .build();
    }

    private String getObject(
        PaymentInstructionRequestRequest apiRequest,
        ApiResponse<PaymentInstructionRequestResponse> apiResponseData
    ) {
        Map<String, Object> apiRequestMap = getObjectRequest(apiRequest);
        Map<String, Object> apiResponseMap = getObjectResponse(apiResponseData);

        return restClientConfig.toJsonString(SecurityTraceObject.builder()
            .RQ(apiRequestMap)
            .RS(apiResponseMap)
            .build());
    }

    private Map<String, Object> getObjectRequest(
        PaymentInstructionRequestRequest apiRequest
    ) {
        if(Objects.isNull(apiRequest)) {
            return Collections.emptyMap();
        }

        Map<String, Object> apiRequestMap = restClientConfig.toMap(apiRequest);
        Map<String, Object> paymentInstructionTransaction =
            (Map<String, Object>) apiRequestMap.get(PAYMENT_INSTRUCTION_TRANSACTION_KEY);

        if(Objects.isNull(paymentInstructionTransaction)) {
            return Collections.emptyMap();
        }

        paymentInstructionTransaction.put(
            "paymentArrangement",
            apiRequest.getPaymentInstructionTransaction().getPaymentArrangement().size()
        );

        apiRequestMap.put(PAYMENT_INSTRUCTION_TRANSACTION_KEY, paymentInstructionTransaction);

        return apiRequestMap;
    }

    private Map<String, Object> getObjectResponse(
        ApiResponse<PaymentInstructionRequestResponse> apiResponseData
    ) {
        if(Objects.isNull(apiResponseData)) {
            return Collections.emptyMap();
        }

        Map<String, Object> apiResponseMap = restClientConfig.toMap(apiResponseData);
        Map<String, Object> data =
            (Map<String, Object>) apiResponseMap.get("data");

        if(Objects.isNull(data)) {
            return Collections.emptyMap();
        }

        Map<String, Object> paymentInstructionTransaction =
            (Map<String, Object>) data.get(PAYMENT_INSTRUCTION_TRANSACTION_KEY);

        if(Objects.isNull(paymentInstructionTransaction)) {
            return Collections.emptyMap();
        }

        paymentInstructionTransaction.put(
            "paymentArrangement",
            apiResponseData.getData().getPaymentInstructionTransaction().getPaymentArrangement().size()
        );

        data.put(PAYMENT_INSTRUCTION_TRANSACTION_KEY, paymentInstructionTransaction);

        apiResponseMap.put("data", data);

        return apiResponseMap;
    }

    private String getStatusMessage(
        ApiResponse<?> apiResponseData,
        int statusHttp
    ) {
        if (apiResponseData != null && apiResponseData.getStatusResponse() != null) {
            String statusCode = apiResponseData.getStatusResponse().getStatusCode();
            String message = apiResponseData.getStatusResponse().getMessage();

            if(StringUtil.isNullOrEmpty(statusCode)) {
                statusCode = String.valueOf(statusHttp);
            }

            return statusCode + "-" + message;
        }

        return null;
    }

    private Integer getPartyIdNumber(String partyId) {
        if (partyId == null || partyId.trim().isEmpty()) {
            return null;
        }

        String digits = partyId.replaceAll("\\D", "");

        if (digits.isEmpty()) {
            return null;
        }

        try {
            return Integer.parseInt(digits);
        } catch (NumberFormatException e) {
            return null;
        }
    }
}