// useMyChildren Hook 单测
// 依据：02-architecture-design.md §4.2 GraphQL 接入、§13 测试策略
// 覆盖：加载态 / 成功返回子女列表 / 错误态 / 同步 Zustand store

import {
  describe,
  it,
  expect,
  beforeAll,
  afterAll,
  afterEach,
  beforeEach,
  vi,
} from "vitest";
import { renderHook, waitFor } from "@testing-library/react";
import {
  Provider as UrqlProvider,
  Client,
  cacheExchange,
  fetchExchange,
} from "urql";
import { graphql, HttpResponse } from "msw";
import type { ReactNode } from "react";
import { server } from "@/test/mocks/server";
import { useChildStore } from "@/store/child-store";
import { useMyChildren } from "./useMyChildren";

// 端点：/api/v1/parent/v1/graphql（双 /v1 前缀，ARB-022 §24.4）
function createTestClient(): Client {
  return new Client({
    url: "/api/v1/parent/v1/graphql",
    exchanges: [cacheExchange, fetchExchange],
    fetchOptions: { headers: { "X-Requested-With": "XMLHttpRequest" } },
  });
}

function renderUseMyChildren() {
  const client = createTestClient();
  const wrapper = ({ children }: { children: ReactNode }) => (
    <UrqlProvider value={client}>{children}</UrqlProvider>
  );
  return renderHook(() => useMyChildren(), { wrapper });
}

beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

beforeEach(() => {
  localStorage.clear();
  useChildStore.setState({
    children: [],
    currentChildId: null,
    isLoading: false,
  });
});

describe("useMyChildren", () => {
  it("初始加载态返回空列表和 loading=true", () => {
    const { result } = renderUseMyChildren();
    expect(result.current.children).toEqual([]);
    expect(result.current.loading).toBe(true);
    expect(result.current.error).toBeUndefined();
  });

  it("加载成功后返回子女列表", async () => {
    const { result } = renderUseMyChildren();
    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });
    expect(result.current.children).toHaveLength(2);
    expect(result.current.children[0]!.id).toBe("student-001");
    expect(result.current.children[1]!.id).toBe("student-002");
    expect(result.current.error).toBeUndefined();
  });

  it("加载成功后同步到 Zustand store（setChildren + setLoading）", async () => {
    const { result } = renderUseMyChildren();
    await waitFor(() => {
      expect(result.current.children).toHaveLength(2);
    });
    // store 已同步 children
    expect(useChildStore.getState().children).toHaveLength(2);
    expect(useChildStore.getState().children[0]!.id).toBe("student-001");
    // 默认选中第一个
    expect(useChildStore.getState().currentChildId).toBe("student-001");
    // loading 同步为 false
    expect(useChildStore.getState().isLoading).toBe(false);
  });

  it("加载中同步 isLoading=true 到 store", () => {
    const spy = vi.spyOn(useChildStore.getState(), "setLoading");
    renderUseMyChildren();
    expect(spy).toHaveBeenCalledWith(true);
    spy.mockRestore();
  });

  it("GraphQL 返回错误时透传 error", async () => {
    server.resetHandlers(
      graphql.query("MyChildren", () =>
        HttpResponse.json(
          {
            errors: [{ message: "未授权" }],
          },
          { status: 200 },
        ),
      ),
    );

    const { result } = renderUseMyChildren();
    await waitFor(() => {
      expect(result.current.error).toBeDefined();
    });
    expect(result.current.loading).toBe(false);
    expect(result.current.children).toEqual([]);
    expect(result.current.error?.message).toContain("未授权");
  });

  it("GraphQL 返回空 children 时 store 保持空", async () => {
    server.resetHandlers(
      graphql.query("MyChildren", () =>
        HttpResponse.json({ data: { myChildren: [] } }),
      ),
    );

    const { result } = renderUseMyChildren();
    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });
    expect(result.current.children).toEqual([]);
    expect(useChildStore.getState().children).toEqual([]);
    expect(useChildStore.getState().currentChildId).toBeNull();
  });

  it("网络错误时返回 error 且 children 为空", async () => {
    server.resetHandlers(
      graphql.query("MyChildren", () => HttpResponse.error()),
    );

    const { result } = renderUseMyChildren();
    await waitFor(() => {
      expect(result.current.error).toBeDefined();
    });
    expect(result.current.children).toEqual([]);
  });
});
