import { useId, useRef, useState } from "react";
import {
  DEFAULT_REGEX_LIMITS,
  utf8ByteLength,
} from "../regex/execution/request-limits";
import type { RegexResourceLimits } from "../regex/execution/request-limits";
import {
  MAXIMUM_REGEX_TEST_CAPTURE_NAME_UTF16,
  MAXIMUM_REGEX_TEST_EXPECTED_UTF16,
  MAXIMUM_REGEX_TEST_NAME_UTF16,
  MAXIMUM_REGEX_TEST_PATTERN_UTF16,
  MAXIMUM_REGEX_TEST_SUBJECT_UTF16,
  MAXIMUM_REGEX_TESTS,
} from "../regex/tests/test-case.types";
import type {
  RegexTestCase,
  RegexTestExpectation,
  RegexTestResult,
} from "../regex/tests/test-case.types";
import {
  importRegexTestSuite,
  MAXIMUM_REGEX_TEST_SUITE_FILE_BYTES,
  serializeRegexTestSuite,
} from "../regex/tests/test-suite.serialization";
import type { RegexFlavourId } from "../regex/model/flavour";
import {
  AVAILABLE_REGEX_FLAVOURS,
  parseRegexFlags,
  type RegexFlavourDefinition,
} from "../regex/flavours/flavour-registry";

const MAXIMUM_MATCH_COUNT = 1_000_000;
const MAXIMUM_MATCH_INDEX = 1_000_000;
const MAXIMUM_CAPTURE_NUMBER = 1_000;
const MAXIMUM_TIMEOUT_MS = DEFAULT_REGEX_LIMITS.advancedMaximumTimeoutMs;

type CaptureSelector = "number" | "name";
type CaptureStatus = "participated" | "did-not-participate" | "matched-empty";

export interface RegexTestDraft {
  readonly name: string;
  readonly pattern: string;
  readonly flags: readonly string[];
  readonly scanAll: boolean;
  readonly subject: string;
  readonly expectation: RegexTestExpectation;
  readonly replacement?: string;
  readonly resourceLimits?: Readonly<
    Pick<RegexResourceLimits, "manualExecutionTimeoutMs">
  >;
  readonly generation?: RegexTestCase["generation"];
}

export interface UnitTestPanelProps {
  readonly tests: readonly RegexTestCase[];
  readonly results: ReadonlyMap<string, RegexTestResult>;
  readonly currentFlavour: RegexFlavourId;
  readonly currentPattern: string;
  readonly currentFlags: readonly string[];
  readonly currentScanAll: boolean;
  readonly currentSubject: string;
  readonly currentReplacement: string;
  readonly currentResultDraft?: RegexTestDraft;
  readonly onAdd: (draft: RegexTestDraft) => void;
  readonly onUpdate: (id: string, draft: RegexTestDraft) => void;
  readonly onClone: (id: string) => void;
  readonly onImport: (tests: readonly RegexTestCase[]) => void;
  readonly onRemove: (id: string) => void;
  readonly onToggle: (id: string) => void;
  readonly onRunAll: () => void;
  readonly onRunSelected: (ids: readonly string[]) => void;
  readonly onCancel: () => void;
  readonly running: boolean;
}

function boundedInteger(
  source: string,
  label: string,
  minimum: number,
  maximum: number,
): number {
  const value = Number(source);
  if (
    source.trim() === "" ||
    !Number.isSafeInteger(value) ||
    value < minimum ||
    value > maximum
  ) {
    throw new Error(
      `${label} must be a whole number from ${minimum.toLocaleString()} to ${maximum.toLocaleString()}.`,
    );
  }
  return value;
}

function boundedText(value: string, label: string, maximum: number): string {
  if (value.length > maximum) {
    throw new Error(
      `${label} exceeds the ${maximum.toLocaleString()} UTF-16 unit limit.`,
    );
  }
  return value;
}

