import { describe, expect, test } from "vitest";
import {
    addTierToStage,
    correctStageTierProbabilities,
    emptyLuckyGiftForm,
    payloadFromLuckyGiftForm,
    stageExpectedRTP,
    stageProbabilityTotal,
    updateStageTier,
} from "@/features/lucky-gift/configModel.js";

describe("lucky gift config payload", () => {
    test("submits business units required by the v2 admin contract", () => {
        const payload = payloadFromLuckyGiftForm(emptyLuckyGiftForm());

        expect(payload.target_rtp_percent).toBe(95);
        expect(payload.pool_rate_percent).toBe(96);
        expect(payload.control_band_percent).toBe(1);
        expect(payload.gift_price_reference).toBe(100);
        expect(payload.novice_max_equivalent_draws).toBe(2000);
        expect(payload.normal_max_equivalent_draws).toBe(20000);
        expect(payload.max_single_payout).toBe(50000);
        expect(payload.user_hourly_payout_cap).toBe(34200);
        expect(payload.user_daily_payout_cap).toBe(615600);
        expect(payload.device_daily_payout_cap).toBe(1026000);
        expect(payload.room_hourly_payout_cap).toBe(684000);
        expect(payload.anchor_daily_payout_cap).toBe(12312000);
        expect(payload).not.toHaveProperty("target_rtp_ppm");
        expect(payload).not.toHaveProperty("multiplier_ppms");
        expect(payload.stages.map((stage) => stage.stage)).toEqual(["novice", "normal", "advanced"]);
    });

    test("keeps each default stage probability at 100 percent", () => {
        const form = emptyLuckyGiftForm();

        expect(form.stages.map(stageProbabilityTotal)).toEqual([100, 100, 100]);
    });

    test("keeps each default stage expected RTP at target percent", () => {
        const form = emptyLuckyGiftForm();

        expect(form.stages.map(stageExpectedRTP)).toEqual([95, 95, 95]);
    });

    test("updates a single tier without mutating other stages", () => {
        const form = emptyLuckyGiftForm();
        const nextStages = updateStageTier(form.stages, "normal", 0, { probabilityPercent: "39.5" });

        expect(nextStages.find((stage) => stage.stage === "normal").tiers[0].probabilityPercent).toBe("39.5");
        expect(nextStages.find((stage) => stage.stage === "novice").tiers[0].probabilityPercent).toBe(10);
    });

    test("adds explicit editable tier to requested stage", () => {
        const form = emptyLuckyGiftForm();
        const nextStages = addTierToStage(form.stages, "advanced");
        const advanced = nextStages.find((stage) => stage.stage === "advanced");

        expect(advanced.tiers).toHaveLength(5);
        expect(advanced.tiers[4]).toMatchObject({
            broadcastLevel: "none",
            highWaterOnly: true,
            multiplier: 10,
            rewardSource: "base_rtp",
            tierId: "advanced_10x",
        });
    });

    test("regenerates unique tier ids when building submit payload", () => {
        const form = emptyLuckyGiftForm();
        const advanced = form.stages.find((stage) => stage.stage === "advanced");
        advanced.tiers = [
            ...advanced.tiers,
            {
                ...advanced.tiers[advanced.tiers.length - 1],
                multiplier: 5,
                probabilityPercent: 0,
                tierId: "manual_duplicate",
            },
        ];

        const payload = payloadFromLuckyGiftForm(form);
        const advancedPayload = payload.stages.find((stage) => stage.stage === "advanced");

        expect(advancedPayload.tiers.map((tier) => tier.tier_id)).toEqual([
            "advanced_none",
            "advanced_1x",
            "advanced_2x",
            "advanced_5x",
            "advanced_5x_2",
        ]);
    });

    test("corrects a stage probability to requested RTP without zeroing enabled tiers", () => {
        const form = emptyLuckyGiftForm();
        const nextStages = correctStageTierProbabilities(form.stages, "normal", 95);
        const normal = nextStages.find((stage) => stage.stage === "normal");

        expect(stageProbabilityTotal(normal)).toBeCloseTo(100, 6);
        expect(stageExpectedRTP(normal)).toBeCloseTo(95, 6);
        expect(enabledProbabilityPPMTotal(normal)).toBe(1_000_000);
        expect(normal.tiers.map((tier) => Number(tier.multiplier))).toEqual([0, 0.5, 1, 2]);
        expect(normal.tiers.every((tier) => Number(tier.probabilityPercent) > 0)).toBe(true);
    });

    test("clamps correction to closest reachable RTP when multiplier range cannot cover target", () => {
        const form = emptyLuckyGiftForm();
        const nextStages = correctStageTierProbabilities(form.stages, "novice", 250);
        const novice = nextStages.find((stage) => stage.stage === "novice");

        expect(stageProbabilityTotal(novice)).toBeCloseTo(100, 6);
        expect(stageExpectedRTP(novice)).toBeCloseTo(199.9995, 6);
        expect(novice.tiers.every((tier) => Number(tier.probabilityPercent) > 0)).toBe(true);
    });

    test("balances RTP contribution across high multipliers including 100x", () => {
        const form = emptyLuckyGiftForm();
        const advanced = form.stages.find((stage) => stage.stage === "advanced");
        advanced.tiers = [
            luckyTier("advanced_none", 0),
            luckyTier("advanced_1x", 1),
            luckyTier("advanced_2x", 2),
            luckyTier("advanced_5x", 5, { highWaterOnly: true }),
            luckyTier("advanced_10x", 10, { highWaterOnly: true }),
            luckyTier("advanced_100x", 100, { highWaterOnly: true }),
        ];

        const nextStages = correctStageTierProbabilities(form.stages, "advanced", 95);
        const corrected = nextStages.find((stage) => stage.stage === "advanced");
        const positiveTiers = corrected.tiers.filter((tier) => Number(tier.multiplier) > 0);
        const contributions = positiveTiers.map((tier) => Number(tier.multiplier) * Number(tier.probabilityPercent));
        const tier100x = corrected.tiers.find((tier) => tier.tierId === "advanced_100x");

        expect(stageProbabilityTotal(corrected)).toBeCloseTo(100, 6);
        expect(stageExpectedRTP(corrected)).toBeCloseTo(95, 6);
        expect(enabledProbabilityPPMTotal(corrected)).toBe(1_000_000);
        expect(corrected.tiers.every((tier) => Number(tier.probabilityPercent) > 0)).toBe(true);
        expect(Number(tier100x.probabilityPercent)).toBeGreaterThan(0.01);
        expect(Math.max(...contributions) - Math.min(...contributions)).toBeLessThan(1.1);
    });
});

function luckyTier(tierId, multiplier, options = {}) {
    return {
        broadcastLevel: "none",
        enabled: true,
        highWaterOnly: Boolean(options.highWaterOnly),
        multiplier,
        probabilityPercent: 1,
        rewardSource: "base_rtp",
        tierId,
    };
}

function enabledProbabilityPPMTotal(stage) {
    return stage.tiers.reduce((sum, tier) => {
        if (tier.enabled === false) {
            return sum;
        }
        return sum + Math.round(Number(tier.probabilityPercent) * 10_000);
    }, 0);
}
