import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import type { NormalizedRegexNode } from "../regex/model/syntax";
import { ExplanationTree } from "./ExplanationTree";

function node(
  id: string,
  children: readonly NormalizedRegexNode[] = [],
): NormalizedRegexNode {
  return {
    id,
    kind: "literal",
    range: { startUtf16: 0, endUtf16: 1 },
    raw: id,
    explanation: `Literal ${id}.`,
    children,
    properties: {
      zeroWidth: false,
      nullable: false,
      minimumLength: 1,
      maximumLength: 1,
      consumesInput: true,
    },
    support: {
      flavour: "ecmascript",
      status: "supported",
      notes: [],
    },
    provenance: {
      provider: "test",
      providerVersion: "1",
      source: "parsed",
    },
  };
}

describe("ExplanationTree", () => {
  it("bounds a large preview while retaining a roving-tabindex fallback", () => {
    const children = Array.from({ length: 2_001 }, (_, index) =>
      node(`child-${index}`),
    );
    const root = node("root", children);
    const { container } = render(
      <ExplanationTree
        root={root}
        selectedId="child-2000"
        onSelect={vi.fn()}
      />,
    );

    expect(screen.getByTestId("explanation-render-limit")).toHaveTextContent(
      "Showing the first 2,000 of 2,002 normalized syntax nodes.",
    );
    expect(screen.getByTestId("explanation-render-limit")).toHaveTextContent(
      "The selected pattern node is outside this bounded tree preview.",
    );
    expect(container.querySelectorAll('[role="treeitem"]')).toHaveLength(2_000);
    expect(
      container.querySelectorAll('[role="treeitem"][tabindex="0"]'),
    ).toHaveLength(1);
    expect(
      container.querySelector('[role="treeitem"][tabindex="0"] .tree-token'),
    ).toHaveTextContent("root");
    expect(root.children).toHaveLength(2_001);
  });

  it("bounds descendants when the cutoff occurs below an unchanged ancestor", () => {
    const nested = node(
      "nested",
      Array.from({ length: 2_100 }, (_, index) => node(`child-${index}`)),
    );
    const root = node("root", [nested]);
    const { container } = render(
      <ExplanationTree root={root} onSelect={vi.fn()} />,
    );

    expect(container.querySelectorAll('[role="treeitem"]')).toHaveLength(2_000);
    expect(root.children[0]?.children).toHaveLength(2_100);
  });

  it("shows normalized semantics, active flags, compatibility and provenance", () => {
    render(
      <ExplanationTree
        root={node("x")}
        flags={["g", "u"]}
        onSelect={vi.fn()}
      />,
    );

    expect(screen.getByRole("treeitem")).toHaveTextContent(
      /range: 0…1 UTF-16.*consumes: yes.*empty: no.*length: 1…1.*active flags: gu.*flavour: ecmascript · supported.*compatibility: no parser warning.*risk: not assessed.*parser: test 1 · parsed/u,
    );
  });
});
