package com.banesco.module.legal_customer_product_directory.infrastructure.persistence.repository;

import com.banesco.common.domain.exception.PersistenceException;
import com.banesco.common.infrastructure.config.DataSourceConfig;
import com.banesco.module.legal_customer_product_directory.domain.dto.request.LegalCustomerProductDirectoryRequest;
import com.banesco.module.legal_customer_product_directory.domain.dto.response.SPAccountResponse;
import com.banesco.module.legal_customer_product_directory.infrastructure.persistence.mapper.AccountMapper;
import io.agroal.api.AgroalDataSource;
import io.netty.util.internal.StringUtil;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@ApplicationScoped
public class AccountRepository {

    private final AgroalDataSource dataSource;

    @Inject
    public AccountRepository(
        AgroalDataSource dataSource
    ) {
        this.dataSource = dataSource;
    }

    public List<SPAccountResponse> execute(
        DataSourceConfig dataSourceConfig,
        LegalCustomerProductDirectoryRequest request
    ) {
        try {
            return callSp(dataSourceConfig, request);
        } catch (PersistenceException e) {
            throw e;
        } catch (Exception e) {
            throw new PersistenceException("ERROR", e.getMessage());
        }
    }

    private List<SPAccountResponse> callSp(
        DataSourceConfig dataSourceConfig,
        LegalCustomerProductDirectoryRequest request
    ) {
        List<SPAccountResponse> results;
        String spCall = buildSpCall(dataSourceConfig, request);

        log.info("Iniciando llamada SP: {}", spCall);

        try (Connection conn = dataSource.getConnection()) {
            log.info("Conexion obtenida exitosamente");
            results = executeStoredProcedure(conn, spCall);

            if (
                results.size() == 1 &&
                StringUtil.isNullOrEmpty(results.get(0).getRif()) &&
                !StringUtil.isNullOrEmpty(results.get(0).getDscError()) &&
                results.get(0).getDscError().toUpperCase().equals(dataSourceConfig.getSpIbsError())
            ) {
                throw new PersistenceException("VRN01", results.get(0).getDscError());
            }
        } catch (SQLException e) {
            log.error("Error en conexion o ejecucion del SP {}: {}", spCall, e.getMessage());
            throw new PersistenceException("ERROR", e.getMessage());
        } catch (Exception e) {
            log.error("Error generico en la ejecucion del SP {}: {}", spCall, e.getMessage());
            throw new PersistenceException("ERROR", e.getMessage());
        } finally {
            log.info("SP conexion finalizada");
        }

        log.info("SP completado exitosamente, resultados: {}", results.size());
        return results;
    }

    private List<SPAccountResponse> executeStoredProcedure(
        Connection conn,
        String spCall
    ) throws SQLException {
        List<SPAccountResponse> results = new ArrayList<>();

        try (CallableStatement cstmt = conn.prepareCall(spCall)) {
            log.info("Statement preparado");

            conn.setNetworkTimeout(Runnable::run, 30000);
            cstmt.setQueryTimeout(30);

            log.info("Ejecutando SP...");
            boolean hasResultSet = cstmt.execute();
            log.info("SP ejecutado, tiene ResultSet: {}", hasResultSet);

            if (hasResultSet) {
                try (ResultSet rs = cstmt.getResultSet()) {
                    if(rs != null) {
                        log.info("Procesando ResultSet...");
                        int rowCount = 0;
                        while (rs.next()) {
                            rowCount++;
                            results.add(AccountMapper.toSpAccount(log, rs));
                        }
                        log.info("Procesadas {} filas del ResultSet", rowCount);
                    }
                }
            } else {
                log.info("SP no devolvio ResultSet");
            }
        } catch (SQLException e) {
            log.error("Error en CallableStatement {}: {}", spCall, e.getMessage());
            throw e;
        } finally {
            log.info("Ejecucion del SP finalizado");
        }

        return results;
    }

    private String buildSpCall(
        DataSourceConfig dataSourceConfig,
        LegalCustomerProductDirectoryRequest request
    ) {
        return "call " + dataSourceConfig.getSpName() +
            "(" +
                buildParam(request.getCustomerId()) + ", " +
                buildParam(request.getBankNumber()) + ", " +
                buildParam(request.getCurrencyCode()) + ", " +
                buildParam(request.getAccountStatus()) + ", " +
                buildParam(request.getProductCvCode()) + ", " +
                buildParam(request.getProductCode()) + ", " +
                buildParam(request.getChannelCode()) + ", " +
                buildParam(request.getServiceType()) + ", " +
                buildParam(request.getAffiliationStatus()) + ", " +
                buildParam(request.getLimitType()) + ", " +
                buildParam(request.getCasheaIndicator()) +
            ")";
    }

    private String buildParam(String value) {
        return (!StringUtil.isNullOrEmpty(value) ? "'" + value + "'" : "NULL");
    }
}