package com.ga.batch.settlement.step;

import com.ga.batch.settlement.SettlementContext;
import com.ga.batch.settlement.calc.CommissionCalculator;
import com.ga.common.util.DateUtil;
import com.ga.common.util.MoneyUtil;
import com.ga.core.mapper.ledger.MaintainLedgerMapper;
import com.ga.core.mapper.product.ContractMapper;
import com.ga.core.mapper.succession.ContractSuccessionMapper;
import com.ga.core.vo.ledger.LedgerVO;
import com.ga.core.vo.product.ContractResp;
import com.ga.core.vo.product.ContractSearchParam;
import com.ga.core.vo.succession.ContractSuccessionVO;
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.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Step 5 — calcMaintain: 유지수수료 계산 (2회차~)
 *
 * 유지수수료: 계약일로부터 N개월 경과한 활동 계약 (status=ACTIVE) 대상.
 *  - 1회차는 모집수수료에서 처리하므로 N >= 1 인 계약 대상.
 */
@Slf4j
@Component
@JobScope
@RequiredArgsConstructor
public class CalcMaintainStep implements Tasklet {

    private final SettlementContext ctx;
    private final ContractMapper contractMapper;
    private final MaintainLedgerMapper maintainMapper;
    private final ContractSuccessionMapper successionMapper;
    private final CommissionCalculator calculator;

    private static final int BATCH_SIZE = 1000;

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

        ContractSearchParam param = new ContractSearchParam();
        param.setStatus("ACTIVE");
        param.setPageSize(Integer.MAX_VALUE);
        List<ContractResp> contracts = contractMapper.selectList(param);

        maintainMapper.deleteBySettleMonth(ctx.getSettleMonth());

        // 계약 승계 반영: 정산월 기준 유효 승계(계약→후임설계사) 사전 로드 (N+1 회피)
        Map<Long, Long> successorByContract = new HashMap<>();
        for (ContractSuccessionVO s : successionMapper.selectEffectiveSuccessions(ctx.getSettleMonth())) {
            successorByContract.put(s.getContractId(), s.getToAgentId());
        }
        if (!successorByContract.isEmpty()) {
            log.info("[Step5] active successions applied: {}", successorByContract.size());
        }

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

        for (ContractResp c : contracts) {
            if (c.getContractDate() == null) continue;
            String contractMonth = DateUtil.getSettleMonth(c.getContractDate());
            int monthsElapsed = DateUtil.calcMonthDiff(contractMonth, ctx.getSettleMonth());
            if (monthsElapsed < 1) continue;       // 모집회차는 step4에서 처리
            int year = (monthsElapsed / 12) + 1;   // 1년차/2년차/...
            if (year > 5) continue;                // 통상 5년차까지

            // 납입주기 매칭 (월납이면 매월, 분기납이면 3개월마다 ...)
            if (!matchesPayCycle(c.getPayCycle(), monthsElapsed)) continue;

            // 승계된 계약이면 유지수수료 귀속을 후임 설계사로 (없으면 원 모집설계사)
            Long effectiveAgentId = successorByContract.getOrDefault(c.getContractId(), c.getAgentId());
            LedgerVO led = calculator.calcMaintainLedger(c, ctx.getSettleMonth(), year, effectiveAgentId);
            if (led == null || MoneyUtil.isZero(led.getAgentAmount())) continue;
            batch.add(led);
            if (batch.size() >= BATCH_SIZE) {
                maintainMapper.batchInsert(batch);
                total += batch.size();
                batch.clear();
            }
        }
        if (!batch.isEmpty()) {
            maintainMapper.batchInsert(batch);
            total += batch.size();
        }

        ctx.setMaintainCount(total);
        log.info("[Step5] maintain ledger inserted: {}", total);
        return RepeatStatus.FINISHED;
    }

    private boolean matchesPayCycle(String cycle, int monthsElapsed) {
        if (cycle == null) return true;
        return switch (cycle) {
            case "MONTHLY"   -> true;
            case "QUARTERLY" -> monthsElapsed % 3 == 0;
            case "ANNUAL"    -> monthsElapsed % 12 == 0;
            case "LUMP"      -> false;       // 일시납은 모집회차만
            default          -> true;
        };
    }
}
