// PracticeSessionList 组件单测
// 依据：02-architecture-design.md §15 组件设计
// 覆盖：空态 / 列表渲染 / 学科标签 / 开始时间 / 题数+正确数 / 正确率 / 用时 / 知识点标签

import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { PracticeSessionList } from "./PracticeSessionList";
import type { PracticeSession } from "@/types";

function makeSessions(): PracticeSession[] {
  return [
    {
      id: "ps-001",
      childId: "student-001",
      subject: "数学",
      startedAt: "2026-07-10T19:00:00Z",
      completedAt: "2026-07-10T19:35:00Z",
      questionCount: 20,
      correctCount: 17,
      accuracy: 0.85,
      durationSeconds: 2100,
      knowledgePoints: ["一元二次方程", "配方法"],
    },
    {
      id: "ps-002",
      childId: "student-001",
      subject: "英语",
      startedAt: "2026-07-09T20:10:00Z",
      completedAt: "2026-07-09T20:40:00Z",
      questionCount: 15,
      correctCount: 9,
      accuracy: 0.6,
      durationSeconds: 1800,
      knowledgePoints: ["现在完成时"],
    },
    {
      id: "ps-003",
      childId: "student-001",
      subject: "物理",
      startedAt: "2026-07-08T18:30:00Z",
      completedAt: "2026-07-08T19:05:00Z",
      questionCount: 10,
      correctCount: 4,
      accuracy: 0.4,
      durationSeconds: 75,
      knowledgePoints: ["浮力"],
    },
  ];
}

describe("PracticeSessionList 空态", () => {
  it("空数组渲染暂无练习记录", () => {
    render(<PracticeSessionList sessions={[]} />);
    expect(screen.getByText("暂无练习记录")).toBeInTheDocument();
  });
});

describe("PracticeSessionList 列表渲染", () => {
  it("渲染列表 region（aria-label）", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    expect(screen.getByLabelText("练习历史列表")).toBeInTheDocument();
  });

  it("渲染每条学科标签", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    expect(screen.getByText("数学")).toBeInTheDocument();
    expect(screen.getByText("英语")).toBeInTheDocument();
    expect(screen.getByText("物理")).toBeInTheDocument();
  });

  it("渲染每条开始时间", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    expect(screen.getAllByText(/题数：/)).toHaveLength(3);
  });

  it("渲染每条题数", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    expect(screen.getByText("20")).toBeInTheDocument();
    expect(screen.getByText("15")).toBeInTheDocument();
    expect(screen.getByText("10")).toBeInTheDocument();
  });

  it("渲染每条正确数", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    expect(screen.getByText("17")).toBeInTheDocument();
    expect(screen.getByText("9")).toBeInTheDocument();
    expect(screen.getByText("4")).toBeInTheDocument();
  });

  it("渲染每条正确率标签", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    // "正确率" 标签每条 1 个
    expect(screen.getAllByText("正确率")).toHaveLength(3);
  });

  it("渲染每条正确率百分比", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    // 0.85 → 85.0%，0.6 → 60.0%，0.4 → 40.0%
    expect(screen.getByText(/85\.0%/)).toBeInTheDocument();
    expect(screen.getByText(/60\.0%/)).toBeInTheDocument();
    expect(screen.getByText(/40\.0%/)).toBeInTheDocument();
  });

  it("渲染每条知识点 badge", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    expect(screen.getByText("一元二次方程")).toBeInTheDocument();
    expect(screen.getByText("配方法")).toBeInTheDocument();
    expect(screen.getByText("现在完成时")).toBeInTheDocument();
    expect(screen.getByText("浮力")).toBeInTheDocument();
  });
});

