package com.ga.api.service.commission;

import com.ga.common.exception.BizException;
import com.ga.common.exception.ErrorCode;
import com.ga.common.model.PageResponse;
import com.ga.core.mapper.commission.AwardLedgerMapper;
import com.ga.core.mapper.commission.AwardRuleMapper;
import com.ga.core.vo.commission.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class AwardService {

    private final AwardRuleMapper ruleMapper;
    private final AwardLedgerMapper ledgerMapper;

    // ── Rule ──────────────────────────────────────────────────────────────────

    public PageResponse<AwardRuleResp> listRules(AwardRuleSearchParam param) {
        param.startPage();
        return PageResponse.of(ruleMapper.selectList(param));
    }

    public AwardRuleResp detailRule(Long ruleId) {
        AwardRuleResp resp = ruleMapper.selectById(ruleId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        return resp;
    }

    @Transactional
    public Long createRule(AwardRuleSaveReq req) {
        AwardRuleVO vo = new AwardRuleVO();
        vo.setAwardType(req.getAwardType());
        vo.setCalcType(req.getCalcType());
        vo.setThresholdAmount(req.getThresholdAmount());
        vo.setAwardRate(req.getAwardRate());
        vo.setAwardFixed(req.getAwardFixed());
        vo.setEffectiveFrom(req.getEffectiveFrom());
        vo.setEffectiveTo(req.getEffectiveTo());
        ruleMapper.insert(vo);
        return vo.getRuleId();
    }

    @Transactional
    public void updateRule(Long ruleId, AwardRuleSaveReq req) {
        AwardRuleResp resp = ruleMapper.selectById(ruleId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        AwardRuleVO vo = new AwardRuleVO();
        vo.setRuleId(ruleId);
        vo.setAwardType(req.getAwardType());
        vo.setCalcType(req.getCalcType());
        vo.setThresholdAmount(req.getThresholdAmount());
        vo.setAwardRate(req.getAwardRate());
        vo.setAwardFixed(req.getAwardFixed());
        vo.setEffectiveFrom(req.getEffectiveFrom());
        vo.setEffectiveTo(req.getEffectiveTo());
        ruleMapper.update(vo);
    }

    @Transactional
    public void deleteRule(Long ruleId) {
        AwardRuleResp resp = ruleMapper.selectById(ruleId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        ruleMapper.delete(ruleId);
    }

    // ── Ledger ────────────────────────────────────────────────────────────────

    public PageResponse<AwardLedgerResp> listLedgers(AwardLedgerSearchParam param) {
        param.startPage();
        return PageResponse.of(ledgerMapper.selectList(param));
    }

    public AwardLedgerResp detailLedger(Long ledgerId) {
        AwardLedgerResp resp = ledgerMapper.selectById(ledgerId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        return resp;
    }

    @Transactional
    public Long createLedger(AwardLedgerSaveReq req) {
        // 룰 조회로 calc_type 결정
        AwardRuleVO rule = ruleMapper.selectActiveRule(req.getAwardType(), LocalDate.now());
        if (rule == null) throw new BizException(ErrorCode.NOT_FOUND);

        // threshold_amount 이상 실적 달성 시에만 시상 발동 (스펙 7-7). 미달 시 시상 없음.
        if (rule.getThresholdAmount() != null
                && req.getBaseAmount().compareTo(rule.getThresholdAmount()) < 0) {
            throw new BizException(ErrorCode.BAD_REQUEST,
                    String.format("시상 기준 실적 미달: 실적 %s < 기준 %s",
                            req.getBaseAmount(), rule.getThresholdAmount()));
        }

        BigDecimal awardAmount;
        if ("FIXED".equals(rule.getCalcType())) {
            if (rule.getAwardFixed() == null) {
                throw new BizException(ErrorCode.BAD_REQUEST, "FIXED 시상 룰에 award_fixed 가 설정되지 않았습니다");
            }
            awardAmount = rule.getAwardFixed().setScale(2, RoundingMode.HALF_UP);
        } else {
            // RATE: base × award_rate
            awardAmount = req.getBaseAmount()
                    .multiply(rule.getAwardRate())
                    .setScale(2, RoundingMode.HALF_UP);
        }

        AwardLedgerVO vo = new AwardLedgerVO();
        vo.setAgentId(req.getAgentId());
        vo.setSettleMonth(req.getSettleMonth());
        vo.setAwardType(req.getAwardType());
        vo.setBaseAmount(req.getBaseAmount());
        vo.setAwardAmount(awardAmount);
        vo.setStatus("CALCULATED");
        ledgerMapper.insert(vo);
        return vo.getLedgerId();
    }
}
