// useReportCard Hook 单测
// 依据：02-architecture-design.md §4.2 GraphQL 接入
// 覆盖：无 childId/academicYearId 暂停 / 加载态 / 成功返回 / 变量传递 / 错误态

import {
  describe,
  it,
  expect,
  beforeAll,
  afterAll,
  afterEach,
} from "vitest";
import { renderHook, waitFor, cleanup } 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 { useReportCard } from "./useReportCard";

function createTestClient(): Client {
  return new Client({
    url: "/api/v1/parent/v1/graphql",
    exchanges: [cacheExchange, fetchExchange],
    fetchOptions: { headers: { "X-Requested-With": "XMLHttpRequest" } },
  });
}

function renderUseReportCard(
  childId: string | null,
  academicYearId: string | null,
  semester: number,
) {
  const client = createTestClient();
  const wrapper = ({ children }: { children: ReactNode }) => (
    <UrqlProvider value={client}>{children}</UrqlProvider>
  );
  return renderHook(
    () => useReportCard(childId, academicYearId, semester),
    { wrapper },
  );
}

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

describe("useReportCard", () => {
  it("无 childId 时暂停查询，返回 null", () => {
    const { result } = renderUseReportCard(null, "ay-2025-2026", 2);
    expect(result.current.reportCard).toBeNull();
    expect(result.current.loading).toBe(false);
    expect(result.current.error).toBeUndefined();
  });

  it("无 academicYearId 时暂停查询，返回 null", () => {
    const { result } = renderUseReportCard("student-001", null, 2);
    expect(result.current.reportCard).toBeNull();
    expect(result.current.loading).toBe(false);
    expect(result.current.error).toBeUndefined();
  });

  it("有 childId 和 academicYearId 时加载成功返回报告卡", async () => {
    const { result } = renderUseReportCard(
      "student-001",
      "ay-2025-2026",
      2,
    );
    await waitFor(() => {
      expect(result.current.reportCard).not.toBeNull();
    });
    expect(result.current.reportCard?.childId).toBe("student-001");
    expect(result.current.reportCard?.semester).toBe(2);
    expect(result.current.reportCard?.subjects).toHaveLength(4);
    expect(result.current.loading).toBe(false);
    expect(result.current.error).toBeUndefined();
  });

  it("加载中 loading 为 true", () => {
    const { result } = renderUseReportCard(
      "student-001",
      "ay-2025-2026",
      1,
    );
    expect(result.current.loading).toBe(true);
  });

  it("传入的变量正确传递", async () => {
    let capturedVars: unknown = null;
    server.resetHandlers(
      graphql.query("ChildReportCard", ({ variables }) => {
        capturedVars = variables;
        return HttpResponse.json({
          data: {
            childReportCard: {
              childId: variables.childId,
              childName: "张小明",
              className: "初一(1)班",
              academicYearId: variables.academicYearId,
              academicYearName: "2025-2026",
              semester: variables.semester,
              subjects: [],
              overallScore: 90,
              classRank: 5,
              classSize: 45,
              teacherComment: "良好",
              issuedAt: "2026-07-05T10:00:00Z",
            },
          },
        });
      }),
    );

    const { result } = renderUseReportCard(
      "student-002",
      "ay-2024-2025",
      1,
    );
    await waitFor(() => {
      expect(result.current.reportCard).not.toBeNull();
    });
    expect(capturedVars).toEqual({
      childId: "student-002",
      academicYearId: "ay-2024-2025",
      semester: 1,
    });
  });

  it("GraphQL 返回错误时透传 error", async () => {
    server.resetHandlers(
      graphql.query("ChildReportCard", () =>
        HttpResponse.json(
          { errors: [{ message: "报告卡查询失败" }] },
          { status: 200 },
        ),
      ),
    );

    const { result } = renderUseReportCard(
      "student-001",
      "ay-2025-2026",
      2,
    );
    await waitFor(() => {
      expect(result.current.error).toBeDefined();
    });
    expect(result.current.reportCard).toBeNull();
    expect(result.current.error?.message).toContain("报告卡查询失败");
  });
});
