import { act, renderHook, waitFor } from "@testing-library/react";
import { afterEach, expect, test, vi } from "vitest";

const mocks = vi.hoisted(() => ({
    batchUpsertLevelConfig: vi.fn(),
    createTier: vi.fn(),
    listLevelConfig: vi.fn(),
    listResourceGroups: vi.fn(),
    listResources: vi.fn(),
    showToast: vi.fn(),
    updateTier: vi.fn(),
    updateTrack: vi.fn(),
    upsertRule: vi.fn(),
}));

vi.mock("@/features/resources/api", () => ({
    listResourceGroups: mocks.listResourceGroups,
    listResources: mocks.listResources,
}));

vi.mock("@/features/level-config/api", () => ({
    batchUpsertLevelConfig: mocks.batchUpsertLevelConfig,
    createTier: mocks.createTier,
    listLevelConfig: mocks.listLevelConfig,
    updateTier: mocks.updateTier,
    updateTrack: mocks.updateTrack,
    upsertRule: mocks.upsertRule,
}));

vi.mock("@/features/level-config/permissions.js", () => ({
    useLevelConfigAbilities: () => ({ canUpdate: true, canView: true }),
}));

vi.mock("@/shared/ui/ToastProvider.jsx", () => ({
    useToast: () => ({ showToast: mocks.showToast }),
}));

import { useLevelConfigPage } from "./useLevelConfigPage.js";

afterEach(() => {
    vi.clearAllMocks();
});

test("loads level badges through backend resource type filtering", async () => {
    const badgeResource = {
        badgeForm: "strip",
        badgeKind: "level",
        levelTrack: "wealth",
        name: "财富长徽章",
        resourceId: 397,
        resourceType: "badge",
    };
    const avatarFrameResource = {
        name: "等级头像框",
        resourceId: 812,
        resourceType: "avatar_frame",
    };

    mocks.listLevelConfig.mockResolvedValue({
        serverTimeMs: 0,
        tracks: [{ rules: [], tiers: [], track: "wealth" }],
    });
    mocks.listResources.mockImplementation(async (query) => {
        if (query.resource_type === "badge") {
            return { items: [badgeResource], page: 1, pageSize: 500, total: 1 };
        }
        if (query.resource_type === "avatar_frame") {
            return { items: [avatarFrameResource], page: 1, pageSize: 500, total: 1 };
        }
        throw new Error(`unexpected resource query ${JSON.stringify(query)}`);
    });
    mocks.listResourceGroups.mockResolvedValue({ items: [], page: 1, pageSize: 500, total: 0 });

    const { result } = renderHook(() => useLevelConfigPage());

    await waitFor(() => expect(result.current.loading).toBe(false));

    expect(mocks.listResources).toHaveBeenCalledTimes(2);
    expect(mocks.listResources).toHaveBeenCalledWith(
        expect.objectContaining({ page: 1, page_size: 500, resource_type: "badge", status: "active" }),
    );
    expect(mocks.listResources).toHaveBeenCalledWith(
        expect.objectContaining({ page: 1, page_size: 500, resource_type: "avatar_frame", status: "active" }),
    );
    expect(result.current.badgeResources).toEqual([badgeResource]);
    expect(result.current.avatarFrameResources).toEqual([avatarFrameResource]);
});

test("submits changed rules through one batch request without returning to page loading", async () => {
    const levelTrack = {
        rules: [
            { level: 0, name: "魅力等级 0级", requiredValue: 0, sortOrder: 0, status: "active", track: "charm" },
            { level: 1, name: "魅力等级 1级", requiredValue: 500, sortOrder: 1, status: "active", track: "charm" },
        ],
        tiers: [],
        track: "charm",
    };
    mocks.listLevelConfig.mockResolvedValue({ serverTimeMs: 0, tracks: [levelTrack] });
    mocks.listResources.mockResolvedValue({ items: [], page: 1, pageSize: 500, total: 0 });
    mocks.listResourceGroups.mockResolvedValue({ items: [], page: 1, pageSize: 500, total: 0 });
    mocks.batchUpsertLevelConfig.mockResolvedValue({ rules: [], tiers: [] });

    const { result } = renderHook(() => useLevelConfigPage());
    await waitFor(() => expect(result.current.loading).toBe(false));

    act(() => result.current.openRuleBatchEdit(levelTrack));
    act(() => result.current.updateBatchForm(1, "requiredValue", "800"));
    await act(async () => result.current.submitRuleBatch({ preventDefault: vi.fn() }));

    expect(mocks.batchUpsertLevelConfig).toHaveBeenCalledTimes(1);
    expect(mocks.batchUpsertLevelConfig).toHaveBeenCalledWith({
        rules: [expect.objectContaining({ level: 1, required_value: 800, track: "charm" })],
        tiers: [],
    });
    expect(mocks.listLevelConfig).toHaveBeenCalledTimes(2);
    expect(result.current.loading).toBe(false);
});
