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.common.util.DateUtil;
import com.ga.core.mapper.commission.ClawbackInstallmentPlanMapper;
import com.ga.core.mapper.commission.ClawbackReceivableMapper;
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.ArrayList;
import java.util.List;

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

    private final ClawbackInstallmentPlanMapper planMapper;
    private final ClawbackReceivableMapper receivableMapper;

    public PageResponse<ClawbackInstallmentPlanResp> list(ClawbackInstallmentPlanSearchParam param) {
        param.startPage();
        return PageResponse.of(planMapper.selectList(param));
    }

    public ClawbackInstallmentPlanResp detail(Long planId) {
        ClawbackInstallmentPlanResp resp = planMapper.selectById(planId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        return resp;
    }

    public List<ClawbackInstallmentPlanVO> listByReceivable(Long receivableId) {
        return planMapper.selectByReceivable(receivableId);
    }

    /**
     * 환수채권을 N회차로 균등 분할상환 계획 생성.
     * 마지막 회차는 잔차(총액 - 앞 회차 합계) 흡수.
     * 기준월: baseMonth(YYYYMM), due_month = baseMonth + (회차-1)
     */
    @Transactional
    public void generate(Long receivableId, int count, String baseMonth) {
        if (count < 1) throw new BizException(ErrorCode.INVALID_PARAMETER);

        // 이미 SCHEDULED 계획이 있으면 중복 방지
        List<ClawbackInstallmentPlanVO> existing = planMapper.selectByReceivable(receivableId);
        boolean hasScheduled = existing.stream()
                .anyMatch(p -> "SCHEDULED".equals(p.getStatus()));
        if (hasScheduled) throw new BizException(ErrorCode.DUPLICATE_DATA);

        ClawbackReceivableResp receivableResp = receivableMapper.selectById(receivableId);
        if (receivableResp == null) throw new BizException(ErrorCode.NOT_FOUND);

        BigDecimal total = receivableResp.getBalance();
        if (total == null || total.compareTo(BigDecimal.ZERO) <= 0)
            throw new BizException(ErrorCode.INVALID_PARAMETER);

        BigDecimal unit = total.divide(BigDecimal.valueOf(count), 2, RoundingMode.DOWN);
        BigDecimal sumBefore = unit.multiply(BigDecimal.valueOf(count - 1));
        BigDecimal last = total.subtract(sumBefore);

        List<ClawbackInstallmentPlanVO> plans = new ArrayList<>();
        for (int i = 1; i <= count; i++) {
            ClawbackInstallmentPlanVO vo = new ClawbackInstallmentPlanVO();
            vo.setReceivableId(receivableId);
            vo.setInstallmentNo(i);
            vo.setInstallmentAmount(i == count ? last : unit);
            vo.setDueMonth(DateUtil.addMonth(baseMonth, i - 1));
            vo.setStatus("SCHEDULED");
            plans.add(vo);
        }
        planMapper.insertBatch(plans);
    }

    /**
     * 분할상환 계획 취소 (SCHEDULED → CANCELLED).
     */
    @Transactional
    public void cancel(Long planId) {
        ClawbackInstallmentPlanResp resp = planMapper.selectById(planId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        if (!"SCHEDULED".equals(resp.getStatus())) throw new BizException(ErrorCode.INVALID_PARAMETER);
        planMapper.updateStatus(planId, "CANCELLED");
    }
}