describe("PracticeSessionList 用时格式化", () => {
  it("2100 秒渲染为 35:00", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    expect(screen.getByText("35:00")).toBeInTheDocument();
  });

  it("1800 秒渲染为 30:00", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    expect(screen.getByText("30:00")).toBeInTheDocument();
  });

  it("75 秒渲染为 01:15", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    expect(screen.getByText("01:15")).toBeInTheDocument();
  });

  it("0 秒渲染为 00:00", () => {
    const sessions: PracticeSession[] = [
      {
        id: "ps-zero",
        childId: "student-001",
        subject: "数学",
        startedAt: "2026-07-10T19:00:00Z",
        questionCount: 0,
        correctCount: 0,
        accuracy: 0,
        durationSeconds: 0,
        knowledgePoints: [],
      },
    ];
    render(<PracticeSessionList sessions={sessions} />);
    expect(screen.getByText("00:00")).toBeInTheDocument();
  });

  it("3600 秒渲染为 60:00", () => {
    const sessions: PracticeSession[] = [
      {
        id: "ps-hour",
        childId: "student-001",
        subject: "数学",
        startedAt: "2026-07-10T19:00:00Z",
        questionCount: 30,
        correctCount: 25,
        accuracy: 0.83,
        durationSeconds: 3600,
        knowledgePoints: [],
      },
    ];
    render(<PracticeSessionList sessions={sessions} />);
    expect(screen.getByText("60:00")).toBeInTheDocument();
  });
});

describe("PracticeSessionList 正确率颜色区分", () => {
  it("正确率 >= 0.8 使用 success 色", () => {
    const { container } = render(
      <PracticeSessionList sessions={makeSessions()} />,
    );
    // accuracy=0.85 → success
    const successTexts = container.querySelectorAll(".text-success");
    expect(successTexts.length).toBeGreaterThan(0);
  });

  it("正确率在 0.6 - 0.8 之间使用 warning 色", () => {
    const { container } = render(
      <PracticeSessionList sessions={makeSessions()} />,
    );
    // accuracy=0.6 → warning
    const warningTexts = container.querySelectorAll(".text-warning");
    expect(warningTexts.length).toBeGreaterThan(0);
  });

  it("正确率 < 0.6 使用 danger 色", () => {
    const { container } = render(
      <PracticeSessionList sessions={makeSessions()} />,
    );
    // accuracy=0.4 → danger
    const dangerTexts = container.querySelectorAll(".text-danger");
    expect(dangerTexts.length).toBeGreaterThan(0);
  });
});

describe("PracticeSessionList 正确率进度条", () => {
  it("每条记录渲染 progressbar", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    const bars = screen.getAllByRole("progressbar");
    expect(bars).toHaveLength(3);
  });

  it("progressbar aria-valuenow 反映正确率百分比", () => {
    render(<PracticeSessionList sessions={makeSessions()} />);
    const bars = screen.getAllByRole("progressbar");
    // 0.85 → 85，0.6 → 60，0.4 → 40
    expect(bars[0]).toHaveAttribute("aria-valuenow", "85");
    expect(bars[1]).toHaveAttribute("aria-valuenow", "60");
    expect(bars[2]).toHaveAttribute("aria-valuenow", "40");
  });
});

describe("PracticeSessionList 知识点 badge", () => {
  it("知识点为空数组时不渲染 badge 区块", () => {
    const sessions: PracticeSession[] = [
      {
        id: "ps-no-kp",
        childId: "student-001",
        subject: "数学",
        startedAt: "2026-07-10T19:00:00Z",
        questionCount: 5,
        correctCount: 5,
        accuracy: 1,
        durationSeconds: 100,
        knowledgePoints: [],
      },
    ];
    const { container } = render(<PracticeSessionList sessions={sessions} />);
    // 不应有知识点 badge
    const badges = container.querySelectorAll(".border-rule.px-2");
    expect(badges.length).toBe(0);
  });

  it("多个知识点都渲染为 badge", () => {
    const sessions: PracticeSession[] = [
      {
        id: "ps-multi-kp",
        childId: "student-001",
        subject: "数学",
        startedAt: "2026-07-10T19:00:00Z",
        questionCount: 10,
        correctCount: 8,
        accuracy: 0.8,
        durationSeconds: 600,
        knowledgePoints: ["一元二次方程", "配方法", "韦达定理"],
      },
    ];
    render(<PracticeSessionList sessions={sessions} />);
    expect(screen.getByText("一元二次方程")).toBeInTheDocument();
    expect(screen.getByText("配方法")).toBeInTheDocument();
    expect(screen.getByText("韦达定理")).toBeInTheDocument();
  });
});
