import { describe, expect, test } from "vitest";
import { wheelConfigFormSchema } from "@/features/operations/schema";
import {
    generateWheelTierProbabilities,
    groupItemToTier,
    wheelPools,
} from "@/features/operations/hooks/useWheelPage.js";

function tier(overrides) {
    return {
        displayName: "奖品",
        enabled: true,
        metadataJson: "{}",
        probabilityPercent: 0,
        rewardCoins: 0,
        rewardCount: 1,
        rewardId: "1",
        rewardType: "gift",
        rtpValueCoins: 0,
        tierId: "classic-1",
        ...overrides,
    };
}

function probabilityPPM(tiers) {
    return tiers.reduce((sum, item) => sum + Math.round(item.probabilityPercent * 10_000), 0);
}

function wheelExpectedRTP(tiers, drawPriceCoins) {
    return tiers.reduce((sum, item) => {
        if (item.rewardType === "coin") {
            return sum + (Number(item.rewardCoins || 0) / drawPriceCoins) * Number(item.probabilityPercent || 0);
        }
        if (item.rewardType === "gift") {
            return sum + (Number(item.rtpValueCoins || 0) / drawPriceCoins) * Number(item.probabilityPercent || 0);
        }
        return sum;
    }, 0);
}

describe("wheel prize probability generation", () => {
    test("stores resource coin price in metadata for prop and dress display", () => {
        const result = groupItemToTier(
            {
                itemType: "resource",
                quantity: 1,
                resource: {
                    assetUrl: "https://example.com/a.png",
                    coinPrice: 1314,
                    name: "道具",
                    previewUrl: "https://example.com/p.png",
                    resourceCode: "prop_a",
                    resourceId: 326,
                    resourceType: "prop",
                },
            },
            "classic",
            0,
        );
        const metadata = JSON.parse(result.metadataJson);

        expect(result.rewardType).toBe("prop");
        expect(result.rtpValueCoins).toBe(0);
        expect(metadata.coin_price).toBe(1314);
        expect(metadata.display_value).toBe(1314);
        expect(metadata.value).toBe(1314);
    });

    test("uses eight prize slots for the classic wheel", () => {
        const classicPool = wheelPools.find((pool) => pool.key === "classic");
        const tiers = Array.from({ length: 8 }, (_, index) =>
            tier({
                displayName: `奖品 ${index + 1}`,
                rewardCoins: 100,
                rewardId: "",
                rewardType: "coin",
                rtpValueCoins: 100,
                tierId: `classic-${index + 1}`,
            }),
        );

        expect(classicPool?.tierCount).toBe(8);
        expect(
            wheelConfigFormSchema.safeParse({
                drawPriceCoins: 100,
                effectiveFromMs: 0,
                enabled: true,
                initialPoolCoins: 0,
                maxSingleRTPPayout: 0,
                poolRatePercent: 90,
                poolReserveCoins: 0,
                settlementWindowDraws: 1000,
                targetRTPPercent: 90,
                tiers: generateWheelTierProbabilities({ drawPriceCoins: 100, targetRTPPercent: 90, tiers }),
                wheelId: "classic",
            }).success,
        ).toBe(true);
        expect(
            wheelConfigFormSchema.safeParse({
                drawPriceCoins: 100,
                effectiveFromMs: 0,
                enabled: true,
                initialPoolCoins: 0,
                maxSingleRTPPayout: 0,
                poolRatePercent: 90,
                poolReserveCoins: 0,
                settlementWindowDraws: 1000,
                targetRTPPercent: 90,
                tiers: generateWheelTierProbabilities({
                    drawPriceCoins: 100,
                    targetRTPPercent: 90,
                    tiers: [
                        ...tiers,
                        tier({
                            rewardCoins: 100,
                            rewardId: "",
                            rewardType: "coin",
                            rtpValueCoins: 100,
                            tierId: "classic-9",
                        }),
                    ],
                }),
                wheelId: "classic",
            }).success,
        ).toBe(false);
    });

    test("uses the shared RTP formula to correct reachable wheel probabilities", () => {
        const result = generateWheelTierProbabilities({
            drawPriceCoins: 10000,
            targetRTPPercent: 90,
            tiers: [
                tier({ rewardCoins: 1000, rewardId: "", rewardType: "coin", rtpValueCoins: 1000, tierId: "classic-1" }),
                tier({ rewardId: "200", rewardType: "gift", rtpValueCoins: 20000, tierId: "classic-2" }),
                tier({ rewardId: "300", rewardType: "dress", rtpValueCoins: 0, tierId: "classic-3" }),
                tier({ rewardId: "400", rewardType: "prop", rtpValueCoins: 0, tierId: "classic-4" }),
            ],
        });

        expect(probabilityPPM(result)).toBe(1_000_000);
        expect(wheelExpectedRTP(result, 10000)).toBeCloseTo(90, 3);
        expect(result[2].probabilityPercent).toBe(0.0001);
        expect(result[3].probabilityPercent).toBe(0.0001);
    });

    test("clamps to the closest reachable RTP when every reward is above the target multiplier", () => {
        const result = generateWheelTierProbabilities({
            drawPriceCoins: 10000,
            targetRTPPercent: 90,
            tiers: Array.from({ length: 12 }, (_, index) =>
                tier({
                    rewardId: String(index + 1),
                    rewardType: "gift",
                    rtpValueCoins: 50000 * (index + 1),
                    tierId: `advanced-${index + 1}`,
                }),
            ),
        });

        expect(probabilityPPM(result)).toBe(1_000_000);
        expect(wheelExpectedRTP(result, 10000)).toBeGreaterThan(90);
        expect(result.every((item) => Number(item.probabilityPercent) > 0)).toBe(true);
    });

    test("falls back to equal probabilities when the wheel has no RTP prize", () => {
        const result = generateWheelTierProbabilities({
            drawPriceCoins: 10000,
            targetRTPPercent: 90,
            tiers: [
                tier({ rewardId: "300", rewardType: "dress", rtpValueCoins: 0, tierId: "classic-1" }),
                tier({ rewardId: "400", rewardType: "prop", rtpValueCoins: 0, tierId: "classic-2" }),
            ],
        });

        expect(probabilityPPM(result)).toBe(1_000_000);
        expect(result.map((item) => item.probabilityPercent)).toEqual([50, 50]);
    });
});
