import { describe, it, expect } from "vitest";
import { routes, navGroups, defaultPath, type NavNode, type RouteDef } from "./routes";

// 把嵌套导航拍平成路由数组（顶层项 + 二级子项），顺序即渲染顺序。
function flatten(nodes: NavNode[]): RouteDef[] {
  return nodes.flatMap((n) => (n.kind === "item" ? [n.route] : n.items));
}

// routes 是控制台导航的单一事实源 —— 派生分组必须保序、不丢项、不串组。
describe("navGroups", () => {
  it("按注册顺序归并分组，每条路由都落到对应组", () => {
    const flat = navGroups().flatMap((g) => flatten(g.nodes));
    // 不丢项、不重复
    expect(flat).toHaveLength(routes.length);
    expect(new Set(flat.map((r) => r.path)).size).toBe(routes.length);
  });

  it("分组首次出现的顺序即组顺序（保持注册顺序）", () => {
    const seen: string[] = [];
    for (const r of routes) if (!seen.includes(r.group)) seen.push(r.group);
    expect(navGroups().map((g) => g.group)).toEqual(seen);
  });

  it("同组路由保持注册时的相对顺序（含二级子项）", () => {
    for (const g of navGroups()) {
      const expected = routes.filter((r) => r.group === g.group).map((r) => r.path);
      expect(flatten(g.nodes).map((r) => r.path)).toEqual(expected);
    }
  });

  it("带 parent 的路由聚成一个二级菜单，不散落成顶层项", () => {
    for (const g of navGroups()) {
      for (const n of g.nodes) {
        if (n.kind === "item") {
          expect(n.route.parent).toBeUndefined(); // 顶层项不该带 parent
        } else {
          expect(n.items.length).toBeGreaterThan(0);
          // 同一个二级菜单下的子项，parent 必须都等于该菜单名
          for (const r of n.items) expect(r.parent).toBe(n.label);
        }
      }
    }
  });

  it("同 parent 的多条路由只生成一个二级菜单节点", () => {
    for (const g of navGroups()) {
      const labels = g.nodes.filter((n) => n.kind === "parent").map((n) => (n as Extract<NavNode, { kind: "parent" }>).label);
      expect(new Set(labels).size).toBe(labels.length);
    }
  });

  it("defaultPath 指向一个真实存在的路由", () => {
    expect(routes.some((r) => r.path === defaultPath)).toBe(true);
  });
});
