// NotificationFeed 集成测试（MSW + urql）
// 依据：02-architecture-design.md §13 测试策略、§8 MSW 模拟未就绪上游
// 覆盖：GraphQL 查询渲染 / 筛选全部未读 / 标记已读 / 全部已读 mutation

import { describe, it, expect, beforeAll, afterAll, afterEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {
  Provider as UrqlProvider,
  Client,
  cacheExchange,
  fetchExchange,
} from "urql";
import { graphql, HttpResponse } from "msw";
import { NotificationFeed } from "./NotificationFeed";
import { server } from "@/test/mocks/server";

// 创建测试用 urql client（无 auth，MSW 拦截 fetch）
function createTestClient(): Client {
  return new Client({
    url: "/api/v1/parent/graphql",
    exchanges: [cacheExchange, fetchExchange],
    fetchOptions: { headers: { "X-Requested-With": "XMLHttpRequest" } },
  });
}

function renderWithProviders(ui: React.ReactElement) {
  const client = createTestClient();
  return render(<UrqlProvider value={client}>{ui}</UrqlProvider>);
}

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

describe("NotificationFeed 集成测试", () => {
  it("加载完成后渲染通知列表", async () => {
    renderWithProviders(<NotificationFeed />);
    // 等待通知加载完成（mock 返回 3 条）
    await waitFor(() => {
      expect(screen.getByText("成绩发布")).toBeInTheDocument();
    });
    expect(screen.getByText("作业布置")).toBeInTheDocument();
    expect(screen.getByText("学校公告")).toBeInTheDocument();
  });

  it("显示未读数量", async () => {
    renderWithProviders(<NotificationFeed />);
    await waitFor(() => {
      expect(screen.getByText(/未读/)).toBeInTheDocument();
    });
    // mock 有 2 条未读
    expect(screen.getByText(/未读.*2/)).toBeInTheDocument();
  });

  it("切换到未读筛选", async () => {
    const user = userEvent.setup();
    renderWithProviders(<NotificationFeed />);
    await waitFor(() => {
      expect(screen.getByText("成绩发布")).toBeInTheDocument();
    });

    // 点击"未读"按钮（精确匹配，避免匹配通知项的 aria-label="未读"）
    const filterButtons = screen.getAllByRole("button");
    const unreadButton = filterButtons.find(
      (b) =>
        b.textContent?.includes("未读") && !b.textContent.includes("成绩发布"),
    )!;
    await user.click(unreadButton);

    // 未读筛选下仍有 2 条（mock 固定返回，忽略 unreadOnly 参数）
    await waitFor(() => {
      expect(screen.getByText("成绩发布")).toBeInTheDocument();
      expect(screen.getByText("作业布置")).toBeInTheDocument();
    });
  });

  it("点击全部标为已读触发 mutation", async () => {
    const user = userEvent.setup();
    renderWithProviders(<NotificationFeed />);
    await waitFor(() => {
      expect(screen.getByText("成绩发布")).toBeInTheDocument();
    });

    const markAllButton = screen.getByRole("button", { name: "全部标为已读" });
    await user.click(markAllButton);

    // mutation 不应抛错（mock 返回成功）
    // 无需额外断言，只要不报错即通过
  });

  it("空通知列表显示空状态", async () => {
    server.resetHandlers(
      graphql.query("MyNotifications", () =>
        HttpResponse.json({ data: { myNotifications: [] } }),
      ),
    );

    renderWithProviders(<NotificationFeed />);
    await waitFor(() => {
      expect(screen.getByText("暂无通知")).toBeInTheDocument();
    });
  });
});
