import { expect, test, type Page } from "@playwright/test";

async function setEditor(page: Page, testId: string, value: string) {
  await page.getByTestId(testId).locator(".cm-content").fill(value);
}

async function waitForReady(page: Page, timeout = 15_000) {
  await expect(page.locator(".run-status")).toHaveClass(/status-ready/u, {
    timeout,
  });
}

test("CPython re executes and replaces with code-point-native offsets under the production CSP", async ({
  page,
}) => {
  const pageErrors: string[] = [];
  const consoleErrors: string[] = [];
  page.on("pageerror", (error) => pageErrors.push(error.message));
  page.on("console", (message) => {
    if (message.type() === "error") consoleErrors.push(message.text());
  });

  const response = await page.goto("/deep/nested/regex/");
  expect(response?.headers()["content-security-policy"]).toContain(
    "script-src 'self' 'wasm-unsafe-eval'",
  );
  expect(response?.headers()["content-security-policy"]).not.toContain(
    "'unsafe-eval'",
  );
  await waitForReady(page);
  await page.getByLabel("Live").uncheck();
  await page.getByRole("combobox", { name: "Flavour" }).selectOption("python");
  await setEditor(page, "editor-regular-expression-pattern", "(?P<word>\\w+)");
  await setEditor(page, "editor-test-text", "😀 café");
  await page.getByRole("button", { name: "Run", exact: true }).click();
  await waitForReady(page, 45_000);

  await expect(page.locator(".run-status")).toContainText(
    "native offsets code-point",
  );
  const match = page.locator(".extraction-row").first();
  await expect(match).toContainText("UTF-16 range: 3…7");
  await expect(match).toContainText("native range: 2…6 code-point");

  await page.getByRole("button", { name: "Replace", exact: true }).click();
  await setEditor(page, "editor-replacement-template", "[\\g<word>]");
  await page.getByRole("button", { name: "Run", exact: true }).click();
  await waitForReady(page, 45_000);
  await expect(
    page.getByTestId("editor-replacement-output").locator(".cm-content"),
  ).toHaveText("😀 [café]");

  await page.getByRole("button", { name: "Quick reference" }).click();
  await expect(
    page.getByRole("dialog", { name: "Quick reference" }),
  ).toContainText("Python 3.14 re documentation");
  await page.keyboard.press("Escape");
  await page.getByRole("button", { name: "Capabilities" }).click();
  const capabilities = page.getByRole("dialog", { name: "Capabilities" });
  await expect(capabilities).toContainText("CPython re via Pyodide");
  await expect(capabilities).toContainText("CPython 3.14.2 re");
  expect(pageErrors).toEqual([]);
  expect(consoleErrors).toEqual([]);
});

test("TeaVM java.util.regex executes Java-only syntax and numeric replacement with UTF-16 offsets", async ({
  page,
}) => {
  const pageErrors: string[] = [];
  page.on("pageerror", (error) => pageErrors.push(error.message));
  await page.goto("/deep/nested/regex/");
  await waitForReady(page);
  await page.getByLabel("Live").uncheck();
  await page.getByRole("combobox", { name: "Flavour" }).selectOption("java");
  await setEditor(
    page,
    "editor-regular-expression-pattern",
    "(?<word>[a-z]++)",
  );
  await setEditor(page, "editor-test-text", "😀 abc");
  await page.getByRole("button", { name: "Run", exact: true }).click();
  await waitForReady(page);

  await expect(page.locator(".run-status")).toContainText(
    "native offsets utf16",
  );
  const match = page.locator(".extraction-row").first();
  await expect(match).toContainText("UTF-16 range: 3…6");
  await expect(match).toContainText("native range: 3…6 utf16");

  await page.getByRole("button", { name: "Replace", exact: true }).click();
  await setEditor(page, "editor-replacement-template", "<$1>");
  await page.getByRole("button", { name: "Run", exact: true }).click();
  await waitForReady(page);
  await expect(
    page.getByTestId("editor-replacement-output").locator(".cm-content"),
  ).toHaveText("😀 <abc>");

  await page.getByRole("button", { name: "Capabilities" }).click();
  const capabilities = page.getByRole("dialog", { name: "Capabilities" });
  await expect(capabilities).toContainText("TeaVM java.util.regex");
  await expect(capabilities).toContainText("not OpenJDK");
  expect(pageErrors).toEqual([]);
});

test("serves the self-hosted Python and Java runtime assets with executable MIME types", async ({
  request,
}) => {
  const pythonModule = await request.get("./engines/python/pyodide.asm.mjs");
  expect(pythonModule.ok()).toBe(true);
  expect(pythonModule.headers()["content-type"]).toContain("text/javascript");

  const pythonWasm = await request.get("./engines/python/pyodide.asm.wasm");
  expect(pythonWasm.ok()).toBe(true);
  expect(pythonWasm.headers()["content-type"]).toContain("application/wasm");
  expect((await pythonWasm.body()).subarray(0, 4)).toEqual(
    Buffer.from([0x00, 0x61, 0x73, 0x6d]),
  );

  const javaModule = await request.get("./engines/java/java-regex.mjs");
  expect(javaModule.ok()).toBe(true);
  expect(javaModule.headers()["content-type"]).toContain("text/javascript");
});
