package com.ga.batch.settlement.step;

import com.ga.batch.settlement.SettlementContext;
import com.ga.batch.settlement.calc.CommissionCalculator;
import com.ga.batch.settlement.domain.BatchInstallmentPlanGenerator;
import com.ga.batch.settlement.domain.BatchRegulatoryLimitChecker;
import com.ga.batch.settlement.domain.ViolationResult;
import com.ga.common.util.DateUtil;
import com.ga.common.util.MoneyUtil;
import com.ga.core.mapper.installment.InstallmentPlanMapper;
import com.ga.core.mapper.ledger.RecruitLedgerMapper;
import com.ga.core.mapper.product.ContractMapper;
import com.ga.core.vo.installment.InstallmentPlanVO;
import com.ga.core.vo.ledger.LedgerVO;
import com.ga.core.vo.product.ContractResp;
import com.ga.core.vo.product.ContractSearchParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * Step 4 — calcRecruit: 모집 수수료 계산 (계약 첫 회차)
 *
 * 원칙: 계약시점의 commission_rate / payout_rule 적용 (effective_from 기준)
 *
 * 추가: 1200%룰(BatchRegulatoryLimitChecker) 검증 후 한도 초과 계약은
 * InstallmentPlan(분급 7년) 생성. Step 4.5 역할을 Step 4 내에서 수행.
 */
@Slf4j
@Component
@JobScope
@RequiredArgsConstructor
public class CalcRecruitStep implements Tasklet {

    private final SettlementContext ctx;
    private final ContractMapper contractMapper;
    private final RecruitLedgerMapper recruitMapper;
    private final CommissionCalculator calculator;
    private final BatchRegulatoryLimitChecker regulatoryChecker;
    private final BatchInstallmentPlanGenerator installmentGenerator;
    private final InstallmentPlanMapper installmentPlanMapper;

    private static final int BATCH_SIZE = 1000;

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
        log.info("[Step4 calcRecruit] settleMonth={}", ctx.getSettleMonth());

        // 모집수수료 대상: 계약일이 정산월에 해당하는 계약
        String[] range = DateUtil.getMonthRange(ctx.getSettleMonth());
        ContractSearchParam param = new ContractSearchParam();
        param.setStartDate(range[0]);
        param.setEndDate(range[1]);
        param.setPageSize(Integer.MAX_VALUE);
        List<ContractResp> contracts = contractMapper.selectList(param);

        // 정산월 모집원장 초기화 (재계산 대비)
        recruitMapper.deleteBySettleMonth(ctx.getSettleMonth());

        List<LedgerVO> batch = new ArrayList<>(BATCH_SIZE);
        List<InstallmentPlanVO> installmentBatch = new ArrayList<>();
        int total = 0;
        int violationCount = 0;

        for (ContractResp c : contracts) {
            LedgerVO led = calculator.calcRecruitLedger(c, ctx.getSettleMonth());
            if (led == null || MoneyUtil.isZero(led.getAgentAmount())) continue;

            // 1200%룰 검증 (1차년도 한도)
            if (c.getContractDate() != null) {
                ViolationResult firstYearCheck = regulatoryChecker.checkFirstYear(
                        c.getContractId(), led.getAgentAmount(), c.getContractDate());

                if (firstYearCheck.violated()) {
                    violationCount++;

                    // 1차년 한도 잔여분까지만 당월 지급, 초과분은 분급(7년)으로 이연.
                    BigDecimal allowed = MoneyUtil.sub(
                            firstYearCheck.limitAmount(), firstYearCheck.currentTotal());
                    if (allowed.signum() < 0) allowed = BigDecimal.ZERO;
                    BigDecimal excess = MoneyUtil.sub(led.getAgentAmount(), allowed);

                    if (excess.signum() > 0) {
                        // 초과분만 분급 계획 생성 (7년 35/25/15/10/7/5/3)
                        List<InstallmentPlanVO> plans = installmentGenerator.generate(
                                c.getContractId(), excess,
                                c.getContractDate(), c.getInsuranceType());
                        installmentBatch.addAll(plans);
                        // 당월 원장은 한도까지만 지급(초과분 차감) + 세액 재계산
                        led.setAgentAmount(allowed);
                        led.setTaxAmount(MoneyUtil.tax(allowed));
                        log.warn("[Step4] 1200%룰 1차년 한도초과 → clip+이연: contractId={}, 지급(한도잔여)={}, 이연(분급)={}",
                                c.getContractId(), allowed, excess);
                    }
                }
            }

            // 한도로 전액 이연되어 당월 지급액이 0이면 원장 미생성
            if (MoneyUtil.isZero(led.getAgentAmount())) continue;
            batch.add(led);
            if (batch.size() >= BATCH_SIZE) {
                recruitMapper.batchInsert(batch);
                total += batch.size();
                batch.clear();
            }
        }
        if (!batch.isEmpty()) {
            recruitMapper.batchInsert(batch);
            total += batch.size();
        }

        // 분급 계획 일괄 저장
        if (!installmentBatch.isEmpty()) {
            installmentPlanMapper.insertBatch(installmentBatch);
            log.info("[Step4] installment plans created: {} (violations: {})",
                    installmentBatch.size(), violationCount);
        }

        ctx.setRecruitCount(total);
        log.info("[Step4] recruit ledger inserted: {}, regulatory violations: {}", total, violationCount);
        return RepeatStatus.FINISHED;
    }
}
