import { expect, test, vi } from "vitest";
import {
    applyGiftPriceDefaults,
    applyGiftResourceSelection,
    buildResourceShopDraftGroup,
    buildGiftResourceDrafts,
    canUnselectResourceShopDraftGroup,
    canUnselectResourceShopDraftSpec,
    cpRelationTypeFromGift,
    cpRelationTypeFromPresentationJson,
    cpRelationTypeShortLabel,
    fetchAllOptionPages,
    groupResourceShopItems,
    normalizeGiftPresentationJson,
} from "./useResourcePages.js";

test("fills gift name and id from selected gift resource", () => {
    const form = {
        coinPrice: "10",
        giftId: "",
        name: "",
        resourceId: "",
    };

    const nextForm = applyGiftResourceSelection(form, {
        coinPrice: 10,
        name: "Rose",
        priceType: "coin",
        resourceCode: "gift_rose",
        resourceId: 11,
    });

    expect(nextForm).toMatchObject({
        coinPrice: "10",
        giftId: "11",
        name: "Rose",
        resourceId: "11",
    });
});

test("fills gift price without gift points", () => {
    const form = {
        coinPrice: "",
        name: "Rose",
    };

    expect(applyGiftPriceDefaults(form, "100")).toMatchObject({
        coinPrice: "100",
    });
    expect(applyGiftPriceDefaults(form, "101")).toMatchObject({
        coinPrice: "101",
    });
});

test("builds gift drafts from multiple selected resources and preserves edits", () => {
    const form = {
        chargeAssetType: "COIN",
        cpRelationType: "",
        effectTypes: ["animation"],
        effectiveFrom: "",
        effectiveTo: "",
        enabled: true,
        giftTypeCode: "normal",
        items: [
            {
                chargeAssetType: "COIN",
                coinPrice: "88",
                effectTypes: [],
                effectiveFrom: "",
                effectiveTo: "",
                enabled: false,
                giftId: "custom_rose",
                giftTypeCode: "normal",
                name: "Custom Rose",
                presentationJson: "{}",
                priceVersion: "default",
                regionIds: ["0"],
                resourceId: "11",
                sortOrder: "7",
            },
        ],
        presentationJson: "{}",
        priceVersion: "default",
        regionIds: ["0", "1001"],
        sortOrder: "3",
    };

    const drafts = buildGiftResourceDrafts(form, ["11", "12"], [
        {
            coinPrice: 10,
            name: "Rose",
            priceType: "coin",
            resourceCode: "gift_rose",
            resourceId: 11,
        },
        {
            coinPrice: 20,
            name: "Ring",
            priceType: "coin",
            resourceCode: "gift_ring",
            resourceId: 12,
        },
    ]);

    expect(drafts).toHaveLength(2);
    expect(drafts[0]).toMatchObject({
        coinPrice: "88",
        giftId: "custom_rose",
        name: "Custom Rose",
        resourceId: "11",
        sortOrder: "7",
    });
    expect(drafts[1]).toMatchObject({
        coinPrice: "20",
        effectTypes: ["animation"],
        giftId: "12",
        giftTypeCode: "normal",
        name: "Ring",
        regionIds: ["0", "1001"],
        resourceId: "12",
        sortOrder: "3",
    });
});

test("fetches all option pages instead of only the first resource page", async () => {
    const fetcher = vi
        .fn()
        .mockResolvedValueOnce({ items: [{ resourceId: 1 }, { resourceId: 2 }], pageSize: 2, total: 5 })
        .mockResolvedValueOnce({ items: [{ resourceId: 3 }, { resourceId: 4 }], pageSize: 2, total: 5 })
        .mockResolvedValueOnce({ items: [{ resourceId: 5 }], pageSize: 2, total: 5 });

    const result = await fetchAllOptionPages(fetcher, { status: "active" }, 2);

    expect(fetcher).toHaveBeenNthCalledWith(1, { page: 1, page_size: 2, status: "active" });
    expect(fetcher).toHaveBeenNthCalledWith(2, { page: 2, page_size: 2, status: "active" });
    expect(fetcher).toHaveBeenNthCalledWith(3, { page: 3, page_size: 2, status: "active" });
    expect(result.items.map((item) => item.resourceId)).toEqual([1, 2, 3, 4, 5]);
    expect(result.total).toBe(5);
});

