import { describe, expect, it } from "vitest";
import { Pcre2SyntaxProvider } from "./Pcre2SyntaxProvider";

const provider = new Pcre2SyntaxProvider();

describe("PCRE2 syntax provider", () => {
  it("reports deliberately partial coverage and common PCRE2 capture forms", async () => {
    const result = await provider.parsePattern({
      flavour: "pcre2",
      flavourVersion: "10.47",
      pattern: "(?<angle>a)(?'quote'b)(?P<python>c)(?:d)(?<=e)(f)",
      flags: ["u"],
      options: {},
    });

    expect(result.accepted).toBe(true);
    expect(result.coverage.status).toBe("partial");
    expect(result.captures).toEqual([
      expect.objectContaining({ number: 1, name: "angle" }),
      expect.objectContaining({ number: 2, name: "quote" }),
      expect.objectContaining({ number: 3, name: "python" }),
      expect.objectContaining({ number: 4 }),
    ]);
    expect(result.root.support.flavour).toBe("pcre2");
    expect(result.provider).toEqual({
      id: "regex-tools-pcre2-lexical",
      version: "0.2.0",
    });
  });

  it("does not invent numbering around branch-reset groups", async () => {
    const result = await provider.parsePattern({
      flavour: "pcre2",
      pattern: "(?|(a)|(b))(?<tail>c)",
      flags: [],
      options: {},
    });
    expect(result.accepted).toBe(true);
    expect(result.captures).toEqual([]);
    expect(result.diagnostics[0]?.code).toBe("partial-syntax-coverage");
  });

  it("does not invent captures inside quoted text or extended comments", async () => {
    const result = await provider.parsePattern({
      flavour: "pcre2",
      pattern: String.raw`\Q(not a group)\E # (also not)
(real)`,
      flags: ["x"],
      options: {},
    });
    expect(result.captures).toEqual([expect.objectContaining({ number: 1 })]);
  });

  it("parses PCRE2 numbered and named replacement references", async () => {
    const result = await provider.parseReplacement({
      flavour: "pcre2",
      replacement: "$0 $$ $1 ${name} $<name>",
      captureMetadata: [
        {
          number: 1,
          name: "name",
          range: { startUtf16: 0, endUtf16: 1 },
          repeated: false,
        },
      ],
    });
    expect(result.accepted).toBe(true);
    expect(result.tokens.map((token) => token.kind)).toEqual([
      "full-match",
      "literal",
      "escaped-dollar",
      "literal",
      "numbered-capture",
      "literal",
      "named-capture",
      "literal",
      "named-capture",
    ]);
  });

  it("describes PCRE2 subject, highest-capture and control-mark references", async () => {
    const result = await provider.parseReplacement({
      flavour: "pcre2",
      replacement: "$_ $+ $*route ${*route}",
      captureMetadata: [],
    });

    expect(result.tokens.map((token) => token.kind)).toEqual([
      "subject",
      "literal",
      "highest-capture",
      "literal",
      "engine-special",
      "literal",
      "engine-special",
    ]);
    expect(result.diagnostics).toHaveLength(2);
    expect(result.diagnostics[0]?.code).toBe("replacement-preview-partial");
  });
});
