package com.zioinfo.kintex.ticket.cancel;

import java.util.List;

/**
 * F-B2 티켓 취소·환불 산정(순수 함수 — 단위테스트 대상). 서버 권위(클라이언트 금액 미신뢰).
 *
 * <p>산식:
 * <ul>
 *   <li>ticketRefund = round(ticketAmount × 구간환불율%)</li>
 *   <li>adminFee = round(ticketRefund × 취소관리수수료%) — 환불액에서 공제</li>
 *   <li>bookingFeeRefund = (D-일 ≥ 예매수수료 환불 최소일) ? bookingFee : 0 (예매수수료 별도 규정)</li>
 *   <li>refund = ticketRefund − adminFee + bookingFeeRefund</li>
 *   <li>forfeit = (ticketAmount + bookingFee) − refund</li>
 * </ul>
 * D-일이 음수(개막 당일·이후)면 매칭 구간 0% → 티켓 환불 0.
 */
public final class TicketCancelCalc {

    private TicketCancelCalc() {
    }

    /** 다구간 — minDaysBefore 이상 남았을 때 refundRatePercent 적용. */
    public record Bracket(int minDaysBefore, double refundRatePercent, String label) {
    }

    /** 산정 결과(스냅샷). */
    public record Result(
            int daysBefore,
            long ticketAmount,
            long bookingFee,
            double refundRatePercent,
            String bracketLabel,
            long ticketRefund,
            long adminFee,
            long bookingFeeRefund,
            long refundAmount,
            long forfeitAmount) {
    }

    /** D-일에 해당하는 구간(조건 만족 중 minDaysBefore 최대). 없으면 null. */
    public static Bracket bracketFor(int daysBefore, List<Bracket> brackets) {
        if (brackets == null || brackets.isEmpty()) {
            return null;
        }
        Bracket best = null;
        for (Bracket b : brackets) {
            if (daysBefore >= b.minDaysBefore() && (best == null || b.minDaysBefore() > best.minDaysBefore())) {
                best = b;
            }
        }
        return best;
    }

    /**
     * 환불 산정.
     *
     * @param daysBefore           개막까지 남은 일수(음수=개막 당일·이후, null 미허용 — 미확정은 호출부가 0 처리)
     * @param ticketAmount         환불 대상 티켓액(예매수수료 제외)
     * @param bookingFee           예매수수료 총액
     * @param brackets             구간 목록
     * @param bookingFeeRefundMinDays 예매수수료 환불 최소 D-일
     * @param cancelAdminFeePercent   취소 관리수수료(%)
     */
    public static Result compute(int daysBefore, long ticketAmount, long bookingFee,
                                 List<Bracket> brackets, int bookingFeeRefundMinDays,
                                 double cancelAdminFeePercent) {
        long tAmt = Math.max(0, ticketAmount);
        long bFee = Math.max(0, bookingFee);
        Bracket br = bracketFor(daysBefore, brackets);
        double rate = br == null ? 0.0 : clampPct(br.refundRatePercent());
        String label = br == null ? "환불 불가 구간" : br.label();

        long ticketRefund = Math.round(tAmt * rate / 100.0);
        long adminFee = Math.round(ticketRefund * clampPct(cancelAdminFeePercent) / 100.0);
        long bookingFeeRefund = daysBefore >= bookingFeeRefundMinDays ? bFee : 0L;
        long refund = Math.max(0L, ticketRefund - adminFee + bookingFeeRefund);
        long forfeit = Math.max(0L, (tAmt + bFee) - refund);

        return new Result(daysBefore, tAmt, bFee, rate, label,
                ticketRefund, adminFee, bookingFeeRefund, refund, forfeit);
    }

    private static double clampPct(double p) {
        if (p < 0) {
            return 0;
        }
        return Math.min(p, 100.0);
    }
}
