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.IncomeCommissionLedgerMapper;
import com.ga.core.mapper.commission.MarginSummaryMapper;
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;

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

    private final IncomeCommissionLedgerMapper ledgerMapper;
    private final MarginSummaryMapper marginMapper;

    // ── Income Ledger ────────────────────────────────────────────────────────

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

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

    @Transactional
    public Long createLedger(IncomeCommissionLedgerSaveReq req) {
        IncomeCommissionLedgerVO vo = new IncomeCommissionLedgerVO();
        vo.setContractId(req.getContractId());
        vo.setCarrierId(req.getCarrierId());
        vo.setSettleMonth(req.getSettleMonth());
        vo.setPremiumAmount(req.getPremiumAmount());
        vo.setCompanyRate(req.getCompanyRate());
        // 수입수수료 = 보험료 × 보험사요율
        vo.setIncomeAmount(req.getPremiumAmount()
                .multiply(req.getCompanyRate())
                .setScale(2, RoundingMode.HALF_UP));
        vo.setCommissionType(req.getCommissionType());
        vo.setStatus("CALCULATED");
        ledgerMapper.insert(vo);
        return vo.getIncomeId();
    }

    // ── Margin Summary ───────────────────────────────────────────────────────

    public PageResponse<MarginSummaryResp> listMargins(MarginSummarySearchParam param) {
        param.startPage();
        return PageResponse.of(marginMapper.selectList(param));
    }

    public MarginSummaryResp detailMargin(Long marginId) {
        MarginSummaryResp resp = marginMapper.selectById(marginId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        return resp;
    }

    /**
     * 수지차 재계산 (recalc).
     * settle_month 단위로 income_total(수입수수료 합) − payout_total(settle_master net_amount 합) 산출.
     * 수입수수료 원장에는 설계사 귀속이 없어(보험사·계약 단위) income 은 월 전체 합을 쓰고,
     * agentId 가 주어지면 payout 만 해당 설계사로 좁혀 집계한다. (미지정 시 GA 월 전체 수지차)
     * 수지차 = income_total − payout_total, 수지차율 = 수지차 / income_total (income_total > 0).
     */
    @Transactional
    public Long recalcMargin(String settleMonth, Long agentId) {
        BigDecimal incomeTotal = marginMapper.sumIncomeByMonth(settleMonth);
        if (incomeTotal == null) incomeTotal = BigDecimal.ZERO;

        BigDecimal payoutTotal = marginMapper.sumPayoutByMonth(settleMonth, agentId);
        if (payoutTotal == null) payoutTotal = BigDecimal.ZERO;

        BigDecimal marginAmount = incomeTotal.subtract(payoutTotal).setScale(2, RoundingMode.HALF_UP);
        BigDecimal marginRate = BigDecimal.ZERO;
        if (incomeTotal.compareTo(BigDecimal.ZERO) > 0) {
            marginRate = marginAmount.divide(incomeTotal, 4, RoundingMode.HALF_UP);
        }

        MarginSummaryVO existing = marginMapper.selectByMonthAndAgent(settleMonth, agentId);
        if (existing != null) {
            existing.setIncomeTotal(incomeTotal);
            existing.setPayoutTotal(payoutTotal);
            existing.setMarginAmount(marginAmount);
            existing.setMarginRate(marginRate);
            marginMapper.update(existing);
            return existing.getMarginId();
        } else {
            MarginSummaryVO vo = new MarginSummaryVO();
            vo.setSettleMonth(settleMonth);
            vo.setAgentId(agentId);
            vo.setIncomeTotal(incomeTotal);
            vo.setPayoutTotal(payoutTotal);
            vo.setMarginAmount(marginAmount);
            vo.setMarginRate(marginRate);
            marginMapper.insert(vo);
            return vo.getMarginId();
        }
    }
}
