import { act, renderHook } from "@testing-library/react";
import { afterEach, beforeEach, expect, test, vi } from "vitest";
import { setSelectedAppCode } from "@/shared/api/request";

const mocks = vi.hoisted(() => ({
  appCode: "lalu",
  confirm: vi.fn(),
  createH5Link: vi.fn(),
  deleteH5Link: vi.fn(),
  listH5Links: vi.fn(),
  queryFn: null,
  queryOptions: null,
  reload: vi.fn(),
  showToast: vi.fn(),
  updateH5Link: vi.fn()
}));

vi.mock("@/app/app-scope/AppScopeProvider.jsx", () => ({
  useAppScope: () => ({ appCode: mocks.appCode })
}));

vi.mock("@/features/app-config/api", () => ({
  createH5Link: mocks.createH5Link,
  deleteH5Link: mocks.deleteH5Link,
  listH5Links: mocks.listH5Links,
  updateH5Link: mocks.updateH5Link
}));

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

vi.mock("@/shared/hooks/useAdminQuery.js", () => ({
  useAdminQuery: (queryFn, options) => {
    mocks.queryFn = queryFn;
    mocks.queryOptions = options;
    return {
      data: { items: [], total: 0 },
      error: "",
      loading: false,
      reload: mocks.reload
    };
  }
}));

vi.mock("@/shared/ui/ConfirmProvider.jsx", () => ({
  useConfirm: () => mocks.confirm
}));

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

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

beforeEach(() => {
  mocks.appCode = "lalu";
  mocks.confirm.mockResolvedValue(true);
  mocks.createH5Link.mockResolvedValue({});
  mocks.deleteH5Link.mockResolvedValue({ deleted: true });
  mocks.listH5Links.mockResolvedValue({ items: [], total: 0 });
  mocks.reload.mockResolvedValue({ items: [], total: 0 });
  mocks.updateH5Link.mockResolvedValue({});
  setSelectedAppCode("lalu");
});

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

test("uses appCode in the H5 query key and read request", async () => {
  const { rerender } = renderHook(() => useH5ConfigPage());

  expect(mocks.queryOptions.queryKey).toEqual(["app-config", "h5-links", "lalu"]);
  await mocks.queryFn();
  expect(mocks.listH5Links).toHaveBeenLastCalledWith("lalu");

  mocks.appCode = "huwaa";
  setSelectedAppCode("huwaa");
  rerender();

  expect(mocks.queryOptions.queryKey).toEqual(["app-config", "h5-links", "huwaa"]);
  await mocks.queryFn();
  expect(mocks.listH5Links).toHaveBeenLastCalledWith("huwaa");
});

test("closes an edit dialog when the selected App changes", () => {
  const item = {
    appCode: "lalu",
    key: "achievement",
    label: "成就",
    updatedAtMs: 1000,
    url: "https://h5.example.com/achievement"
  };
  const { result, rerender } = renderHook(() => useH5ConfigPage());

  act(() => result.current.openEdit(item));
  expect(result.current.activeAction).toBe("edit");

  mocks.appCode = "huwaa";
  setSelectedAppCode("huwaa");
  rerender();

  expect(result.current.activeAction).toBe("");
  expect(result.current.editingItem).toBeNull();
  expect(result.current.form).toEqual({ key: "", label: "", url: "" });
});

test("does not submit stale form state after an App switch", async () => {
  const { result, rerender } = renderHook(() => useH5ConfigPage());

  act(() => {
    result.current.openEdit({
      appCode: "lalu",
      key: "achievement",
      label: "成就",
      updatedAtMs: 1000,
      url: "https://h5.example.com/achievement"
    });
  });

  mocks.appCode = "huwaa";
  setSelectedAppCode("huwaa");
  rerender();
  await act(async () => {
    await result.current.submitEdit({ preventDefault: vi.fn() });
  });

  expect(mocks.createH5Link).not.toHaveBeenCalled();
  expect(mocks.updateH5Link).not.toHaveBeenCalled();
  expect(mocks.showToast).toHaveBeenCalledWith("应用已切换，请重新操作", "warning");
});

test("pins an update mutation to the App that owns the edited row", async () => {
  const { result } = renderHook(() => useH5ConfigPage());

  act(() => {
    result.current.openEdit({
      appCode: "lalu",
      key: "achievement",
      label: "成就",
      updatedAtMs: 1000,
      url: "https://h5.example.com/achievement"
    });
  });
  await act(async () => {
    await result.current.submitEdit({ preventDefault: vi.fn() });
  });

  expect(mocks.updateH5Link).toHaveBeenCalledWith("lalu", "achievement", {
    key: "achievement",
    label: "成就",
    url: "https://h5.example.com/achievement"
  });
});

test("aborts delete when App changes while confirmation is open", async () => {
  let resolveConfirm;
  mocks.confirm.mockReturnValue(new Promise((resolve) => {
    resolveConfirm = resolve;
  }));
  const { result, rerender } = renderHook(() => useH5ConfigPage());
  let pendingDelete;

  act(() => {
    pendingDelete = result.current.removeH5Link({ key: "achievement", label: "成就" });
  });
  mocks.appCode = "huwaa";
  setSelectedAppCode("huwaa");
  rerender();
  await act(async () => {
    resolveConfirm(true);
    await pendingDelete;
  });

  expect(mocks.deleteH5Link).not.toHaveBeenCalled();
  expect(mocks.showToast).toHaveBeenCalledWith("应用已切换，请重新操作", "warning");
});