test("groups every shop duration instead of overwriting the same resource", () => {
    const itemsByResourceId = groupResourceShopItems([
        { durationDays: 7, resourceId: 11, shopItemId: 3 },
        { durationDays: 1, resourceId: 11, shopItemId: 1 },
        { durationDays: 3, resourceId: 11, shopItemId: 2 },
    ]);

    expect(itemsByResourceId.get(11)?.map((item) => item.shopItemId)).toEqual([1, 2, 3]);
});

test("builds independent shop specs and defaults a new spec to the resource price", () => {
    const resource = { coinPrice: 100, resourceId: 11 };
    const existingGroup = buildResourceShopDraftGroup(resource, [
        { coinPrice: 260, durationDays: 7, resourceId: 11, shopItemId: 3 },
        { coinPrice: 120, durationDays: 3, resourceId: 11, shopItemId: 2 },
    ]);
    const newGroup = buildResourceShopDraftGroup(resource);

    expect(existingGroup.specsByDuration["3"]).toMatchObject({
        coinPrice: "120",
        durationDays: "3",
        shopItemId: 2,
    });
    expect(existingGroup.specsByDuration["7"]).toMatchObject({
        coinPrice: "260",
        durationDays: "7",
        shopItemId: 3,
    });
    expect(newGroup.specsByDuration["1"]).toMatchObject({
        coinPrice: "100",
        durationDays: "1",
        resourceId: "11",
    });
    expect(canUnselectResourceShopDraftSpec(existingGroup.specsByDuration["3"])).toBe(false);
    expect(canUnselectResourceShopDraftSpec(newGroup.specsByDuration["1"])).toBe(true);
    expect(canUnselectResourceShopDraftGroup(existingGroup)).toBe(false);
    expect(canUnselectResourceShopDraftGroup(newGroup)).toBe(true);
});

test("reads CP relation type from gift presentation json", () => {
    expect(cpRelationTypeFromPresentationJson('{"cp_relation_type":"brother"}')).toBe("brother");
    expect(cpRelationTypeFromPresentationJson('{"cpRelationType":"sister"}')).toBe("sister");
    expect(cpRelationTypeFromPresentationJson('{"cp_relation_type":"unknown"}')).toBe("");
});

test("prefers explicit CP relation type and formats compact list label", () => {
    expect(
        cpRelationTypeFromGift({
            cpRelationType: "brother",
            giftTypeCode: "cp",
            presentationJson: '{"cp_relation_type":"sister"}',
        }),
    ).toBe("brother");
    expect(
        cpRelationTypeFromGift({
            giftTypeCode: "cp",
            presentationJson: '{"cp_relation_type":"sister"}',
        }),
    ).toBe("sister");
    expect(cpRelationTypeShortLabel("brother")).toBe("兄弟");
    expect(cpRelationTypeShortLabel("cp")).toBe("CP");
});

test("normalizes CP gift presentation json for submit", () => {
    const nextJson = normalizeGiftPresentationJson(
        '{"other":1,"cp_relation_type":"brother","cpRelationLabel":"old"}',
        "cp",
        "sister",
    );

    expect(JSON.parse(nextJson)).toEqual({
        cp_relation_label: "姐妹礼物",
        cp_relation_type: "sister",
        other: 1,
    });
});

test("removes CP relation metadata from non-CP gifts", () => {
    const nextJson = normalizeGiftPresentationJson(
        '{"other":1,"cp_relation_type":"cp","cp_relation_label":"CP礼物"}',
        "normal",
        "",
    );

    expect(JSON.parse(nextJson)).toEqual({ other: 1 });
});
