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

import com.banesco.common.domain.model.*;
import com.banesco.common.infrastructure.config.DataSourceConfig;
import com.banesco.module.account.domain.model.*;
import com.banesco.module.legal_customer_product_directory.domain.dto.response.SPAccountResponse;
import com.banesco.module.party.domain.model.Party;
import com.banesco.module.party.domain.model.PartyIdentification;
import com.banesco.module.party.domain.model.PartyIdentificationType;
import com.banesco.module.party.domain.model.PartyType;
import org.slf4j.Logger;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;

public class AccountMapper {

    private AccountMapper() {}

    public static Account toAccount(
        DataSourceConfig dataSourceConfig,
        SPAccountResponse response
    ) {
        String accountType = response.getTipoCta().toUpperCase();
        String currency = response.getMoneda().toUpperCase();
        String parent = response.getParentChild().toUpperCase();

        AccountStatus status = AccountStatus.builder()
            .status(response.getEstatus())
            .statusType(getAccountStatusType(dataSourceConfig, response))
            .build();
        AccountType type = (dataSourceConfig.getSpAccountTypeSaving().contains(accountType))
            ? AccountType.SAVING_ACCOUNT
            : AccountType.CURRENT_ACCOUNT;

        List<AccountCurrency> currencies = List.of(
            AccountCurrency.builder()
                .currencyCode(currency)
                .currencyType((dataSourceConfig.getSpCurrencyBase().contains(currency))
                    ? CurrencyType.BASE
                    : CurrencyType.SECONDARY
                )
                .build()
        );
        List<AccountRelationship> relationships = List.of(
            AccountRelationship.builder()
                .accountRelationshipType((dataSourceConfig.getSpAccountParent().contains(parent))
                    ? AccountRelationshipType.ACCOUNT_IS_PARENT_ACCOUNT_FOR_ACCOUNT
                    : AccountRelationshipType.ACCOUNT_IS_SUB_ACCOUNT_FOR_ACCOUNT
                )
                .build()
        );
        List<Agreement> agreements = (dataSourceConfig.getSpAccountTypeInterestArrangement().contains(accountType)) ? List.of(
            Agreement.builder()
                .arrangement(List.of(
                    Arrangement.builder()
                        .arrangementType(ArrangementType.INTEREST_ARRANGEMENT)
                        .build()
                ))
                .build()
        ) : Collections.emptyList();
        List<AccountInvolvement> involvements = List.of(
            AccountInvolvement.builder()
                .accountInvolvementType(AccountInvolvementType.PARTY_IS_OWNER_OF_ACCOUNT)
                .partyReference(
                    Party.builder()
                        .partyName(List.of(
                            Name.builder()
                                .fullName(response.getNombre())
                                .build()
                        ))
                        .partyType(PartyType.ORGANIZATION)
                        .partyIdentification(List.of(
                            PartyIdentification.builder()
                                .partyIdentificationType(PartyIdentificationType.TAX_IDENTIFICATION_NUMBER)
                                .partyIdentification(Identifier.builder()
                                    .identifierValue(response.getRif())
                                    .build())
                                .build(),
                            PartyIdentification.builder()
                                .partyIdentificationType(PartyIdentificationType.TELEPHONE_NUMBER)
                                .partyIdentification(Identifier.builder()
                                    .identifierValue(response.getCelular())
                                    .build())
                                .build(),
                            PartyIdentification.builder()
                                .partyIdentificationType(PartyIdentificationType.TELEPHONE_OPERATOR)
                                .partyIdentification(Identifier.builder()
                                    .identifierValue(response.getOperadora())
                                    .build())
                                .build()
                        ))
                        .build()
                )
                .build()
        );


        return Account.builder()
            .accountStatus(status)
            .accountType(type)
            .accountIdentification(getAccountIdentifications(response))
            .accountBalance(getAccountBalances(response))
            .accountCurrency(currencies)
            .accountInvolvement(involvements)
            .accountRelationship(relationships)
            .agreement(agreements)
            .build();
    }

    private static AccountStatusType getAccountStatusType(
        DataSourceConfig dataSourceConfig,
        SPAccountResponse response
    ) {
        String accountStatus = response.getEstatus().toUpperCase();

        if (dataSourceConfig.getSpAccountStatusEnable().contains(accountStatus)) return AccountStatusType.ENABLED;
        if (dataSourceConfig.getSpAccountStatusDisabled().contains(accountStatus)) return AccountStatusType.DISABLED;
        return AccountStatusType.PENDING;
    }

