import { describe, expect, it } from "vitest";

import type { OutlineChapterView, WorldEntityCardView } from "@/lib/api/types";
import {
  CONTEXT_TABS,
  contextTabHref,
  toCodexGroups,
  toOutlineRows,
} from "./contextDrawer";

const entity = (
  type: string,
  name: string,
  rules: string[] = [],
): WorldEntityCardView => ({ type, name, rules });

describe("contextTabHref", () => {
  it("按 Tab key 拼出完整编辑页路由", () => {
    // Arrange / Act / Assert
    expect(contextTabHref("p1", "codex")).toBe("/projects/p1/codex");
    expect(contextTabHref("p1", "foreshadow")).toBe("/projects/p1/foreshadow");
    expect(contextTabHref("p1", "style")).toBe("/projects/p1/style");
  });

  it("五个 Tab 覆盖全部读为主入口", () => {
    expect(CONTEXT_TABS.map((t) => t.key)).toEqual([
      "codex",
      "outline",
      "foreshadow",
      "rules",
      "style",
    ]);
  });
});

describe("toCodexGroups", () => {
  it("空列表返回空分组", () => {
    expect(toCodexGroups([])).toEqual([]);
  });

  it("按 type 归组并保持首次出现顺序", () => {
    const entities = [
      entity("势力", "雾港议会"),
      entity("地理", "北境"),
      entity("势力", "红手会"),
    ];

    const groups = toCodexGroups(entities);

    expect(groups.map((g) => g.type)).toEqual(["势力", "地理"]);
    expect(groups[0]?.entities.map((e) => e.name)).toEqual([
      "雾港议会",
      "红手会",
    ]);
    expect(groups[1]?.entities.map((e) => e.name)).toEqual(["北境"]);
  });

  it("空白/缺省 type 归入「未分类」", () => {
    const groups = toCodexGroups([entity("  ", "无名之物")]);
    expect(groups[0]?.type).toBe("未分类");
  });

  it("不修改入参数组", () => {
    const entities = [entity("势力", "甲"), entity("势力", "乙")];
    const snapshot = [...entities];
    toCodexGroups(entities);
    expect(entities).toEqual(snapshot);
  });
});

describe("toOutlineRows", () => {
  const chapter = (
    no: number,
    volume: number,
    beats: string[],
    codes: string[] = [],
  ): OutlineChapterView => ({
    no,
    volume,
    beats,
    foreshadow_windows: codes.map((code) => ({ code })),
  });

  it("空列表返回空数组", () => {
    expect(toOutlineRows([])).toEqual([]);
  });

  it("按章号升序整形并抽出节拍与伏笔代号", () => {
    const rows = toOutlineRows([
      chapter(2, 1, ["决战"], ["F2"]),
      chapter(1, 1, ["开场", "引入"], ["F1"]),
    ]);

    expect(rows.map((r) => r.no)).toEqual([1, 2]);
    expect(rows[0]?.beats).toEqual(["开场", "引入"]);
    expect(rows[0]?.foreshadowCodes).toEqual(["F1"]);
    expect(rows[1]?.foreshadowCodes).toEqual(["F2"]);
  });

  it("缺省 beats / foreshadow_windows 回退为空数组", () => {
    const rows = toOutlineRows([{ no: 1, volume: 1 }]);
    expect(rows[0]?.beats).toEqual([]);
    expect(rows[0]?.foreshadowCodes).toEqual([]);
  });

  it("不修改入参数组", () => {
    const chapters = [chapter(2, 1, []), chapter(1, 1, [])];
    const snapshot = chapters.map((c) => c.no);
    toOutlineRows(chapters);
    expect(chapters.map((c) => c.no)).toEqual(snapshot);
  });
});
