package com.banesco.module.payment_initiation_request.application.service;

import com.banesco.common.application.helper.MessageHelper;
import com.banesco.common.application.helper.PartyValidateHelper;
import com.banesco.common.domain.exception.HttpStatusCodeException;
import com.banesco.common.domain.model.ApiResponse;
import com.banesco.common.domain.model.Either;
import com.banesco.common.infrastructure.context.RequestContext;
import com.banesco.module.black_list.application.usecase.BlackListUseCase;
import com.banesco.module.black_list.domain.dto.response.BlackListResponse;
import com.banesco.module.get_affiliation.application.usecase.GetAffiliationUseCase;
import com.banesco.module.get_affiliation.domain.dto.response.GetAffiliationResponse;
import com.banesco.module.payment_initiation_request.application.usecase.DomainUseCase;
import com.banesco.module.payment_initiation_request.application.usecase.PaymentInitiationRequestUseCase;
import com.banesco.module.payment_initiation_request.domain.dto.request.PaymentInitiationRequestRequest;
import com.banesco.module.payment_initiation_request.domain.dto.response.PaymentInitiationRequestResponse;
import com.banesco.module.security_trace.application.usecase.SecurityTraceUseCase;
import com.banesco.module.security_trace.domain.dto.response.SecurityTraceResponse;
import com.banesco.module.service_status.application.usecase.ServiceStatusUseCase;
import com.banesco.module.service_status.domain.dto.request.ServiceStatusRequest;
import com.banesco.module.service_status.domain.dto.response.ServiceStatusResponse;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;

@Slf4j
@ApplicationScoped
public class PaymentInitiationRequestService implements PaymentInitiationRequestUseCase {

    private final MessageHelper messageHelper;
    private final ServiceStatusUseCase serviceStatusUseCase;
    private final BlackListUseCase blackListUseCase;
    private final GetAffiliationUseCase getAffiliationUseCase;
    private final SecurityTraceUseCase securityTraceUseCase;
    private final DomainUseCase domainUseCase;

    @Inject
    public PaymentInitiationRequestService(
        MessageHelper messageHelper,
        ServiceStatusUseCase serviceStatusUseCase,
        BlackListUseCase blackListUseCase,
        GetAffiliationUseCase getAffiliationUseCase,
        SecurityTraceUseCase securityTraceUseCase,
        DomainUseCase domainUseCase
    ) {
        this.messageHelper = messageHelper;
        this.serviceStatusUseCase = serviceStatusUseCase;
        this.blackListUseCase = blackListUseCase;
        this.getAffiliationUseCase = getAffiliationUseCase;
        this.securityTraceUseCase = securityTraceUseCase;
        this.domainUseCase = domainUseCase;
    }

    @Override
    public Response execute(
        PaymentInitiationRequestRequest request
    ) {
        log.info("Iniciando ejecucion para el archivo: {}", request.getPayerId());

        Response response = null;
        long startTime = System.currentTimeMillis();

        try {
            if(!isServiceStatusActive(request)) {
                log.info("Estatus del servicio no disponible: {}", request.getPayerId());
                throw HttpStatusCodeException.serviceUnavailable("VRN04");
            }

            blackList(request);
            getAffiliation(request);

            ApiResponse<PaymentInitiationRequestResponse> apiResponse = domain(request);

            response = messageHelper.handleSuccess(
                apiResponse.getData(),
                apiResponse.getStatusResponse().getStatusCode()
            );
        } catch (HttpStatusCodeException e) {
            log.error("Excepcion HTTP del api de dominio: {} - {}", e.getStatusCode(), e.getErrorCode());
            response = messageHelper.handleException(e);
        } catch (Exception e) {
            log.error("Excepcion generica del api de dominio: {}", e.getMessage());
            response = messageHelper.handleGenericException(e);
        } finally {
            long endTime = System.currentTimeMillis();
            securityTrace(request, response, startTime, endTime);
        }

        return response;
    }

    private ApiResponse<PaymentInitiationRequestResponse> domain(
        PaymentInitiationRequestRequest request
    ) {
        log.info("Ejecutando llamada al api de dominio: {}", request.getPayerId());
        return domainUseCase.execute(request, PaymentInitiationRequestResponse.class);
    }

    private boolean isServiceStatusActive(
        PaymentInitiationRequestRequest request
    ) {
        log.info("Ejecutando llamada al api de la consulta del estatus del servicio");

        boolean isServiceActive = false;

        try {
            ServiceStatusResponse response = serviceStatusUseCase.execute(
                ServiceStatusRequest.builder()
                    .applicationId(request.getAppId())
                    .transactionId(RequestContext.getRequestId())
                    .build(),
                ServiceStatusResponse.class
            );

            isServiceActive = Objects.equals(response.getStatus(), "A");
        } catch (HttpStatusCodeException e) {
            log.info(
                "Error HTTP al ejecutar la consulta del estatus del servicio: {} -> {}",
                e.getStatusCode(),
                e.getMessage()
            );
        } catch (Exception e) {
            log.info(
                "Error al ejecutar la consulta del estatus del servicio: {}",
                e.getMessage()
            );
        }

        return isServiceActive;
    }

    private void blackList(
        PaymentInitiationRequestRequest request
    ) {
        log.info("Ejecutando llamada al api de la consulta de la lista negra");

        try {
            Either<BlackListResponse, BlackListResponse> responseEither = blackListUseCase.execute(
                request, BlackListResponse.class, BlackListResponse.class
            );
            BlackListResponse response = (responseEither.isLeft())
                ? responseEither.getLeft()
                : responseEither.getRight();

            PartyValidateHelper.validateBlackListStatus(
                response.getBlackList(),
                response.getStatus()
            );
        } catch (HttpStatusCodeException e) {
            log.info(
                "Error HTTP al ejecutar la consulta de la lista negra: {} -> {}",
                e.getStatusCode(),
                e.getMessage()
            );
            throw e;
        } catch (Exception e) {
            log.info(
                "Error al ejecutar la consulta de la lista negra: {}",
                e.getMessage()
            );
            throw e;
        }
    }

    private void getAffiliation(
        PaymentInitiationRequestRequest request
    ) {
        log.info("Ejecutando llamada al api de la consulta de afiliacion");

        try {
            Either<GetAffiliationResponse, GetAffiliationResponse> responseEither = getAffiliationUseCase.execute(
                request, GetAffiliationResponse.class, GetAffiliationResponse.class
            );
            GetAffiliationResponse response = (responseEither.isLeft())
                ? responseEither.getLeft()
                : responseEither.getRight();

            PartyValidateHelper.validateStatus(response.getStatus());
        } catch (HttpStatusCodeException e) {
            log.info(
                "Error HTTP al ejecutar la consulta de afiliacion: {} -> {}",
                e.getStatusCode(),
                e.getMessage()
            );
            throw e;
        } catch (Exception e) {
            log.info(
                "Error al ejecutar la consulta de afiliacion: {}",
                e.getMessage()
            );
            throw e;
        }
    }

    private void securityTrace(
        PaymentInitiationRequestRequest request,
        Response response,
        long startTime,
        long endTime
    ) {
        log.info("Ejecutando llamada al api de la traza de seguridad");

        try {
            securityTraceUseCase.execute(
                request, response,
                startTime, endTime,
                SecurityTraceResponse.class
            );
        } catch (HttpStatusCodeException e) {
            log.info(
                "Error HTTP al ejecutar la traza de seguridad: {} -> {}",
                e.getStatusCode(),
                e.getMessage()
            );
        } catch (Exception e) {
            log.info("Error al ejecutar la traza de seguridad: {}", e.getMessage());
        }
    }
}