    private static List<AccountIdentification> getAccountIdentifications(
        SPAccountResponse response
    ) {
        return List.of(
            AccountIdentification.builder()
                .accountIdentificationType(AccountIdentificationType.ACCOUNT_NUMBER)
                .accountIdentification(Identifier.builder()
                    .identifierValue(response.getCuenta())
                    .build())
                .build(),
            AccountIdentification.builder()
                .accountIdentificationType(AccountIdentificationType.BANK_NUMBER)
                .accountIdentification(Identifier.builder()
                    .identifierValue(response.getBanco())
                    .build())
                .build(),
            AccountIdentification.builder()
                .accountIdentificationType(AccountIdentificationType.PRODUCT_CODE)
                .accountIdentification(Identifier.builder()
                    .identifierValue(response.getProducto())
                    .build())
                .build(),
            AccountIdentification.builder()
                .accountIdentificationType(AccountIdentificationType.ACCOUNT_CLASS)
                .accountIdentification(Identifier.builder()
                    .identifierValue(response.getClaseCta())
                    .build())
                .build()
        );
    }

    private static  List<AccountBalance> getAccountBalances(
        SPAccountResponse response
    ) {
        return List.of(
            AccountBalance.builder()
                .balanceAmount(response.getSaldoDisponible())
                .balanceType(BalanceType.AVAILABLE_BALANCE)
                .build(),
            AccountBalance.builder()
                .balanceAmount(response.getMaxDia())
                .balanceType(BalanceType.DAILY_MAXIMUM)
                .build(),
            AccountBalance.builder()
                .balanceAmount(response.getMaxMens())
                .balanceType(BalanceType.MONTHLY_MAXIMUM)
                .build(),
            AccountBalance.builder()
                .balanceAmount(response.getMinTran())
                .balanceType(BalanceType.TRANSACTION_MINIMUM)
                .build(),
            AccountBalance.builder()
                .balanceAmount(response.getMaxTran())
                .balanceType(BalanceType.TRANSACTION_MAXIMUM)
                .build()
        );
    }

    public static SPAccountResponse toSpAccount(
        Logger log,
        ResultSet rs
    ) {
        return SPAccountResponse.builder()
            .parentChild(getSafeString(log, rs, "padre_hijo"))
            .rif(getSafeString(log, rs, "RIF"))
            .nombre(getSafeString(log, rs, "NOMBRE"))
            .banco(getSafeString(log, rs, "BANCO"))
            .cuenta(getSafeString(log, rs, "CUENTA"))
            .producto(getSafeString(log, rs, "PRODUCTO"))
            .claseCta(getSafeString(log, rs, "CLASE_CTA"))
            .tipoCta(getSafeString(log, rs, "TIPO_CTA"))
            .moneda(getSafeString(log, rs, "MONEDA"))
            .saldoDisponible(getSafeBigDecimal(log, rs, "SALDO_DISPONIBLE"))
            .estatus(getSafeString(log, rs, "ESTATUS"))
            .maxDia(getSafeBigDecimal(log, rs, "MAX_DIA"))
            .maxTran(getSafeBigDecimal(log, rs, "MAX_TRAN"))
            .maxMens(getSafeBigDecimal(log, rs, "MAX_MENS"))
            .minTran(getSafeBigDecimal(log, rs, "MIN_TRAN"))
            .operadora(getSafeString(log, rs, "OPERADORA"))
            .celular(getSafeString(log, rs, "CELULAR"))
            .canal(getSafeString(log, rs, "CANAL"))
            .servicio(getSafeString(log, rs, "SERVICIO"))
            .statusAfil(getSafeString(log, rs, "STATUSAFIL2"))
            .cashea(getSafeString(log, rs, "CASHEA"))
            .codError(getSafeString(log, rs, "COD_ERROR"))
            .dscError(getSafeString(log, rs, "DSC_ERROR"))
            .build();
    }

    private static String getSafeString(
        Logger log,
        ResultSet rs,
        String columnName
    ) {
        try {
            String value = rs.getString(columnName);
            if (value == null) {
                return null;
            }
            return value.trim().replaceAll("\\s+", " ").trim();
        } catch (SQLException e) {
            log.warn("Columna {} no encontrada como string, retornando null", columnName);
            return null;
        }
    }

    private static BigDecimal getSafeBigDecimal(
        Logger log,
        ResultSet rs, 
        String columnName
    ) {
        try {
            return rs.getBigDecimal(columnName);
        } catch (SQLException e) {
            log.warn("Columna {} no encontrada como numerico, retornando null", columnName);
            return BigDecimal.valueOf(0.0);
        }
    }
}
