package com.banesco.infrastructure.healthcheck;

import com.banesco.commons.config.AppConf;
import com.banesco.commons.model.SoapEndpoint;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.xml.soap.MessageFactory;
import jakarta.xml.soap.SOAPConnection;
import jakarta.xml.soap.SOAPConnectionFactory;
import jakarta.xml.soap.SOAPMessage;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
import org.eclipse.microprofile.health.Readiness;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Semaphore;

/**
 * Verifica connexion a SOAP
 * Readiness
 */
@SuppressWarnings("unused")
@Readiness
@Slf4j
@ApplicationScoped
public class SoapHealthCheck implements HealthCheck {

    private final SoapEndpoint soapConfig;

    // Semáforo para evitar múltiples health checks simultáneos
    private final Semaphore healthCheckSemaphore = new Semaphore(1);


    private final String HEALTHCHECK_NAME = "SOAP";

    private final String CHECK_OK = "OK";


    private final SOAPMessage soapMessage;

    private final AppConf appConf;


    @ConfigProperty(name = "soap.health-check.enabled", defaultValue = "true")
    boolean enabled;

    List<String> domainsToValidate;

    @Inject
    public SoapHealthCheck(AppConf appConf) throws Exception {
        soapConfig = appConf.loadSoapClientConfig("soap.service.block-unblock-debit-card");
        this.appConf = appConf;
        Optional<List<String>> domains = appConf.getEnv().getOptionalValues("soap.health-check.validation.domains", String.class);

        if (domains.isEmpty()) {
            domainsToValidate = new ArrayList<>();
        } else {
            domainsToValidate = domains.get();
        }

        log.info("SoapHealthCheck inicializado");
        MessageFactory messageFactory = MessageFactory.newInstance();
        soapMessage = messageFactory.createMessage();
    }


    @Override
    public HealthCheckResponse call() {

        if (!enabled) {
            return HealthCheckResponse.up("Validacion indicada por soap.health-check.enabled esta inactiva");
        }

        HealthCheckResponseBuilder responseBuilder = HealthCheckResponse.named(HEALTHCHECK_NAME);
        // Evitar múltiples health checks concurrentes
        if (!healthCheckSemaphore.tryAcquire()) {
            log.debug("Health check en progreso, saltando esta invocación");
            return responseBuilder
                    .withData("soap", "progressing")
                    .withData("timestamp", System.currentTimeMillis())
                    .build();
        }


        // check Current Domain

        try {
            responseBuilder.withData("TYPE", "OSB");
            responseBuilder.withData("DOMAINS", "STATUS");
            if (!domainsToValidate.isEmpty()) {

                log.debug("Checking ALL");
                boolean allEndpointsUp = true;


                String checkResponseSoap;
                for (String domainKey : domainsToValidate) {

                    checkResponseSoap = getEndpointAvailable(domainKey);
                    responseBuilder.withData(domainKey, checkResponseSoap);
                    if (!checkResponseSoap.equals(CHECK_OK)) {
                        allEndpointsUp = false;
                    }


                }

                return buildResponse(responseBuilder, allEndpointsUp ? CHECK_OK : "Al menos un SoapEndpoint con Error");


            } else {
                return responseBuilder
                        .up()
                        .withData("*", "ningun Dominio para chequeo")
                        .withData("timestamp", System.currentTimeMillis())
                        .build();
            }

        } catch (Exception e) {
            log.error("Error checking SoapEndpoint: {}", e.getMessage());
            return buildResponse(responseBuilder, "Error checking SoapEndpoint");

        } finally {
            healthCheckSemaphore.release();
        }


    }

    private String getEndpointAvailable(String domainKey) {
        SOAPConnection soapConnection = null;
        try {
            soapConnection = SOAPConnectionFactory.newInstance().createConnection();

            // Populate the SOAP message with your request here
            // For example, you can add a body, headers, etc.

            // Send the request and get the response
            SOAPMessage soapResponse = soapConnection.call(soapMessage, soapConfig.getUrl(appConf.getDomainConfig(domainKey)));

            // Process the response (you can check for specific elements or status)
            // For simplicity, we assume the service is up if we receive a response

            if (soapResponse == null) {
                return "Error de conexion SOAP";
            }
            // Close the connection
            log.debug("Health check exitoso: conexión SOAP");
            return CHECK_OK;



        } catch (Exception e) {
            log.error("Health check detectó error de conexión: {}", e.getMessage());
            return "Error de conexión: " + e.getMessage();

        } finally {
            if (soapConnection != null) {
                try {
                    soapConnection.close();
                } catch (Exception e) {
                    log.warn("Error soapConnection.close()");
                }
            }
        }

    }


    private HealthCheckResponse buildResponse(HealthCheckResponseBuilder responseBuilder, String errorMessage) {


        if (errorMessage.equals(CHECK_OK)) {
            return responseBuilder
                    .status(true)
                    .withData("timestamp", System.currentTimeMillis())
                    .build();

        }

        return responseBuilder
                .status(false)
                .withData("timestamp", System.currentTimeMillis())
                .withData("errorMessage", errorMessage)
                .build();

    }
}