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.RecruiterCommissionLedgerMapper;
import com.ga.core.mapper.commission.RecruiterCommissionRuleMapper;
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.util.List;

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

    private final RecruiterCommissionRuleMapper ruleMapper;
    private final RecruiterCommissionLedgerMapper ledgerMapper;

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

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

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

    @Transactional
    public Long createRule(RecruiterCommissionRuleSaveReq req) {
        RecruiterCommissionRuleVO vo = new RecruiterCommissionRuleVO();
        vo.setRecruiterType(req.getRecruiterType());
        vo.setCalcType(req.getCalcType());
        vo.setCommissionRate(req.getCommissionRate());
        vo.setFixedAmount(req.getFixedAmount());
        vo.setEffectiveFrom(req.getEffectiveFrom());
        vo.setEffectiveTo(req.getEffectiveTo());
        ruleMapper.insert(vo);
        return vo.getRuleId();
    }

    @Transactional
    public void updateRule(Long ruleId, RecruiterCommissionRuleSaveReq req) {
        RecruiterCommissionRuleResp resp = ruleMapper.selectById(ruleId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        RecruiterCommissionRuleVO vo = new RecruiterCommissionRuleVO();
        vo.setRuleId(ruleId);
        vo.setRecruiterType(req.getRecruiterType());
        vo.setCalcType(req.getCalcType());
        vo.setCommissionRate(req.getCommissionRate());
        vo.setFixedAmount(req.getFixedAmount());
        vo.setEffectiveFrom(req.getEffectiveFrom());
        vo.setEffectiveTo(req.getEffectiveTo());
        ruleMapper.update(vo);
    }

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

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

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

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

    /**
     * 도입수수료 원장 생성.
     * 활성 룰 목록 중 recruiterType + 오늘 기준 유효한 룰 우선 적용:
     *   RATE  → commissionAmount = baseAmount × commissionRate
     *   FIXED → commissionAmount = fixedAmount
     * 룰 없음 → 요청 commissionAmount 사용 (수동 등록 호환).
     */
    @Transactional
    public Long createLedger(RecruiterCommissionLedgerSaveReq req) {
        RecruiterCommissionRuleVO activeRule = findActiveRule(req.getRecruiterType());

        BigDecimal commissionAmount;
        if (activeRule != null) {
            if ("FIXED".equals(activeRule.getCalcType())) {
                if (activeRule.getFixedAmount() == null) throw new BizException(ErrorCode.NOT_FOUND);
                commissionAmount = activeRule.getFixedAmount().setScale(2, RoundingMode.HALF_UP);
            } else { // RATE
                BigDecimal rate = activeRule.getCommissionRate();
                if (rate == null) throw new BizException(ErrorCode.NOT_FOUND);
                commissionAmount = req.getBaseAmount().multiply(rate).setScale(2, RoundingMode.HALF_UP);
            }
        } else {
            commissionAmount = req.getCommissionAmount();
            if (commissionAmount == null) throw new BizException(ErrorCode.NOT_FOUND);
        }

        RecruiterCommissionLedgerVO vo = new RecruiterCommissionLedgerVO();
        vo.setRecruiterAgentId(req.getRecruiterAgentId());
        vo.setRecruitAgentId(req.getRecruitAgentId());
        vo.setSettleMonth(req.getSettleMonth());
        vo.setRecruiterType(req.getRecruiterType());
        vo.setBaseAmount(req.getBaseAmount());
        vo.setCommissionAmount(commissionAmount);
        vo.setStatus("CALCULATED");
        ledgerMapper.insert(vo);
        return vo.getLedgerId();
    }

    /** recruiterType + 오늘 날짜 기준 유효 룰 첫 번째 반환. */
    private RecruiterCommissionRuleVO findActiveRule(String recruiterType) {
        RecruiterCommissionRuleSearchParam param = new RecruiterCommissionRuleSearchParam();
        param.setRecruiterType(recruiterType);
        List<RecruiterCommissionRuleResp> list = ruleMapper.selectList(param);
        java.time.LocalDate today = java.time.LocalDate.now();
        return list.stream()
                .filter(r -> !today.isBefore(r.getEffectiveFrom())
                        && (r.getEffectiveTo() == null || !today.isAfter(r.getEffectiveTo())))
                .findFirst()
                .map(r -> {
                    RecruiterCommissionRuleVO vo = new RecruiterCommissionRuleVO();
                    vo.setRuleId(r.getRuleId());
                    vo.setRecruiterType(r.getRecruiterType());
                    vo.setCalcType(r.getCalcType());
                    vo.setCommissionRate(r.getCommissionRate());
                    vo.setFixedAmount(r.getFixedAmount());
                    vo.setEffectiveFrom(r.getEffectiveFrom());
                    vo.setEffectiveTo(r.getEffectiveTo());
                    return vo;
                })
                .orElse(null);
    }
}
