// useChildTrend Hook 单测
// 依据：02-architecture-design.md §4.2 GraphQL 接入
// 覆盖：无 currentChildId 暂停 / 加载态 / 成功返回 / 统计计算 / trendDirection / period / 错误态

import {
  describe,
  it,
  expect,
  beforeAll,
  afterAll,
  afterEach,
  beforeEach,
} 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 { useChildStore } from "@/store/child-store";
import { useChildTrend } from "./useChildTrend";
import type { TrendDataPoint } from "@/types";

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

function renderUseChildTrend(period?: "week" | "month" | "semester") {
  const client = createTestClient();
  const wrapper = ({ children }: { children: ReactNode }) => (
    <UrqlProvider value={client}>{children}</UrqlProvider>
  );
  return renderHook(() => useChildTrend(period), { wrapper });
}

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

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

describe("useChildTrend", () => {
  it("无 currentChildId 时暂停查询，返回空数据点和稳定趋势", () => {
    const { result } = renderUseChildTrend();
    expect(result.current.dataPoints).toEqual([]);
    expect(result.current.avgScore).toBe(0);
    expect(result.current.maxScore).toBe(0);
    expect(result.current.minScore).toBe(0);
    expect(result.current.trendDirection).toBe("stable");
    expect(result.current.loading).toBe(false);
    expect(result.current.error).toBeUndefined();
  });

  it("有 currentChildId 时加载成功返回趋势数据点", async () => {
    useChildStore.setState({ currentChildId: "student-001" });
    const { result } = renderUseChildTrend();
    await waitFor(() => {
      expect(result.current.dataPoints.length).toBeGreaterThan(0);
    });
    // mock 数据：82, 88, 92, 90
    expect(result.current.dataPoints).toHaveLength(4);
    expect(result.current.loading).toBe(false);
    expect(result.current.error).toBeUndefined();
  });

  it("统计计算：avgScore / maxScore / minScore", async () => {
    useChildStore.setState({ currentChildId: "student-001" });
    const { result } = renderUseChildTrend();
    await waitFor(() => {
      expect(result.current.dataPoints.length).toBeGreaterThan(0);
    });
    // scores = [82, 88, 92, 90]
    expect(result.current.maxScore).toBe(92);
    expect(result.current.minScore).toBe(82);
    // avg = (82+88+92+90)/4 = 88
    expect(result.current.avgScore).toBe(88);
  });

  it("trendDirection 下降：last < prev - 1", async () => {
    useChildStore.setState({ currentChildId: "student-001" });
    // mock 数据最后两个 92 → 90，90 < 92-1=91 → down
    const { result } = renderUseChildTrend();
    await waitFor(() => {
      expect(result.current.dataPoints.length).toBeGreaterThan(0);
    });
    expect(result.current.trendDirection).toBe("down");
  });

  it("trendDirection 上升：last > prev + 1", async () => {
    useChildStore.setState({ currentChildId: "student-001" });
    const upPoints: TrendDataPoint[] = [
      { date: "2026-03", score: 80, subject: "数学" },
      { date: "2026-04", score: 90, subject: "数学" },
    ];
    server.resetHandlers(
      graphql.query("ChildTrend", () =>
        HttpResponse.json({
          data: {
            childTrend: {
              childId: "student-001",
              period: "month",
              dataPoints: upPoints,
            },
          },
        }),
      ),
    );

    const { result } = renderUseChildTrend();
    await waitFor(() => {
      expect(result.current.dataPoints).toHaveLength(2);
    });
    expect(result.current.trendDirection).toBe("up");
  });

  it("trendDirection 稳定：|last - prev| <= 1", async () => {
    useChildStore.setState({ currentChildId: "student-001" });
    const stablePoints: TrendDataPoint[] = [
      { date: "2026-03", score: 85, subject: "数学" },
      { date: "2026-04", score: 85, subject: "数学" },
    ];
    server.resetHandlers(
      graphql.query("ChildTrend", () =>
        HttpResponse.json({
          data: {
            childTrend: {
              childId: "student-001",
              period: "month",
              dataPoints: stablePoints,
            },
          },
        }),
      ),
    );

    const { result } = renderUseChildTrend();
    await waitFor(() => {
      expect(result.current.dataPoints).toHaveLength(2);
    });
    expect(result.current.trendDirection).toBe("stable");
  });

  it("trendDirection 单点时为 stable", async () => {
    useChildStore.setState({ currentChildId: "student-001" });
    server.resetHandlers(
      graphql.query("ChildTrend", () =>
        HttpResponse.json({
          data: {
            childTrend: {
              childId: "student-001",
              period: "month",
              dataPoints: [{ date: "2026-03", score: 85 }],
            },
          },
        }),
      ),
    );

    const { result } = renderUseChildTrend();
    await waitFor(() => {
      expect(result.current.dataPoints).toHaveLength(1);
    });
    expect(result.current.trendDirection).toBe("stable");
    expect(result.current.avgScore).toBe(85);
  });

  it("period 默认为 month", async () => {
    useChildStore.setState({ currentChildId: "student-001" });
    const { result } = renderUseChildTrend();
    await waitFor(() => {
      expect(result.current.dataPoints.length).toBeGreaterThan(0);
    });
    expect(result.current.period).toBe("month");
  });

  it("period 可指定为 week", async () => {
    useChildStore.setState({ currentChildId: "student-001" });
    let capturedVars: unknown = null;
    server.resetHandlers(
      graphql.query("ChildTrend", ({ variables }) => {
        capturedVars = variables;
        return HttpResponse.json({
          data: {
            childTrend: {
              childId: "student-001",
              period: "week",
              dataPoints: [],
            },
          },
        });
      }),
    );

    const { result } = renderUseChildTrend("week");
    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });
    expect(result.current.period).toBe("week");
    expect(capturedVars).toEqual({ childId: "student-001", period: "week" });
  });

  it("GraphQL 返回错误时透传 error", async () => {
    useChildStore.setState({ currentChildId: "student-001" });
    server.resetHandlers(
      graphql.query("ChildTrend", () =>
        HttpResponse.json(
          { errors: [{ message: "趋势查询失败" }] },
          { status: 200 },
        ),
      ),
    );

    const { result } = renderUseChildTrend();
    await waitFor(() => {
      expect(result.current.error).toBeDefined();
    });
    expect(result.current.dataPoints).toEqual([]);
    expect(result.current.error?.message).toContain("趋势查询失败");
  });

  it("数据为空时统计为 0 且趋势稳定", async () => {
    useChildStore.setState({ currentChildId: "student-001" });
    server.resetHandlers(
      graphql.query("ChildTrend", () =>
        HttpResponse.json({
          data: {
            childTrend: {
              childId: "student-001",
              period: "month",
              dataPoints: [],
            },
          },
        }),
      ),
    );

    const { result } = renderUseChildTrend();
    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });
    expect(result.current.dataPoints).toEqual([]);
    expect(result.current.avgScore).toBe(0);
    expect(result.current.maxScore).toBe(0);
    expect(result.current.minScore).toBe(0);
    expect(result.current.trendDirection).toBe("stable");
  });
});
