package com.ga.api.service.regulatory;

import com.ga.core.mapper.ledger.MaintainLedgerMapper;
import com.ga.core.mapper.ledger.RecruitLedgerMapper;
import com.ga.core.mapper.product.ContractMapper;
import com.ga.core.mapper.regulatory.RegulatoryLimitMapper;
import com.ga.core.vo.product.ContractVO;
import com.ga.core.vo.regulatory.RegulatoryLimitVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@Component
@RequiredArgsConstructor
public class RegulatoryLimitChecker {

    private static final String TOTAL_RATIO = "TOTAL_RATIO";
    private static final String FIRST_YEAR_RATIO = "FIRST_YEAR_RATIO";
    private static final String UNIT_PERCENT = "PERCENT";
    private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    private final RegulatoryLimitMapper limitMapper;
    private final RecruitLedgerMapper recruitLedgerMapper;
    private final MaintainLedgerMapper maintainLedgerMapper;
    private final ContractMapper contractMapper;

    /** 누적 수수료율 한도 체크 (1200%). 신계약 + 유지 원장 합산. */
    public ViolationResult checkContractTotal(Long contractId, BigDecimal additionalAmount, LocalDate baseDate) {
        String baseDateStr = baseDate.format(DATE_FMT);
        RegulatoryLimitVO limit = limitMapper.selectActiveByType(TOTAL_RATIO, null, baseDateStr);
        if (limit == null) {
            return ViolationResult.ok(additionalAmount, null, TOTAL_RATIO);
        }

        BigDecimal limitKrw = resolveLimitKrw(limit, contractId);
        BigDecimal currentTotal = sumPaidByContract(contractId);
        BigDecimal projected = currentTotal.add(additionalAmount);

        if (projected.compareTo(limitKrw) > 0) {
            return ViolationResult.violated(
                    currentTotal,
                    limitKrw,
                    TOTAL_RATIO,
                    String.format("누적 수수료 한도 초과: 현재 %s, 추가 %s, 한도 %s(%s %s)",
                            currentTotal, additionalAmount, limitKrw, limit.getLimitValue(), limit.getUnit())
            );
        }
        return ViolationResult.ok(currentTotal, limitKrw, TOTAL_RATIO);
    }

    /** 1차년 수수료율 한도 체크. 신계약 원장 1차년도만 (유지는 1차년 개념 없음). */
    public ViolationResult checkFirstYear(Long contractId, BigDecimal additionalAmount, LocalDate baseDate) {
        String baseDateStr = baseDate.format(DATE_FMT);
        RegulatoryLimitVO limit = limitMapper.selectActiveByType(FIRST_YEAR_RATIO, null, baseDateStr);
        if (limit == null) {
            return ViolationResult.ok(additionalAmount, null, FIRST_YEAR_RATIO);
        }

        BigDecimal limitKrw = resolveLimitKrw(limit, contractId);
        BigDecimal currentTotal = sumFirstYearPaidByContract(contractId);
        BigDecimal projected = currentTotal.add(additionalAmount);

        if (projected.compareTo(limitKrw) > 0) {
            return ViolationResult.violated(
                    currentTotal,
                    limitKrw,
                    FIRST_YEAR_RATIO,
                    String.format("1차년 수수료 한도 초과: 현재 %s, 추가 %s, 한도 %s(%s %s)",
                            currentTotal, additionalAmount, limitKrw, limit.getLimitValue(), limit.getUnit())
            );
        }
        return ViolationResult.ok(currentTotal, limitKrw, FIRST_YEAR_RATIO);
    }

    /** 비율 한도(PERCENT)를 계약 연환산보험료 기준 KRW 한도로 환산. */
    private BigDecimal resolveLimitKrw(RegulatoryLimitVO limit, Long contractId) {
        if (!UNIT_PERCENT.equalsIgnoreCase(limit.getUnit())) {
            return limit.getLimitValue();
        }
        ContractVO contract = contractMapper.selectById(contractId);
        BigDecimal annualPremium = (contract != null && contract.getPremium() != null)
                ? contract.getPremium() : BigDecimal.ZERO;
        return annualPremium.multiply(limit.getLimitValue())
                .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
    }

    private BigDecimal sumPaidByContract(Long contractId) {
        BigDecimal recruit = recruitLedgerMapper.sumByContract(contractId);
        BigDecimal maintain = maintainLedgerMapper.sumByContract(contractId);
        return nullSafe(recruit).add(nullSafe(maintain));
    }

    private BigDecimal sumFirstYearPaidByContract(Long contractId) {
        return nullSafe(recruitLedgerMapper.sumFirstYearByContract(contractId));
    }

    private BigDecimal nullSafe(BigDecimal value) {
        return value != null ? value : BigDecimal.ZERO;
    }
}
