import { describe, expect, it } from "vitest";
import type { RegexTestCase } from "./test-case.types";
import { MAXIMUM_REGEX_TESTS } from "./test-case.types";
import {
  assertRegexTestSuiteWithinLimit,
  importRegexTestSuite,
  MAXIMUM_REGEX_TEST_SUITE_FILE_BYTES,
  serializeRegexTestSuite,
} from "./test-suite.serialization";

function testCase(id = "case-1"): RegexTestCase {
  return {
    id,
    name: "Unicode word",
    enabled: true,
    flavour: "ecmascript",
    flavourVersion: "ECMAScript 2025",
    pattern: "\\p{Letter}+",
    flags: ["g", "u"],
    options: {},
    scanAll: true,
    subject: "Bérénice",
    expectation: { kind: "match-count", count: 1 },
  };
}

describe("standalone regex test-suite JSON", () => {
  it("round-trips all reproducibility fields when subjects are included", () => {
    const suite = importRegexTestSuite(
      serializeRegexTestSuite([testCase()], true),
    );

    expect(suite.schemaVersion).toBe(1);
    expect(suite.tests).toEqual([testCase()]);
  });

  it("omits sensitive subjects by default without mutating the source", () => {
    const source = testCase();
    const suite = importRegexTestSuite(
      serializeRegexTestSuite([source], false),
    );

    expect(suite.tests[0]?.subject).toBe("");
    expect(source.subject).toBe("Bérénice");
  });

  it("round-trips bounded generated-case seed provenance even when the subject is omitted", () => {
    const generated: RegexTestCase = {
      ...testCase("generated"),
      expectation: { kind: "should-match" },
      generation: {
        kind: "generated",
        generatorId: "regex-tools-ast-cases",
        generatorVersion: "1",
        seed: "release-seed-42",
        candidateId: "generated-aabbccdd-1",
        intendedOutcome: "match",
      },
    };
    const suite = importRegexTestSuite(
      serializeRegexTestSuite([generated], false),
    );

    expect(suite.tests[0]?.subject).toBe("");
    expect(suite.tests[0]?.generation).toEqual(generated.generation);
  });

  it("rejects forged or inconsistent generated-case provenance", () => {
    const generated = {
      ...testCase("generated"),
      expectation: { kind: "should-match" },
      generation: {
        kind: "generated",
        generatorId: "regex-tools-ast-cases",
        generatorVersion: "1",
        seed: "release-seed-42",
        candidateId: "generated-aabbccdd-1",
        intendedOutcome: "no-match",
      },
    };
    expect(() =>
      importRegexTestSuite(
        JSON.stringify({ schemaVersion: 1, tests: [generated] }),
      ),
    ).toThrow("intendedOutcome does not match");
    expect(() =>
      importRegexTestSuite(
        JSON.stringify({
          schemaVersion: 1,
          tests: [
            {
              ...generated,
              expectation: { kind: "should-not-match" },
              generation: {
                ...generated.generation,
                generatorVersion: "future",
              },
            },
          ],
        }),
      ),
    ).toThrow("generatorVersion");
  });

  it("rejects malformed, unsupported, oversized, and over-count suites", () => {
    expect(() => importRegexTestSuite("{")).toThrow("not valid JSON");
    expect(() =>
      importRegexTestSuite('{"schemaVersion":2,"tests":[]}'),
    ).toThrow("schema version 1");
    expect(() =>
      importRegexTestSuite(
        JSON.stringify({
          schemaVersion: 1,
          tests: Array.from({ length: MAXIMUM_REGEX_TESTS + 1 }, (_, index) =>
            testCase(`case-${index}`),
          ),
        }),
      ),
    ).toThrow(`more than ${MAXIMUM_REGEX_TESTS} tests`);
    expect(() =>
      importRegexTestSuite(" ".repeat(MAXIMUM_REGEX_TEST_SUITE_FILE_BYTES + 1)),
    ).toThrow("32 MiB");
    expect(() =>
      importRegexTestSuite(
        JSON.stringify({
          schemaVersion: 1,
          tests: [testCase("duplicate"), testCase("duplicate")],
        }),
      ),
    ).toThrow(/test id.*duplicated/iu);
  });

  it("rejects an oversized export before JSON materialization", () => {
    const largeSubject = "x".repeat(16 * 1024 * 1024);
    const largeSuite = [
      { ...testCase("large-1"), subject: largeSubject },
      { ...testCase("large-2"), subject: largeSubject },
    ];
    expect(() => assertRegexTestSuiteWithinLimit(largeSuite)).toThrow(
      "32 MiB aggregate state limit",
    );
    expect(() => serializeRegexTestSuite(largeSuite, true)).toThrow(
      "32 MiB aggregate export limit",
    );
  });
});
