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.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 org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

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

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

/**
 * 1200%룰 1차년 한도초과 → clip(한도까지만 지급) + 이연(초과분 분급) 검증.
 */
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class CalcRecruitStepTest {

    @Mock SettlementContext ctx;
    @Mock ContractMapper contractMapper;
    @Mock RecruitLedgerMapper recruitMapper;
    @Mock CommissionCalculator calculator;
    @Mock BatchRegulatoryLimitChecker regulatoryChecker;
    @Mock BatchInstallmentPlanGenerator installmentGenerator;
    @Mock InstallmentPlanMapper installmentPlanMapper;

    CalcRecruitStep step;

    static final String MONTH = "202401";
    static final Long CONTRACT_ID = 100L;
    static final LocalDate CONTRACT_DATE = LocalDate.of(2024, 1, 15);

    @BeforeEach
    void setUp() {
        step = new CalcRecruitStep(ctx, contractMapper, recruitMapper,
                calculator, regulatoryChecker, installmentGenerator, installmentPlanMapper);
        when(ctx.getSettleMonth()).thenReturn(MONTH);
    }

    @Test
    @DisplayName("1차년 한도 350,000 < 모집수수료 450,000 → 350,000 지급 + 초과 100,000 분급 이연")
    void firstYearLimit_clipAndDefer() {
        ContractResp c = new ContractResp();
        c.setContractId(CONTRACT_ID);
        c.setAgentId(10L);
        c.setInsuranceType("WHOLE_LIFE");
        c.setContractDate(CONTRACT_DATE);
        when(contractMapper.selectList(any())).thenReturn(List.of(c));

        LedgerVO led = new LedgerVO();
        led.setContractId(CONTRACT_ID);
        led.setAgentId(10L);
        led.setAgentAmount(new BigDecimal("450000"));
        led.setTaxAmount(new BigDecimal("14850"));
        when(calculator.calcRecruitLedger(eq(c), eq(MONTH))).thenReturn(led);

        // 한도 350,000(=연납 1,000,000 × 35%), 기지급 0 → 위반
        when(regulatoryChecker.checkFirstYear(eq(CONTRACT_ID), any(BigDecimal.class), any(LocalDate.class)))
                .thenReturn(ViolationResult.violated(
                        BigDecimal.ZERO, new BigDecimal("350000"), "FIRST_YEAR_RATIO", "초과"));

        when(installmentGenerator.generate(eq(CONTRACT_ID), any(BigDecimal.class), any(LocalDate.class), anyString()))
                .thenReturn(List.of(new InstallmentPlanVO()));

        ArgumentCaptor<List<LedgerVO>> ledgerCaptor = ArgumentCaptor.forClass(List.class);
        when(recruitMapper.batchInsert(ledgerCaptor.capture())).thenReturn(1);

        step.execute(null, null);

        // 초과분 100,000(=450,000−350,000) 이 분급으로 이연 생성
        ArgumentCaptor<BigDecimal> excessCaptor = ArgumentCaptor.forClass(BigDecimal.class);
        org.mockito.Mockito.verify(installmentGenerator)
                .generate(eq(CONTRACT_ID), excessCaptor.capture(), any(LocalDate.class), anyString());
        assertThat(excessCaptor.getValue()).isEqualByComparingTo("100000");

        // 당월 원장은 한도까지만(350,000) + 세액 재계산(350,000 × 3.3% = 11,550)
        LedgerVO saved = ledgerCaptor.getValue().get(0);
        assertThat(saved.getAgentAmount()).isEqualByComparingTo("350000");
        assertThat(saved.getTaxAmount()).isEqualByComparingTo("11550.00");
    }
}
