import { afterEach, expect, test, vi } from "vitest";
import { setAccessToken, setSelectedAppCode } from "@/shared/api/request";
import {
    createAppVersion,
    createBanner,
    createExploreTab,
    createH5Link,
    createPopup,
    createSplashScreen,
    createSystemMessagePushFanout,
    deleteAppVersion,
    deleteBanner,
    deleteExploreTab,
    deleteH5Link,
    deletePopup,
    deleteSplashScreen,
    listAppVersions,
    listBanners,
    listExploreTabs,
    listH5Links,
    listPopups,
    listSplashScreens,
    updateAppVersion,
    updateBanner,
    updateExploreTab,
    updateH5Link,
    updateH5Links,
    updatePopup,
    updateSplashScreen,
} from "./api";

afterEach(() => {
    setAccessToken("");
    setSelectedAppCode("lalu");
    vi.unstubAllGlobals();
});

test("H5 config API pins every request to its explicit app scope", async () => {
    setSelectedAppCode("lalu");
    const link = {
        appCode: "huwaa",
        key: "achievement",
        label: "成就",
        updatedAtMs: 1000,
        url: "https://h5.example.com/achievement",
    };
    vi.stubGlobal(
        "fetch",
        vi.fn(async (_url, init) => {
            const method = String(init?.method || "GET");
            const data = method === "GET" || (method === "PUT" && !String(_url).endsWith("/achievement"))
                ? { items: [link], total: 1 }
                : method === "DELETE"
                  ? { deleted: true }
                  : link;
            return new Response(JSON.stringify({ code: 0, data }), { status: method === "POST" ? 201 : 200 });
        }),
    );

    const payload = { key: link.key, label: link.label, url: link.url };
    await listH5Links(" Huwaa ");
    await createH5Link("huwaa", payload);
    await updateH5Links("huwaa", { items: [payload] });
    await updateH5Link("huwaa", link.key, payload);
    await deleteH5Link("huwaa", link.key);

    const calls = vi.mocked(fetch).mock.calls;
    expect(calls.map(([, init]) => init?.method)).toEqual(["GET", "POST", "PUT", "PUT", "DELETE"]);
    calls.forEach(([, init]) => {
        expect(init?.headers).toMatchObject({ "X-App-Code": "huwaa" });
    });
    expect(String(calls[0][0])).toContain("/api/v1/admin/app-config/h5-links");
    expect(String(calls[3][0])).toContain("/api/v1/admin/app-config/h5-links/achievement");
    expect(JSON.parse(String(calls[1][1]?.body))).toEqual(payload);
    expect(JSON.parse(String(calls[2][1]?.body))).toEqual({ items: [payload] });
    expect(JSON.parse(String(calls[3][1]?.body))).toEqual(payload);
});

test("every APP config API pins reads and writes to the explicit app scope", async () => {
    setSelectedAppCode("lalu");
    vi.stubGlobal(
        "fetch",
        vi.fn(async (_url, init) => {
            const method = String(init?.method || "GET");
            const data = method === "GET" ? { items: [], total: 0 } : method === "DELETE" ? { deleted: true } : {};
            return new Response(JSON.stringify({ code: 0, data }), { status: method === "POST" ? 201 : 200 });
        }),
    );

    const appCode = " Huwaa ";
    const banner = { bannerType: "h5", coverUrl: "https://cdn.example.com/banner.png", platform: "android", status: "active" } as const;
    const splash = { coverUrl: "https://cdn.example.com/splash.png", platform: "android", splashType: "h5", status: "active" } as const;
    const popup = { code: "welcome", jumpType: "h5", jumpUrl: "https://h5.example.com/welcome", name: "Welcome", status: "active" } as const;
    const explore = { enabled: true, h5Url: "https://h5.example.com/explore", tab: "Explore" };
    const version = { buildNumber: 10, downloadUrl: "https://cdn.example.com/app.apk", forceUpdate: false, platform: "android", version: "1.0.0" } as const;

    await listBanners(appCode);
    await createBanner(appCode, banner);
    await updateBanner(appCode, 1, banner);
    await deleteBanner(appCode, 1);
    await listSplashScreens(appCode);
    await createSplashScreen(appCode, splash);
    await updateSplashScreen(appCode, 2, splash);
    await deleteSplashScreen(appCode, 2);
    await listPopups(appCode);
    await createPopup(appCode, popup);
    await updatePopup(appCode, 3, popup);
    await deletePopup(appCode, 3);
    await listExploreTabs(appCode);
    await createExploreTab(appCode, explore);
    await updateExploreTab(appCode, 4, explore);
    await deleteExploreTab(appCode, 4);
    await listAppVersions(appCode);
    await createAppVersion(appCode, version);
    await updateAppVersion(appCode, 5, version);
    await deleteAppVersion(appCode, 5);

    const calls = vi.mocked(fetch).mock.calls;
    expect(calls).toHaveLength(20);
    calls.forEach(([, init]) => {
        expect(init?.headers).toMatchObject({ "X-App-Code": "huwaa" });
    });
});

test("system message push fanout API keeps existing operations path", async () => {
    vi.stubGlobal(
        "fetch",
        vi.fn(
            async () =>
                new Response(
                    JSON.stringify({
                        code: 0,
                        data: {
                            command_id: "admin_notice_test",
                            created: true,
                            job_id: "mfan_test",
                            message_type: "system",
                            status: "pending",
                            target_scope: "all_registered_users",
                        },
                    }),
                ),
        ),
    );

    await createSystemMessagePushFanout({
        action_param: "https://example.com",
        action_type: "app_h5",
        batch_size: 500,
        message_type: "system",
        producer_event_type: "admin_full_server_notice",
        summary: "今晚 23:00 维护",
        target_scope: "all_registered_users",
        title: "维护通知",
    });

    const [url, init] = vi.mocked(fetch).mock.calls[0];

    expect(String(url)).toContain("/api/v1/admin/operations/full-server-notices/fanout");
    expect(init?.method).toBe("POST");
    expect(JSON.parse(String(init?.body))).toMatchObject({
        action_param: "https://example.com",
        action_type: "app_h5",
        message_type: "system",
        producer_event_type: "admin_full_server_notice",
        target_scope: "all_registered_users",
        title: "维护通知",
    });
});

test("system message push fanout API preserves string user IDs", async () => {
    vi.stubGlobal(
        "fetch",
        vi.fn(
            async () =>
                new Response(
                    JSON.stringify({
                        code: 0,
                        data: {
                            command_id: "admin_notice_users",
                            created: true,
                            job_id: "mfan_users",
                            message_type: "system",
                            status: "pending",
                            target_scope: "user_ids",
                        },
                    }),
                ),
        ),
    );

    await createSystemMessagePushFanout({
        message_type: "system",
        summary: "指定用户维护",
        target_scope: "user_ids",
        title: "维护通知",
        user_ids: ["325379237278126080", "42"],
    });

    const [, init] = vi.mocked(fetch).mock.calls[0];

    expect(JSON.parse(String(init?.body))).toMatchObject({
        target_scope: "user_ids",
        user_ids: ["325379237278126080", "42"],
    });
});