function validateDraft(
  draft: RegexTestDraft,
  flavour: RegexFlavourDefinition,
): RegexTestDraft {
  boundedText(draft.name, "Test name", MAXIMUM_REGEX_TEST_NAME_UTF16);
  boundedText(draft.pattern, "Pattern", MAXIMUM_REGEX_TEST_PATTERN_UTF16);
  boundedText(draft.subject, "Subject", MAXIMUM_REGEX_TEST_SUBJECT_UTF16);
  const subjectBytes = utf8ByteLength(draft.subject);
  if (subjectBytes > DEFAULT_REGEX_LIMITS.interactiveSubjectHardBytes) {
    throw new Error(
      `Subject exceeds the ${DEFAULT_REGEX_LIMITS.interactiveSubjectHardBytes.toLocaleString()} UTF-8 byte limit.`,
    );
  }
  parseRegexFlags(draft.flags, flavour, "Flags");
  if (
    draft.replacement !== undefined &&
    draft.replacement.length >
      DEFAULT_REGEX_LIMITS.maximumReplacementTemplateUtf16
  ) {
    throw new Error(
      `Replacement template exceeds the ${DEFAULT_REGEX_LIMITS.maximumReplacementTemplateUtf16.toLocaleString()} UTF-16 unit limit.`,
    );
  }
  switch (draft.expectation.kind) {
    case "full-match":
      boundedText(
        draft.expectation.value,
        "Expected full match",
        MAXIMUM_REGEX_TEST_EXPECTED_UTF16,
      );
      break;
    case "capture":
      if (draft.expectation.groupName !== undefined) {
        boundedText(
          draft.expectation.groupName,
          "Capture name",
          MAXIMUM_REGEX_TEST_CAPTURE_NAME_UTF16,
        );
      }
      if (draft.expectation.value !== undefined) {
        boundedText(
          draft.expectation.value,
          "Expected capture value",
          MAXIMUM_REGEX_TEST_EXPECTED_UTF16,
        );
      }
      break;
    case "replacement":
      boundedText(
        draft.expectation.expected,
        "Expected replacement",
        MAXIMUM_REGEX_TEST_EXPECTED_UTF16,
      );
      break;
    default:
      break;
  }
  return draft;
}

