// PreferenceForm 组件单测
// 依据：02-architecture-design.md §14 通知偏好数据模型、§15.8 PreferenceForm 设计、ISSUE-033
// 覆盖：加载态 / 子女切换标签 / 偏好矩阵表 / 切换 checkbox 调用 updatePreference / 全局默认

import { describe, it, expect, beforeEach, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { PreferenceForm } from "./PreferenceForm";
import type { ChildInfo, NotificationPreferences } from "@/types";

// mock useNotificationPreferences
const mockUseNotificationPreferences = vi.fn();
vi.mock("@/hooks/useNotificationPreferences", () => ({
  useNotificationPreferences: (parentId: string) =>
    mockUseNotificationPreferences(parentId),
}));

// mock useChildSwitcher
const mockUseChildSwitcher = vi.fn();
vi.mock("@/hooks/useChildSwitcher", () => ({
  useChildSwitcher: () => mockUseChildSwitcher(),
}));

// mock getUser
const mockGetUser = vi.fn();
vi.mock("@/lib/auth", () => ({
  getUser: () => mockGetUser(),
}));

const mockChildren: ChildInfo[] = [
  {
    id: "student-001",
    name: "张小明",
    grade: "grade.7",
    schoolName: "实验中学",
    classId: "class-7-1",
    className: "初一(1)班",
  },
  {
    id: "student-002",
    name: "张小红",
    grade: "grade.5",
    schoolName: "实验小学",
    classId: "class-5-2",
    className: "五年级(2)班",
  },
];

const mockPreferences: NotificationPreferences = {
  parentId: "parent-001",
  preferences: {
    "student-001": {
      grade_recorded: { in_app: true, push: true },
      homework_graded: { in_app: true },
    },
  },
  defaults: {
    grade_recorded: { in_app: true, push: true },
    homework_graded: { in_app: true },
    homework_assigned: { in_app: true },
    exam_published: { in_app: true },
    attendance_alert: { in_app: true },
    school_announcement: { in_app: true },
    teacher_message: { in_app: true },
    fee_reminder: { in_app: true },
    event_invitation: { in_app: true },
  },
  updatedAt: "2026-07-13T00:00:00.000Z",
};

beforeEach(() => {
  mockGetUser.mockReturnValue({ id: "parent-001" });
  mockUseChildSwitcher.mockReset();
  mockUseNotificationPreferences.mockReset();
});

function setupHappyPath() {
  mockUseChildSwitcher.mockReturnValue({
    children: mockChildren,
    currentChild: null,
    // currentChildId=null 使初始 activeChildId="*"（全局默认）
    currentChildId: null,
    switchChild: vi.fn(),
    loading: false,
    error: undefined,
    hasMultipleChildren: true,
  });
  mockUseNotificationPreferences.mockReturnValue({
    preferences: mockPreferences,
    loading: false,
    error: undefined,
    updatePreference: vi.fn(),
  });
}

describe("PreferenceForm 加载态", () => {
  it("loading 时渲染骨架屏", () => {
    mockGetUser.mockReturnValue({ id: "parent-001" });
    mockUseChildSwitcher.mockReturnValue({
      children: [],
      currentChild: null,
      currentChildId: null,
      switchChild: vi.fn(),
      loading: false,
      error: undefined,
      hasMultipleChildren: false,
    });
    mockUseNotificationPreferences.mockReturnValue({
      preferences: null,
      loading: true,
      error: undefined,
      updatePreference: vi.fn(),
    });
    const { container } = render(<PreferenceForm />);
    expect(container.querySelectorAll(".skeleton").length).toBeGreaterThan(0);
  });

  it("preferences 为 null 时渲染骨架屏", () => {
    mockUseChildSwitcher.mockReturnValue({
      children: [],
      currentChild: null,
      currentChildId: null,
      switchChild: vi.fn(),
      loading: false,
      error: undefined,
      hasMultipleChildren: false,
    });
    mockUseNotificationPreferences.mockReturnValue({
      preferences: null,
      loading: false,
      error: undefined,
      updatePreference: vi.fn(),
    });
    const { container } = render(<PreferenceForm />);
    expect(container.querySelectorAll(".skeleton").length).toBeGreaterThan(0);
  });
});

describe("PreferenceForm 子女切换标签", () => {
  beforeEach(setupHappyPath);

  it("渲染全局默认标签", () => {
    render(<PreferenceForm />);
    expect(screen.getByText("全局默认")).toBeInTheDocument();
  });

  it("渲染所有子女标签", () => {
    render(<PreferenceForm />);
    expect(screen.getByText("张小明")).toBeInTheDocument();
    expect(screen.getByText("张小红")).toBeInTheDocument();
  });

  it("点击子女标签切换 activeChildId（矩阵内容变化）", async () => {
    const user = userEvent.setup();
    render(<PreferenceForm />);
    // 初始为全局默认（"*"），scope = preferences.defaults
    // 默认表 homework_assigned 有 in_app: true
    const homeworkAssignedCheckbox = screen.getByLabelText("作业布置 - 应用内");
    expect(homeworkAssignedCheckbox).toBeChecked();

    // 点击张小明标签
    await user.click(screen.getByText("张小明"));
    // 张小明的偏好中没有 homework_assigned，应回退到 defaults
    // 但 scope = preferences["student-001"] ?? defaults
    // student-001 没有 homework_assigned key，pref = {} → checked = false
    expect(homeworkAssignedCheckbox).not.toBeChecked();
  });

  it("点击全局默认标签切换回全局", async () => {
    const user = userEvent.setup();
    render(<PreferenceForm />);
    // 先切到张小明
    await user.click(screen.getByText("张小明"));
    // 再切回全局默认
    await user.click(screen.getByText("全局默认"));
    // 全局默认 homework_assigned.in_app = true
    const checkbox = screen.getByLabelText("作业布置 - 应用内");
    expect(checkbox).toBeChecked();
  });
});

describe("PreferenceForm 偏好矩阵表", () => {
  beforeEach(setupHappyPath);

  it("渲染表头渠道列（5 种渠道）", () => {
    render(<PreferenceForm />);
    expect(screen.getByText("应用内")).toBeInTheDocument();
    expect(screen.getByText("推送")).toBeInTheDocument();
    expect(screen.getByText("短信")).toBeInTheDocument();
    expect(screen.getByText("邮件")).toBeInTheDocument();
    expect(screen.getByText("微信")).toBeInTheDocument();
  });

  it("渲染表头事件列（9 种事件）", () => {
    render(<PreferenceForm />);
    const events = [
      "成绩发布",
      "作业评分",
      "作业布置",
      "考试发布",
      "考勤提醒",
      "学校公告",
      "老师消息",
      "缴费提醒",
      "活动邀请",
    ];
    for (const e of events) {
      expect(screen.getByText(e)).toBeInTheDocument();
    }
  });

  it("矩阵包含 9 行 × 5 列 = 45 个 checkbox", () => {
    render(<PreferenceForm />);
    const checkboxes = screen.getAllByRole("checkbox");
    expect(checkboxes).toHaveLength(45);
  });

  it("已启用的偏好 checkbox 为 checked", () => {
    render(<PreferenceForm />);
    // defaults.grade_recorded: { in_app: true, push: true }
    expect(screen.getByLabelText("成绩发布 - 应用内")).toBeChecked();
    expect(screen.getByLabelText("成绩发布 - 推送")).toBeChecked();
  });

  it("未设置的偏好 checkbox 为 unchecked", () => {
    render(<PreferenceForm />);
    // defaults.grade_recorded 没有 sms
    expect(screen.getByLabelText("成绩发布 - 短信")).not.toBeChecked();
  });

  it("渲染自动保存提示文案", () => {
    render(<PreferenceForm />);
    expect(screen.getByText(/偏好设置自动保存到本地/)).toBeInTheDocument();
  });
});

describe("PreferenceForm checkbox 切换", () => {
  it("点击 checkbox 调用 updatePreference", async () => {
    const user = userEvent.setup();
    const updatePreference = vi.fn();
    mockUseChildSwitcher.mockReturnValue({
      children: mockChildren,
      currentChild: null,
      // currentChildId=null → 初始 activeChildId="*"（全局默认）
      currentChildId: null,
      switchChild: vi.fn(),
      loading: false,
      error: undefined,
      hasMultipleChildren: true,
    });
    mockUseNotificationPreferences.mockReturnValue({
      preferences: mockPreferences,
      loading: false,
      error: undefined,
      updatePreference,
    });

    render(<PreferenceForm />);

    // 在全局默认下点击 "成绩发布 - 短信"（当前未选中）
    const checkbox = screen.getByLabelText("成绩发布 - 短信");
    await user.click(checkbox);

    expect(updatePreference).toHaveBeenCalledWith(
      "*",
      "grade_recorded",
      "sms",
      true,
    );
  });

  it("切换到子女标签后点击 checkbox 使用子女 ID", async () => {
    const user = userEvent.setup();
    const updatePreference = vi.fn();
    mockUseChildSwitcher.mockReturnValue({
      children: mockChildren,
      currentChild: null,
      currentChildId: null,
      switchChild: vi.fn(),
      loading: false,
      error: undefined,
      hasMultipleChildren: true,
    });
    mockUseNotificationPreferences.mockReturnValue({
      preferences: mockPreferences,
      loading: false,
      error: undefined,
      updatePreference,
    });

    render(<PreferenceForm />);

    // 切换到张小红
    await user.click(screen.getByText("张小红"));
    // 点击 "考试发布 - 推送"
    const checkbox = screen.getByLabelText("考试发布 - 推送");
    await user.click(checkbox);

    expect(updatePreference).toHaveBeenCalledWith(
      "student-002",
      "exam_published",
      "push",
      true,
    );
  });

  it("取消已选中的 checkbox 传 enabled=false", async () => {
    const user = userEvent.setup();
    const updatePreference = vi.fn();
    mockUseChildSwitcher.mockReturnValue({
      children: mockChildren,
      currentChild: null,
      currentChildId: null,
      switchChild: vi.fn(),
      loading: false,
      error: undefined,
      hasMultipleChildren: true,
    });
    mockUseNotificationPreferences.mockReturnValue({
      preferences: mockPreferences,
      loading: false,
      error: undefined,
      updatePreference,
    });

    render(<PreferenceForm />);

    // defaults.grade_recorded.in_app = true，取消选中
    const checkbox = screen.getByLabelText("成绩发布 - 应用内");
    expect(checkbox).toBeChecked();
    await user.click(checkbox);

    expect(updatePreference).toHaveBeenCalledWith(
      "*",
      "grade_recorded",
      "in_app",
      false,
    );
  });
});

describe("PreferenceForm getUser fallback", () => {
  it("getUser 返回 null 时 parentId 回退到 parent-001", () => {
    mockGetUser.mockReturnValue(null);
    mockUseChildSwitcher.mockReturnValue({
      children: [],
      currentChild: null,
      currentChildId: null,
      switchChild: vi.fn(),
      loading: false,
      error: undefined,
      hasMultipleChildren: false,
    });
    mockUseNotificationPreferences.mockReturnValue({
      preferences: mockPreferences,
      loading: false,
      error: undefined,
      updatePreference: vi.fn(),
    });

    render(<PreferenceForm />);
    // 验证 useNotificationPreferences 被调用时 parentId = "parent-001"
    expect(mockUseNotificationPreferences).toHaveBeenCalledWith("parent-001");
  });
});

describe("PreferenceForm 子女偏好回退到默认", () => {
  it("子女无偏好时回退到 defaults", () => {
    mockUseChildSwitcher.mockReturnValue({
      children: mockChildren,
      currentChild: mockChildren[1]!,
      currentChildId: "student-002",
      switchChild: vi.fn(),
      loading: false,
      error: undefined,
      hasMultipleChildren: true,
    });
    mockUseNotificationPreferences.mockReturnValue({
      preferences: {
        ...mockPreferences,
        // student-002 无偏好，应回退到 defaults
        preferences: {},
      },
      loading: false,
      error: undefined,
      updatePreference: vi.fn(),
    });

    render(<PreferenceForm />);
    // initial activeChildId = currentChildId = "student-002"
    // scope = preferences["student-002"] ?? defaults = defaults
    // defaults.grade_recorded.in_app = true
    expect(screen.getByLabelText("成绩发布 - 应用内")).toBeChecked();
  });
});
