package com.ga.batch.settlement.calc;

import com.ga.common.util.MoneyUtil;
import com.ga.core.mapper.org.AgentMapper;
import com.ga.core.mapper.rule.RuleMapper;
import com.ga.core.vo.ledger.LedgerVO;
import com.ga.core.vo.org.AgentVO;
import com.ga.core.vo.product.ContractResp;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDate;

/**
 * 수수료 계산 핵심 로직.
 *  - 모집수수료: premium × company_rate × payout_rate
 *  - 유지수수료: 동일 공식, 회차에 따른 commission_year 만 다름
 *  - 모든 금액은 BigDecimal HALF_UP, 소수점 2자리
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CommissionCalculator {

    private final RuleMapper ruleMapper;
    private final AgentMapper agentMapper;

    public LedgerVO calcRecruitLedger(ContractResp c, String settleMonth) {
        return buildLedger(c, settleMonth, 1, c.getAgentId());
    }

    public LedgerVO calcMaintainLedger(ContractResp c, String settleMonth, int commissionYear) {
        return buildLedger(c, settleMonth, commissionYear, c.getAgentId());
    }

    /**
     * 계약 승계 반영 유지수수료 계산.
     * effectiveAgentId 가 후임 설계사이면 해당 설계사 등급의 payout 비율로 계산되고
     * 원장 agent_id 도 후임으로 기록된다. null 이면 원 모집설계사로 동작.
     */
    public LedgerVO calcMaintainLedger(ContractResp c, String settleMonth, int commissionYear, Long effectiveAgentId) {
        return buildLedger(c, settleMonth, commissionYear,
                effectiveAgentId != null ? effectiveAgentId : c.getAgentId());
    }

    private LedgerVO buildLedger(ContractResp c, String settleMonth, int year, Long agentId) {
        AgentVO agent = agentMapper.selectById(agentId);
        if (agent == null) return null;
        LocalDate baseDate = c.getContractDate() != null ? c.getContractDate() : LocalDate.now();

        BigDecimal companyRate = ruleMapper.findCompanyRate(c.getProductId(), year, baseDate);
        if (companyRate == null) {
            log.warn("[수수료율 누락] company_rate 없음 → 0 폴백(과소지급 위험): contractId={}, productId={}, year={}, baseDate={}",
                    c.getContractId(), c.getProductId(), year, baseDate);
            companyRate = BigDecimal.ZERO;
        }

        BigDecimal payoutRate = ruleMapper.findPayoutRate(
                agent.getGradeId(), c.getInsuranceType(), year, baseDate);
        if (payoutRate == null) {
            log.warn("[수수료율 누락] payout_rate 없음 → 0 폴백(과소지급 위험): contractId={}, gradeId={}, insuranceType={}, year={}, baseDate={}",
                    c.getContractId(), agent.getGradeId(), c.getInsuranceType(), year, baseDate);
            payoutRate = BigDecimal.ZERO;
        }

        BigDecimal premium = MoneyUtil.zero(c.getPremium());
        BigDecimal companyAmount = MoneyUtil.pct(premium, companyRate);
        BigDecimal agentAmount = MoneyUtil.pct(companyAmount, payoutRate);
        BigDecimal taxAmount = MoneyUtil.tax(agentAmount);

        LedgerVO led = new LedgerVO();
        led.setContractId(c.getContractId());
        led.setAgentId(agentId);
        led.setPolicyNo(c.getPolicyNo());
        led.setCompanyCode(c.getCompanyCode());
        led.setProductCode(c.getProductCode());
        led.setInsuranceType(c.getInsuranceType());
        led.setCommissionYear(year);
        led.setPremium(premium);
        led.setPayMethod(c.getPayCycle());
        led.setCompanyRate(companyRate);
        led.setCompanyAmount(companyAmount);
        led.setPayoutRate(payoutRate);
        led.setAgentAmount(agentAmount);
        led.setTaxAmount(taxAmount);
        led.setSettleMonth(settleMonth);
        led.setStatus("CALCULATED");
        return led;
    }
}