function expectationSummary(expectation: RegexTestExpectation): string {
  switch (expectation.kind) {
    case "should-match":
      return "should match";
    case "should-not-match":
      return "should not match";
    case "match-count":
      return `match count: ${expectation.count}`;
    case "full-match":
      return `full match ${expectation.matchIndex}: exact value`;
    case "capture":
      return `capture ${
        expectation.groupName === undefined
          ? `#${expectation.groupNumber}`
          : `<${expectation.groupName}>`
      }: ${expectation.status.replaceAll("-", " ")}`;
    case "replacement":
      return "replacement: exact output";
    case "must-complete-within":
      return `complete within ${expectation.milliseconds} ms`;
    case "must-time-out":
      return "must time out";
  }
}

function downloadJson(serialized: string): void {
  const blob = new Blob([serialized], { type: "application/json" });
  const url = URL.createObjectURL(blob);
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.download = "regex-tests.regex-tests.json";
  anchor.click();
  window.setTimeout(() => URL.revokeObjectURL(url), 1_000);
}

export function UnitTestPanel({
  tests,
  results,
  currentFlavour,
  currentPattern,
  currentFlags,
  currentScanAll,
  currentSubject,
  currentReplacement,
  currentResultDraft,
  onAdd,
  onUpdate,
  onClone,
  onImport,
  onRemove,
  onToggle,
  onRunAll,
  onRunSelected,
  onCancel,
  running,
}: UnitTestPanelProps) {
  const flavourDefinition = AVAILABLE_REGEX_FLAVOURS.require(currentFlavour);
  const helpId = useId();
  const errorId = useId();
  const formId = useId();
  const importInput = useRef<HTMLInputElement>(null);
  const [editingId, setEditingId] = useState<string>();
  const [name, setName] = useState("Current example");
  const [pattern, setPattern] = useState(currentPattern);
  const [flags, setFlags] = useState<readonly string[]>(currentFlags);
  const [scanAll, setScanAll] = useState(currentScanAll);
  const [subject, setSubject] = useState(currentSubject);
  const [expectationKind, setExpectationKind] =
    useState<RegexTestExpectation["kind"]>("should-match");
  const [matchCount, setMatchCount] = useState("1");
  const [matchIndex, setMatchIndex] = useState("0");
  const [expectedFullMatch, setExpectedFullMatch] = useState("");
  const [captureSelector, setCaptureSelector] =
    useState<CaptureSelector>("number");
  const [captureNumber, setCaptureNumber] = useState("1");
  const [captureName, setCaptureName] = useState("");
  const [captureStatus, setCaptureStatus] =
    useState<CaptureStatus>("participated");
  const [assertCaptureValue, setAssertCaptureValue] = useState(false);
  const [expectedCaptureValue, setExpectedCaptureValue] = useState("");
  const [includeReplacement, setIncludeReplacement] = useState(false);
  const [replacement, setReplacement] = useState(currentReplacement);
  const [expectedReplacement, setExpectedReplacement] = useState("");
  const [completionMilliseconds, setCompletionMilliseconds] = useState("100");
  const [timeoutMilliseconds, setTimeoutMilliseconds] = useState("");
  const [formError, setFormError] = useState<string>();
  const [selectedIds, setSelectedIds] = useState<ReadonlySet<string>>(
    new Set(),
  );
  const [failuresOnly, setFailuresOnly] = useState(false);
  const [includeSubjects, setIncludeSubjects] = useState(false);
  const [suiteNotice, setSuiteNotice] = useState<string>();
  const [suiteError, setSuiteError] = useState<string>();

  const maximumReached = tests.length >= MAXIMUM_REGEX_TESTS;
  const runnableSelected = tests
    .filter((test) => test.enabled && selectedIds.has(test.id))
    .map((test) => test.id);
  const visibleTests = failuresOnly
    ? tests.filter((test) => results.get(test.id)?.passed === false)
    : tests;

  function createExpectation(): RegexTestExpectation {
    switch (expectationKind) {
      case "should-match":
      case "should-not-match":
      case "must-time-out":
        return { kind: expectationKind };
      case "match-count":
        return {
          kind: expectationKind,
          count: boundedInteger(
            matchCount,
            "Expected match count",
            0,
            MAXIMUM_MATCH_COUNT,
          ),
        };
      case "full-match":
        return {
          kind: expectationKind,
          matchIndex: boundedInteger(
            matchIndex,
            "Match index",
            0,
            MAXIMUM_MATCH_INDEX,
          ),
          value: expectedFullMatch,
        };
      case "capture": {
        const selector =
          captureSelector === "number"
            ? {
                groupNumber: boundedInteger(
                  captureNumber,
                  "Capture number",
                  1,
                  MAXIMUM_CAPTURE_NUMBER,
                ),
              }
            : captureName.trim() === ""
              ? (() => {
                  throw new Error("Capture name must not be empty.");
                })()
              : { groupName: captureName.trim() };
        return {
          kind: expectationKind,
          matchIndex: boundedInteger(
            matchIndex,
            "Match index",
            0,
            MAXIMUM_MATCH_INDEX,
          ),
          ...selector,
          status: captureStatus,
          ...(assertCaptureValue ? { value: expectedCaptureValue } : {}),
        };
      }
      case "replacement":
        return {
          kind: expectationKind,
          expected: expectedReplacement,
        };
      case "must-complete-within":
        return {
          kind: expectationKind,
          milliseconds: boundedInteger(
            completionMilliseconds,
            "Completion limit",
            1,
            MAXIMUM_TIMEOUT_MS,
          ),
        };
    }
  }

  function makeDraft(): RegexTestDraft {
    const timeout =
      timeoutMilliseconds.trim() === ""
        ? undefined
        : boundedInteger(
            timeoutMilliseconds,
            "Per-test timeout",
            1,
            MAXIMUM_TIMEOUT_MS,
          );
    return validateDraft(
      {
        name: name.trim() || "Untitled test",
        pattern,
        flags,
        scanAll,
        subject,
        expectation: createExpectation(),
        ...(expectationKind === "replacement" && includeReplacement
          ? { replacement }
          : {}),
        ...(timeout === undefined
          ? {}
          : {
              resourceLimits: {
                manualExecutionTimeoutMs: timeout,
              },
            }),
      },
      flavourDefinition,
    );
  }

  function submitTest(): void {
    if (running) return;
    try {
      if (!editingId && maximumReached) {
        throw new Error(
          `A suite is limited to ${MAXIMUM_REGEX_TESTS.toLocaleString()} tests.`,
        );
      }
      const draft = makeDraft();
      if (editingId) {
        onUpdate(editingId, draft);
        setEditingId(undefined);
      } else {
        onAdd(draft);
      }
      setFormError(undefined);
    } catch (error) {
      setFormError(error instanceof Error ? error.message : String(error));
    }
  }

  function loadDraft(draft: RegexTestDraft, id?: string): void {
    setEditingId(id);
    setName(draft.name);
    setPattern(draft.pattern);
    setFlags(draft.flags);
    setScanAll(draft.scanAll);
    setSubject(draft.subject);
    setExpectationKind(draft.expectation.kind);
    setMatchCount(
      draft.expectation.kind === "match-count"
        ? String(draft.expectation.count)
        : "1",
    );
    setMatchIndex(
      draft.expectation.kind === "full-match" ||
        draft.expectation.kind === "capture"
        ? String(draft.expectation.matchIndex)
        : "0",
    );
    setExpectedFullMatch(
      draft.expectation.kind === "full-match" ? draft.expectation.value : "",
    );
    setCaptureSelector(
      draft.expectation.kind === "capture" &&
        draft.expectation.groupName !== undefined
        ? "name"
        : "number",
    );
    setCaptureNumber(
      draft.expectation.kind === "capture" &&
        draft.expectation.groupNumber !== undefined
        ? String(draft.expectation.groupNumber)
        : "1",
    );
    setCaptureName(
      draft.expectation.kind === "capture"
        ? (draft.expectation.groupName ?? "")
        : "",
    );
    setCaptureStatus(
      draft.expectation.kind === "capture"
        ? draft.expectation.status
        : "participated",
    );
    setAssertCaptureValue(
      draft.expectation.kind === "capture" &&
        draft.expectation.value !== undefined,
    );
    setExpectedCaptureValue(
      draft.expectation.kind === "capture"
        ? (draft.expectation.value ?? "")
        : "",
    );
    setIncludeReplacement(draft.replacement !== undefined);
    setReplacement(draft.replacement ?? currentReplacement);
    setExpectedReplacement(
      draft.expectation.kind === "replacement"
        ? draft.expectation.expected
        : "",
    );
    setCompletionMilliseconds(
      draft.expectation.kind === "must-complete-within"
        ? String(draft.expectation.milliseconds)
        : "100",
    );
    setTimeoutMilliseconds(
      draft.resourceLimits?.manualExecutionTimeoutMs === undefined
        ? ""
        : String(draft.resourceLimits.manualExecutionTimeoutMs),
    );
    setFormError(undefined);
  }

  function editTest(test: RegexTestCase): void {
    loadDraft(
      {
        name: test.name,
        pattern: test.pattern,
        flags: test.flags,
        scanAll: test.scanAll ?? false,
        subject: test.subject,
        expectation: test.expectation,
        ...(test.replacement === undefined
          ? {}
          : { replacement: test.replacement }),
        ...(test.resourceLimits?.manualExecutionTimeoutMs === undefined
          ? {}
          : {
              resourceLimits: {
                manualExecutionTimeoutMs:
                  test.resourceLimits.manualExecutionTimeoutMs,
              },
            }),
      },
      test.id,
    );
  }

  function addCurrentResult(): void {
    if (running || maximumReached || !currentResultDraft) return;
    try {
      onAdd(validateDraft(currentResultDraft, flavourDefinition));
      setFormError(undefined);
    } catch (error) {
      setFormError(error instanceof Error ? error.message : String(error));
    }
  }

  async function importSuite(file: File): Promise<void> {
    setSuiteError(undefined);
    setSuiteNotice(undefined);
    try {
      if (file.size > MAXIMUM_REGEX_TEST_SUITE_FILE_BYTES) {
        throw new Error(
          `Test-suite JSON exceeds the ${(MAXIMUM_REGEX_TEST_SUITE_FILE_BYTES / 1024 / 1024).toLocaleString()} MiB file limit.`,
        );
      }
      const imported = importRegexTestSuite(await file.text());
      if (tests.length + imported.tests.length > MAXIMUM_REGEX_TESTS) {
        throw new Error(
          `Import would exceed the ${MAXIMUM_REGEX_TESTS.toLocaleString()} test limit.`,
        );
      }
      onImport(imported.tests);
      setSuiteNotice(
        `Imported ${imported.tests.length.toLocaleString()} test${
          imported.tests.length === 1 ? "" : "s"
        }.`,
      );
    } catch (error) {
      setSuiteError(error instanceof Error ? error.message : String(error));
    }
  }

  function exportSuite(): void {
    setSuiteError(undefined);
    setSuiteNotice(undefined);
    try {
      downloadJson(serializeRegexTestSuite(tests, includeSubjects));
      setSuiteNotice(
        `Exported ${tests.length.toLocaleString()} test${
          tests.length === 1 ? "" : "s"
        }${includeSubjects ? " with subjects" : " without subjects"}.`,
      );
    } catch (error) {
      setSuiteError(error instanceof Error ? error.message : String(error));
    }
  }

  return (
    <div className="mode-grid">
      <section className="panel test-create-panel">
        <header className="panel-heading">
          <div>
            <p className="eyebrow">Versioned project tests</p>
            <h2>{editingId ? "Edit unit test" : "Add unit test"}</h2>
          </div>
        </header>
        <form
          id={formId}
          noValidate
          aria-describedby={formError ? errorId : undefined}
          onSubmit={(event) => {
            event.preventDefault();
            submitTest();
          }}
        >
          <label className="stacked-control">
            <span>Name</span>
            <input
              type="text"
              maxLength={MAXIMUM_REGEX_TEST_NAME_UTF16}
              value={name}
              onChange={(event) => setName(event.target.value)}
            />
          </label>
          <label className="stacked-control">
            <span>Pattern</span>
            <textarea
              rows={3}
              maxLength={MAXIMUM_REGEX_TEST_PATTERN_UTF16}
              value={pattern}
              onChange={(event) => setPattern(event.target.value)}
            />
          </label>
          <fieldset className="test-flag-selector">
            <legend>Flags</legend>
            {flavourDefinition.flags.map((flag) => (
              <label key={flag.value} title={flag.label}>
                <input
                  type="checkbox"
                  aria-label={`${flag.label} (${flag.value})`}
                  checked={flags.includes(flag.value)}
                  onChange={(event) => {
                    setFlags((current) => {
                      const next = new Set(current);
                      if (event.target.checked) {
                        next.add(flag.value);
                        for (const group of flavourDefinition.mutuallyExclusiveFlags ??
                          []) {
                          if (!group.includes(flag.value)) continue;
                          for (const incompatible of group) {
                            if (incompatible !== flag.value) {
                              next.delete(incompatible);
                            }
                          }
                        }
                      } else {
                        next.delete(flag.value);
                      }
                      return flavourDefinition.flags
                        .map((definition) => definition.value)
                        .filter((value) => next.has(value));
                    });
                  }}
                />
                <span>{flag.value}</span>
              </label>
            ))}
          </fieldset>
          <div className="stacked-control">
            <label className="check-control">
              <input
                type="checkbox"
                checked={scanAll}
                onChange={(event) => setScanAll(event.target.checked)}
              />
              <span>Scan all matches without changing saved flags</span>
            </label>
          </div>
          <label className="stacked-control">
            <span>Subject</span>
            <textarea
              rows={6}
              maxLength={MAXIMUM_REGEX_TEST_SUBJECT_UTF16}
              value={subject}
              onChange={(event) => setSubject(event.target.value)}
            />
          </label>
          <label className="stacked-control">
            <span>Expectation</span>
            <select
              value={expectationKind}
              onChange={(event) => {
                setExpectationKind(
                  event.target.value as RegexTestExpectation["kind"],
                );
                setFormError(undefined);
              }}
            >
              <option value="should-match">Should match at least once</option>
              <option value="should-not-match">Should not match</option>
              <option value="match-count">Exact match count</option>
              <option value="full-match">Full match equals value</option>
              <option value="capture">Capture status and value</option>
              <option value="replacement">
                Replacement output equals value
              </option>
              <option value="must-complete-within">
                Must complete within a time
              </option>
              <option value="must-time-out">Must time out</option>
            </select>
          </label>

          {expectationKind === "match-count" ? (
            <label className="stacked-control">
              <span>Expected match count</span>
              <input
                type="number"
                min={0}
                max={MAXIMUM_MATCH_COUNT}
                step={1}
                value={matchCount}
                onChange={(event) => setMatchCount(event.target.value)}
              />
            </label>
          ) : null}

          {expectationKind === "full-match" ? (
            <>
              <label className="stacked-control">
                <span>Match index (zero-based)</span>
                <input
                  type="number"
                  min={0}
                  max={MAXIMUM_MATCH_INDEX}
                  step={1}
                  value={matchIndex}
                  onChange={(event) => setMatchIndex(event.target.value)}
                />
              </label>
              <label className="stacked-control">
                <span>Expected full match</span>
                <textarea
                  rows={3}
                  maxLength={MAXIMUM_REGEX_TEST_EXPECTED_UTF16}
                  value={expectedFullMatch}
                  onChange={(event) => setExpectedFullMatch(event.target.value)}
                />
              </label>
            </>
          ) : null}

          {expectationKind === "capture" ? (
            <>
              <label className="stacked-control">
                <span>Match index (zero-based)</span>
                <input
                  type="number"
                  min={0}
                  max={MAXIMUM_MATCH_INDEX}
                  step={1}
                  value={matchIndex}
                  onChange={(event) => setMatchIndex(event.target.value)}
                />
              </label>
              <label className="stacked-control">
                <span>Identify capture by</span>
                <select
                  value={captureSelector}
                  onChange={(event) =>
                    setCaptureSelector(event.target.value as CaptureSelector)
                  }
                >
                  <option value="number">Group number</option>
                  <option value="name">Group name</option>
                </select>
              </label>
              {captureSelector === "number" ? (
                <label className="stacked-control">
                  <span>Capture number</span>
                  <input
                    type="number"
                    min={1}
                    max={MAXIMUM_CAPTURE_NUMBER}
                    step={1}
                    value={captureNumber}
                    onChange={(event) => setCaptureNumber(event.target.value)}
                  />
                </label>
              ) : (
                <label className="stacked-control">
                  <span>Capture name</span>
                  <input
                    type="text"
                    maxLength={MAXIMUM_REGEX_TEST_CAPTURE_NAME_UTF16}
                    value={captureName}
                    onChange={(event) => setCaptureName(event.target.value)}
                  />
                </label>
              )}
              <label className="stacked-control">
                <span>Expected capture status</span>
                <select
                  value={captureStatus}
                  onChange={(event) =>
                    setCaptureStatus(event.target.value as CaptureStatus)
                  }
                >
                  <option value="participated">Participated</option>
                  <option value="did-not-participate">
                    Did not participate
                  </option>
                  <option value="matched-empty">Matched empty</option>
                </select>
              </label>
              <div className="stacked-control">
                <label className="check-control">
                  <input
                    type="checkbox"
                    checked={assertCaptureValue}
                    onChange={(event) =>
                      setAssertCaptureValue(event.target.checked)
                    }
                  />
                  <span>Also assert the exact capture value</span>
                </label>
              </div>
              {assertCaptureValue ? (
                <label className="stacked-control">
                  <span>Expected capture value</span>
                  <textarea
                    rows={3}
                    maxLength={MAXIMUM_REGEX_TEST_EXPECTED_UTF16}
                    value={expectedCaptureValue}
                    onChange={(event) =>
                      setExpectedCaptureValue(event.target.value)
                    }
                  />
                </label>
              ) : null}
            </>
          ) : null}

          {expectationKind === "replacement" ? (
            <>
              <div className="stacked-control">
                <label className="check-control">
                  <input
                    type="checkbox"
                    checked={includeReplacement}
                    onChange={(event) =>
                      setIncludeReplacement(event.target.checked)
                    }
                  />
                  <span>Store a replacement template for this test</span>
                </label>
              </div>
              {includeReplacement ? (
                <label className="stacked-control">
                  <span>Replacement template</span>
                  <textarea
                    rows={3}
                    value={replacement}
                    onChange={(event) => setReplacement(event.target.value)}
                    aria-label="Replacement template"
                    maxLength={
                      DEFAULT_REGEX_LIMITS.maximumReplacementTemplateUtf16
                    }
                  />
                </label>
              ) : null}
              <label className="stacked-control">
                <span>Expected replacement output</span>
                <textarea
                  rows={3}
                  maxLength={MAXIMUM_REGEX_TEST_EXPECTED_UTF16}
                  value={expectedReplacement}
                  onChange={(event) =>
                    setExpectedReplacement(event.target.value)
                  }
                />
              </label>
            </>
          ) : null}

          {expectationKind === "must-complete-within" ? (
            <label className="stacked-control">
              <span>Completion limit (ms)</span>
              <input
                type="number"
                min={1}
                max={MAXIMUM_TIMEOUT_MS}
                step={1}
                value={completionMilliseconds}
                onChange={(event) =>
                  setCompletionMilliseconds(event.target.value)
                }
              />
            </label>
          ) : null}

          <label className="stacked-control">
            <span>Per-test timeout (ms, optional)</span>
            <input
              type="number"
              min={1}
              max={MAXIMUM_TIMEOUT_MS}
              step={1}
              value={timeoutMilliseconds}
              aria-describedby={helpId}
              placeholder={`Default (${DEFAULT_REGEX_LIMITS.manualExecutionTimeoutMs} ms)`}
              onChange={(event) => setTimeoutMilliseconds(event.target.value)}
            />
          </label>
          <p className="field-help" id={helpId}>
            Maximum {MAXIMUM_REGEX_TESTS.toLocaleString()} tests. Names,
            regexes, subjects, and expected values are bounded before entering
            project state. Leave timeout blank for the default worker limit.
          </p>
          {formError ? (
            <p className="privacy-warning" id={errorId} role="alert">
              {formError}
            </p>
          ) : null}
        </form>
        <div className="test-form-actions">
          <button
            type="submit"
            form={formId}
            className="primary-button"
            disabled={running || (!editingId && maximumReached)}
          >
            {editingId ? "Update test" : "Add test"}
          </button>
          {editingId ? (
            <button
              type="button"
              className="secondary-button"
              disabled={running}
              onClick={() => setEditingId(undefined)}
            >
              Cancel edit
            </button>
          ) : null}
          <button
            type="button"
            className="secondary-button"
            disabled={running || maximumReached || !currentResultDraft}
            title={
              currentResultDraft
                ? "Store an assertion from the latest complete result"
                : "Run the current expression successfully first"
            }
            onClick={addCurrentResult}
          >
            Add current result
          </button>
        </div>
      </section>

      <section className="panel tests-panel">
        <header className="panel-heading">
          <div>
            <p className="eyebrow">Actual engine assertions</p>
            <h2>
              Test suite <small>({tests.length.toLocaleString()})</small>
            </h2>
          </div>
          {running ? (
            <button type="button" className="primary-button" onClick={onCancel}>
              Cancel
            </button>
          ) : (
            <div className="test-run-actions">
              <button
                type="button"
                className="secondary-button"
                onClick={() => onRunSelected(runnableSelected)}
                disabled={runnableSelected.length === 0}
              >
                Run selected
              </button>
              <button
                type="button"
                className="primary-button"
                onClick={onRunAll}
                disabled={tests.every((test) => !test.enabled)}
              >
                Run all
              </button>
            </div>
          )}
        </header>
        <div className="test-suite-toolbar">
          <label className="check-control">
            <input
              type="checkbox"
              checked={failuresOnly}
              onChange={(event) => setFailuresOnly(event.target.checked)}
            />
            <span>Failures only</span>
          </label>
          <label
            className="check-control"
            title="Test subjects can be sensitive"
          >
            <input
              type="checkbox"
              checked={includeSubjects}
              onChange={(event) => setIncludeSubjects(event.target.checked)}
            />
            <span>Include subjects in JSON</span>
          </label>
          <button
            type="button"
            className="secondary-button"
            disabled={tests.length === 0}
            onClick={exportSuite}
          >
            Export JSON
          </button>
          <button
            type="button"
            className="secondary-button"
            disabled={running || maximumReached}
            onClick={() => importInput.current?.click()}
          >
            Import JSON
          </button>
          <input
            ref={importInput}
            className="visually-hidden"
            type="file"
            tabIndex={-1}
            accept="application/json,.json"
            aria-label="Import test-suite JSON file"
            onChange={(event) => {
              const input = event.currentTarget;
              const file = input.files?.[0];
              if (file) {
                void importSuite(file).finally(() => {
                  input.value = "";
                });
              }
            }}
          />
        </div>
        {!includeSubjects && tests.length > 0 ? (
          <p className="privacy-warning">
            Export omits test subjects by default. Enable subject export only
            when the JSON may safely contain those inputs.
          </p>
        ) : null}
        {suiteError ? (
          <p className="privacy-warning" role="alert">
            {suiteError}
          </p>
        ) : null}
        {suiteNotice ? <p role="status">{suiteNotice}</p> : null}
        {tests.length === 0 ? (
          <p className="empty-state">No tests yet.</p>
        ) : visibleTests.length === 0 ? (
          <p className="empty-state">No failing tests to show.</p>
        ) : (
          <ul className="test-list">
            {visibleTests.map((test) => {
              const result = results.get(test.id);
              return (
                <li key={test.id}>
                  <label className="test-selection">
                    <input
                      type="checkbox"
                      aria-label={`Select ${test.name}`}
                      checked={selectedIds.has(test.id)}
                      disabled={running || !test.enabled}
                      onChange={(event) => {
                        const checked = event.target.checked;
                        setSelectedIds((current) => {
                          const next = new Set(current);
                          if (checked) next.add(test.id);
                          else next.delete(test.id);
                          return next;
                        });
                      }}
                    />
                  </label>
                  <label className="test-toggle">
                    <input
                      type="checkbox"
                      aria-label={`Enable ${test.name}`}
                      checked={test.enabled}
                      disabled={running}
                      onChange={() => onToggle(test.id)}
                    />
                    <span>
                      <strong>{test.name}</strong>
                      <small>
                        /{test.pattern}/{test.flags.join("")} ·{" "}
                        {expectationSummary(test.expectation)} ·{" "}
                        {test.subject.length.toLocaleString()} UTF-16 units
                        {test.generation
                          ? ` · generated · seed ${test.generation.seed}`
                          : ""}
                      </small>
                    </span>
                  </label>
                  <span
                    className={`test-result ${
                      result ? (result.passed ? "is-pass" : "is-fail") : ""
                    }`}
                    role="status"
                  >
                    {result
                      ? `${result.passed ? "Pass" : "Fail"} — ${result.message}`
                      : "Not run"}
                  </span>
                  <div className="test-row-actions">
                    <button
                      type="button"
                      className="secondary-button"
                      aria-label={`Edit ${test.name}`}
                      disabled={running}
                      onClick={() => editTest(test)}
                    >
                      Edit
                    </button>
                    <button
                      type="button"
                      className="secondary-button"
                      aria-label={`Clone ${test.name}`}
                      disabled={running || maximumReached}
                      onClick={() => {
                        setSuiteError(undefined);
                        setSuiteNotice(undefined);
                        try {
                          onClone(test.id);
                        } catch (error) {
                          setSuiteError(
                            error instanceof Error
                              ? error.message
                              : String(error),
                          );
                        }
                      }}
                    >
                      Clone
                    </button>
                    <button
                      type="button"
                      className="icon-button"
                      aria-label={`Remove ${test.name}`}
                      disabled={running}
                      onClick={() => onRemove(test.id)}
                    >
                      ×
                    </button>
                  </div>
                </li>
              );
            })}
          </ul>
        )}
      </section>
    </div>
  );
}
