package com.ga.api.service.commission;

import com.ga.common.exception.BizException;
import com.ga.core.mapper.commission.PersistencyBonusMapper;
import com.ga.core.mapper.commission.PersistencyBonusRuleMapper;
import com.ga.core.vo.commission.PersistencyBonusRuleResp;
import com.ga.core.vo.commission.PersistencyBonusSaveReq;
import com.ga.core.vo.commission.PersistencyBonusVO;
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.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.math.BigDecimal;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class PersistencyBonusServiceTest {

    @Mock PersistencyBonusRuleMapper ruleMapper;
    @Mock PersistencyBonusMapper bonusMapper;

    @InjectMocks PersistencyBonusService service;

    private PersistencyBonusRuleResp rule(String bonusType, String threshold, String bonusValue) {
        PersistencyBonusRuleResp r = new PersistencyBonusRuleResp();
        r.setRuleId(7L);
        r.setBonusType(bonusType);
        r.setRetentionRateThreshold(threshold != null ? new BigDecimal(threshold) : null);
        r.setBonusValue(bonusValue != null ? new BigDecimal(bonusValue) : null);
        return r;
    }

    private PersistencyBonusSaveReq req(String retentionRate, String bonusAmount) {
        PersistencyBonusSaveReq req = new PersistencyBonusSaveReq();
        req.setAgentId(1L);
        req.setRuleId(7L);
        req.setBaseMonth("202506");
        req.setRetentionMonth(13);
        req.setSettleMonth("202506");
        req.setRetentionRate(retentionRate != null ? new BigDecimal(retentionRate) : null);
        req.setBonusAmount(bonusAmount != null ? new BigDecimal(bonusAmount) : null);
        return req;
    }

    @Test
    @DisplayName("유지율이 임계 미달이면 지급 차단(BizException), 원장 미생성")
    void belowThreshold_rejected() {
        when(ruleMapper.selectById(7L)).thenReturn(rule("RATE", "0.90", null));

        assertThatThrownBy(() -> service.upsertBonus(req("0.85", "30000")))
                .isInstanceOf(BizException.class);
        verify(bonusMapper, never()).upsert(any());
    }

    @Test
    @DisplayName("유지율이 임계 이상이면 정상 지급(경계=이상)")
    void atThreshold_ok() {
        when(ruleMapper.selectById(7L)).thenReturn(rule("RATE", "0.90", null));

        ArgumentCaptor<PersistencyBonusVO> captor = ArgumentCaptor.forClass(PersistencyBonusVO.class);
        service.upsertBonus(req("0.90", "30000"));

        verify(bonusMapper).upsert(captor.capture());
        assertThat(captor.getValue().getBonusAmount()).isEqualByComparingTo("30000");
    }

    @Test
    @DisplayName("FIXED형 룰이면 지급액을 룰의 bonus_value(정액)로 도출")
    void fixedRule_derivesAmountFromRule() {
        when(ruleMapper.selectById(7L)).thenReturn(rule("FIXED", "0.90", "50000"));

        ArgumentCaptor<PersistencyBonusVO> captor = ArgumentCaptor.forClass(PersistencyBonusVO.class);
        // 요청 bonusAmount(999)는 무시되고 룰 정액 50000 으로 대체
        service.upsertBonus(req("0.95", "999"));

        verify(bonusMapper).upsert(captor.capture());
        assertThat(captor.getValue().getBonusAmount()).isEqualByComparingTo("50000.00");
    }
}
