const { test, expect } = require("@playwright/test");
const AxeBuilder = require("@axe-core/playwright").default;

async function loginAsAdmin(page) {
  await page.goto("/auth/localhost");
  await page.getByLabel(/username/i).fill("admin");
  await page.getByLabel(/password/i).fill("admin");
  await Promise.all([
    page.waitForURL(/\/$|\/admin/),
    page.getByRole("button", { name: /login locally/i }).click()
  ]);
}

async function expectNoSeriousAxeViolations(page) {
  const result = await new AxeBuilder({ page }).analyze();
  const blocking = result.violations.filter((item) => ["serious", "critical"].includes(item.impact));
  expect(blocking, blocking.map((item) => `${item.id}: ${item.help}`).join("\n")).toEqual([]);
}

async function clickSidebarLink(page, href) {
  const link = page.locator(`.nav-link[href="${href}"]`);
  if (!(await link.isVisible())) {
    await link.locator("xpath=ancestor::details/summary").click();
  }
  await Promise.all([
    page.waitForNavigation({ waitUntil: "load" }),
    link.click()
  ]);
}

test("public shell renders without page errors or serious accessibility violations", async ({ page }, testInfo) => {
  const errors = [];
  page.on("pageerror", (error) => errors.push(error.message));
  await page.goto("/");
  await expect(page.locator("main.content")).toBeVisible();
  await expect(page.getByRole("heading", { level: 1 })).toBeVisible();
  await expectNoSeriousAxeViolations(page);
  await page.screenshot({ path: testInfo.outputPath("home-baseline.png"), fullPage: true });
  expect(errors).toEqual([]);
});

test("mobile navigation is a focus-contained drawer and restores its opener", async ({ page }) => {
  test.skip((page.viewportSize()?.width || 999) > 900, "Mobile drawer contract applies below 900px.");
  await page.goto("/");
  const opener = page.locator(".mobile-topbar [data-sidebar-toggle]");
  await opener.click();
  await expect(opener).toHaveAttribute("aria-expanded", "true");
  await expect(page.locator("#lumi-sidebar")).toHaveAttribute("aria-modal", "true");
  await page.keyboard.press("Escape");
  await expect(opener).toHaveAttribute("aria-expanded", "false");
  await expect(opener).toBeFocused();
});

test("live events use one connection and release it across repeated navigation", async ({ page }) => {
  test.skip((page.viewportSize()?.width || 0) <= 900, "Desktop navigation stress coverage exercises the same event lifecycle.");
  await page.addInitScript(() => {
    const NativeEventSource = window.EventSource;
    window.__lumiEventSourceUrls = [];
    window.EventSource = new Proxy(NativeEventSource, {
      construct(Target, args) {
        window.__lumiEventSourceUrls.push(new URL(args[0], window.location.href).pathname);
        return Reflect.construct(Target, args);
      }
    });
  });
  await loginAsAdmin(page);
  await clickSidebarLink(page, "/admin/updates");
  await expect.poll(() => page.evaluate(() => window.__lumiEventSourceUrls)).toEqual(["/api/events"]);

  const routes = ["/commands", "/feedback", "/admin", "/admin/navigation", "/admin/settings", "/"];
  for (let cycle = 0; cycle < 3; cycle += 1) {
    for (const route of routes) {
      await clickSidebarLink(page, route);
      await expect.poll(() => page.evaluate(() => window.__lumiEventSourceUrls)).toEqual(["/api/events"]);
    }
  }
});

test("updates reconcile checks and cleanup settings without document reloads", async ({ page }) => {
  await loginAsAdmin(page);
  let snapshotSaveAttempts = 0;
  await page.route("**/admin/updates/core/check", async (route) => {
    if (route.request().method() !== "POST") return route.continue();
    const core = {
      current_version: "0.2.24",
      safe_target_version: "0.2.25",
      latest_available_version: "0.2.25",
      source_branch: "main",
      update_available: true,
      blocked: false,
      version_description: "A targeted browser-test update is available."
    };
    await route.fulfill({
      status: 200,
      contentType: "application/json",
      body: JSON.stringify({
        ok: true,
        message: "Core version 0.2.25 is available.",
        status: { core },
        data: { status: { core } },
        patch: {},
        reloadRequired: false
      })
    });
  });
  await page.route("**/admin/updates/snapshots/settings", async (route) => {
    if (route.request().method() !== "POST" || snapshotSaveAttempts++ > 0) return route.continue();
    await route.fulfill({
      status: 422,
      contentType: "application/json",
      body: JSON.stringify({
        ok: false,
        message: "Correct the snapshot cleanup limits and try again.",
        fieldErrors: { max_age_days: "Enter a whole number from 1 to 3650." },
        data: {},
        patch: {},
        reloadRequired: false
      })
    });
  });
  await page.goto("/admin/updates");
  await expect(page.locator("[data-update-management]")).toBeVisible();
  const deployment = page.locator("[data-deployment-panel]");
  await expect(deployment.getByRole("heading", { name: "Running code branch" })).toBeVisible();
  await expect(deployment.locator("select[name='branch']")).toContainText("Stable · main");
  await expect(deployment.getByRole("button", { name: "Deploy selected branch" })).toBeVisible();
  const navigationCount = await page.evaluate(() => performance.getEntriesByType("navigation").length);
  const core = page.locator('[data-update-key="core"]');
  await core.locator("summary").first().click();
  const [checkResponse] = await Promise.all([
    page.waitForResponse((response) => new URL(response.url()).pathname === "/admin/updates/core/check"),
    core.getByRole("button", { name: "Check for updates" }).click()
  ]);
  expect(checkResponse.headers()["content-type"]).toContain("application/json");
  await expect(core.locator("[data-update-summary]")).toContainText("0.2.25");
  await expect(core.locator("[data-update-badge]")).toHaveText("Update available");
  await expect(core.locator("[data-update-inline-result]")).toContainText("0.2.25");
  expect(await page.evaluate(() => performance.getEntriesByType("navigation").length)).toBe(navigationCount);

  const settings = page.locator("[data-update-settings-form]");
  const age = settings.locator("input[name='max_age_days']");
  const count = settings.locator("input[name='max_per_target']");
  const originalAge = await age.inputValue();
  const originalCount = await count.inputValue();
  const [invalidResponse] = await Promise.all([
    page.waitForResponse((response) => new URL(response.url()).pathname === "/admin/updates/snapshots/settings"),
    settings.getByRole("button", { name: "Save cleanup policy" }).click()
  ]);
  expect(invalidResponse.status()).toBe(422);
  await expect(age).toHaveAttribute("aria-invalid", "true");
  await expect(settings.locator("[data-update-inline-result]")).toContainText("Correct the snapshot cleanup limits");
  await age.fill(originalAge);
  await count.fill(originalCount);
  const [saveResponse] = await Promise.all([
    page.waitForResponse((response) => new URL(response.url()).pathname === "/admin/updates/snapshots/settings" && response.status() === 200),
    settings.getByRole("button", { name: "Save cleanup policy" }).click()
  ]);
  expect(saveResponse.headers()["content-type"]).toContain("application/json");
  await expect(settings.locator("[data-update-inline-result]")).toContainText("Snapshot cleanup now keeps");
  expect(await page.evaluate(() => performance.getEntriesByType("navigation").length)).toBe(navigationCount);

  const reflow = await page.evaluate(() => ({ viewport: document.documentElement.clientWidth, documentWidth: document.documentElement.scrollWidth }));
  expect(reflow.documentWidth).toBeLessThanOrEqual(reflow.viewport + 1);
  await expectNoSeriousAxeViolations(page);
});

test("custom commands and policies save targeted state without document reloads", async ({ page }) => {
  await loginAsAdmin(page);
  await page.goto("/commands");
  const triggerHeader = page.locator("th[data-sort='trigger']");
  await triggerHeader.getByRole("button", { name: "Trigger" }).focus();
  await page.keyboard.press("Enter");
  await expect(triggerHeader).toHaveAttribute("aria-sort", "ascending");
  await expectNoSeriousAxeViolations(page);
  await page.goto("/admin/commands");
  const createForm = page.locator("[data-command-create-form]");
  const mode = createForm.locator("select[name='mode']");
  await mode.selectOption("random");
  await expect(createForm.locator(".js-field-random")).toBeVisible();
  await expect(createForm.locator(".js-field-response").locator("xpath=ancestor::div[contains(@class,'field')]")).toBeHidden();
  await mode.selectOption("plain");

  const trigger = `uxcommand${Date.now()}`;
  await createForm.locator("input[name='trigger']").fill(trigger);
  await createForm.locator("input[name='response']").fill("A targeted command response");
  const commandNavigationCount = await page.evaluate(() => performance.getEntriesByType("navigation").length);
  const [createResponse] = await Promise.all([
    page.waitForResponse((response) => new URL(response.url()).pathname === "/admin/commands" && response.request().method() === "POST"),
    createForm.getByRole("button", { name: "Create command" }).click({ timeout: 5000 })
  ]);
  expect(createResponse.headers()["content-type"]).toContain("application/json");
  await expect(page.locator("[data-command-list-section]")).toContainText(trigger);
  await expect(page.locator("[data-command-management-status]")).toContainText("Command created");
  expect(await page.evaluate(() => performance.getEntriesByType("navigation").length)).toBe(commandNavigationCount);
  await expect(createForm.locator("input[name='trigger']")).toHaveValue("");
  expect(await createForm.evaluate((form) => Array.from(form.elements)
    .filter((field) => typeof field.checkValidity === "function" && !field.checkValidity())
    .map((field) => ({ name: field.name, message: field.validationMessage })))).toEqual([]);
  const [invalidResponse] = await Promise.all([
    page.waitForResponse((response) => new URL(response.url()).pathname === "/admin/commands" && response.request().method() === "POST"),
    createForm.evaluate((form) => form.requestSubmit(form.querySelector("button[type='submit']")))
  ]);
  expect(invalidResponse.status()).toBe(422);
  await expect(createForm.locator("input[name='trigger']")).toHaveAttribute("aria-invalid", "true");
  await expect(createForm.locator("[data-lumi-form-status]")).toContainText("Trigger is required");
  expect(await page.evaluate(() => performance.getEntriesByType("navigation").length)).toBe(commandNavigationCount);
  const commandReflow = await page.evaluate(() => ({ viewport: document.documentElement.clientWidth, documentWidth: document.documentElement.scrollWidth }));
  expect(commandReflow.documentWidth).toBeLessThanOrEqual(commandReflow.viewport + 1);

  await page.goto("/admin/command-policies");
  const commandItem = page.locator('[data-policy-search-scope="commands"] [data-policy-item]', { hasText: `!${trigger}` });
  await expect(commandItem).toHaveCount(1);
  await commandItem.locator("summary").click();
  await commandItem.locator("select[name$='window_mode']").selectOption("custom");
  await commandItem.locator("input[name$='window_uses']").fill("2");
  const policyNavigationCount = await page.evaluate(() => performance.getEntriesByType("navigation").length);
  const [policyResponse] = await Promise.all([
    page.waitForResponse((response) => new URL(response.url()).pathname === "/admin/command-policies/commands"),
    page.locator(".command-policy-savebar").first().getByRole("button", { name: "Save all command settings" }).click()
  ]);
  expect(policyResponse.headers()["content-type"]).toContain("application/json");
  await expect(page.locator("[data-command-save-state]")).toContainText("saved");
  expect(await page.evaluate(() => performance.getEntriesByType("navigation").length)).toBe(policyNavigationCount);

  const reflow = await page.evaluate(() => ({ viewport: document.documentElement.clientWidth, documentWidth: document.documentElement.scrollWidth }));
  expect(reflow.documentWidth).toBeLessThanOrEqual(reflow.viewport + 1);
  await expectNoSeriousAxeViolations(page);
});

test("enhanced settings save preserves the document, focus, and viewport", async ({ page }) => {
  await loginAsAdmin(page);
  await page.goto("/admin/settings");
  const title = page.locator("input[name='site_title']");
  await title.focus();
  const navigationCount = await page.evaluate(() => performance.getEntriesByType("navigation").length);
  const scrollBefore = await page.evaluate(() => window.scrollY);
  await title.fill(`Lumi UI Test ${Date.now()}`);
  await page.getByRole("button", { name: "Save changes" }).click();
  await expect(page.getByText(/Section saved|Settings saved/).first()).toBeVisible();
  expect(await page.evaluate(() => performance.getEntriesByType("navigation").length)).toBe(navigationCount);
  expect(Math.abs((await page.evaluate(() => window.scrollY)) - scrollBefore)).toBeLessThan(3);
  await expect(title).toBeFocused();
  await expectNoSeriousAxeViolations(page);
});

test("overlay editor keeps settings scrollable and synchronizes interactive previews", async ({ page }) => {
  await loginAsAdmin(page);
  await page.goto("/admin/overlays");
  await page.getByRole("button", { name: "Create overlay" }).click();
  const createName = page.locator("[data-overlay-create-modal] input[name='name']");
  const overlayName = `UX overlay ${Date.now()}`;
  await expect(createName).toBeFocused();
  await createName.fill(overlayName);
  await Promise.all([
    page.waitForURL(/\/admin\/overlays/),
    page.locator("[data-overlay-create-modal]").getByRole("button", { name: /create|add/i }).click()
  ]);
  await page.locator("tbody tr", { hasText: overlayName }).getByRole("link", { name: "Open editor" }).click();
  const viewportWidth = page.viewportSize()?.width || 0;
  const preview = page.locator("[data-overlay-editor]");
  const previewLauncher = page.locator("[data-preview-drawer-toggle]");
  if (viewportWidth >= 1180) {
    await expect(preview).toBeVisible();
  } else {
    await expect(preview).toBeHidden();
    await page.mouse.move((page.viewportSize()?.width || 320) / 2, Math.min(420, (page.viewportSize()?.height || 720) / 2));
    await page.mouse.wheel(0, 480);
    await expect.poll(() => page.evaluate(() => window.scrollY)).toBeGreaterThan(0);
    await page.evaluate(() => window.scrollTo(0, 0));
    const launcherGeometry = await previewLauncher.evaluate((button) => {
      const rect = button.getBoundingClientRect();
      return {
        top: rect.top,
        bottom: rect.bottom,
        viewportHeight: window.innerHeight
      };
    });
    expect(launcherGeometry.top).toBeGreaterThanOrEqual(0);
    expect(launcherGeometry.bottom).toBeLessThanOrEqual(launcherGeometry.viewportHeight);
    await previewLauncher.click();
    await expect(preview).toBeVisible();
    await expect(previewLauncher).toHaveAttribute("aria-expanded", "true");
    await expect(preview).toHaveCSS("overflow-y", "auto");
  }

  const previewGeometry = await preview.evaluate((panel) => {
    const panelRect = panel.getBoundingClientRect();
    const frameRect = panel.querySelector("[data-preview-frame]").getBoundingClientRect();
    return {
      panelTop: panelRect.top,
      panelBottom: panelRect.bottom,
      viewportHeight: window.innerHeight,
      frameRatio: frameRect.width / frameRect.height,
      stageHeight: panel.querySelector("[data-preview-stage]").getBoundingClientRect().height,
      frameHeight: frameRect.height,
      panelHeight: panelRect.height
    };
  });
  expect(previewGeometry.panelTop).toBeGreaterThanOrEqual(-1);
  expect(previewGeometry.panelBottom).toBeLessThanOrEqual(previewGeometry.viewportHeight + 1);
  expect(previewGeometry.frameRatio).toBeCloseTo(16 / 9, 2);
  expect(Math.abs(previewGeometry.stageHeight - previewGeometry.frameHeight)).toBeLessThanOrEqual(2);
  if (viewportWidth >= 1180) expect(previewGeometry.panelHeight).toBeLessThan(previewGeometry.viewportHeight * 0.9);

  if (viewportWidth < 1180) {
    await preview.getByRole("button", { name: "Close scene preview" }).click();
    await expect(preview).toBeHidden();
    await expect(previewLauncher).toBeFocused();
  }

  const settingsColumn = page.locator(".overlay-settings-column");
  const finalSettingsCard = page.locator(".overlay-settings-column > .card").last();
  const finalDisclosure = finalSettingsCard.locator("details").first();
  if (viewportWidth < 1180) {
    await finalDisclosure.locator("summary").evaluate((item) => item.scrollIntoView({ block: "center", behavior: "instant" }));
  }
  await finalDisclosure.locator("summary").click();
  const scrollContract = await settingsColumn.evaluate((item) => ({
    overflowY: getComputedStyle(item).overflowY,
    maxHeight: getComputedStyle(item).maxHeight,
    legacyHeight: item.style.getPropertyValue("--overlay-settings-max-height"),
    clientHeight: item.clientHeight,
    scrollHeight: item.scrollHeight
  }));
  expect(scrollContract.legacyHeight).toBe("");
  if (viewportWidth >= 1180) {
    expect(scrollContract.overflowY).toBe("auto");
    expect(scrollContract.scrollHeight).toBeGreaterThan(scrollContract.clientHeight);
    await settingsColumn.evaluate((item) => { item.scrollTop = item.scrollHeight; });
    await expect.poll(() => settingsColumn.evaluate((item) => item.scrollTop)).toBeGreaterThan(0);
  } else {
    expect(["visible", "clip"]).toContain(scrollContract.overflowY);
  }

  const finalSettingsAction = finalSettingsCard.locator("button").last();
  await finalSettingsAction.scrollIntoViewIfNeeded();
  await expect(finalSettingsAction).toBeVisible();
  const finalActionGeometry = await finalSettingsAction.evaluate((button) => {
    const rect = button.getBoundingClientRect();
    return { top: rect.top, bottom: rect.bottom, viewportHeight: window.innerHeight };
  });
  expect(finalActionGeometry.top).toBeGreaterThanOrEqual(-1);
  expect(finalActionGeometry.bottom).toBeLessThanOrEqual(finalActionGeometry.viewportHeight + 1);
  if (viewportWidth >= 1180) {
    const stickyGeometry = await preview.evaluate((panel) => {
      const rect = panel.getBoundingClientRect();
      return { top: rect.top, bottom: rect.bottom, viewportHeight: window.innerHeight };
    });
    expect(stickyGeometry.top).toBeGreaterThanOrEqual(-1);
    expect(stickyGeometry.bottom).toBeLessThanOrEqual(stickyGeometry.viewportHeight + 1);

    const addSource = page.locator(".overlay-add-source").first();
    await addSource.scrollIntoViewIfNeeded();
    await addSource.locator("summary").click();
    await addSource.locator("input[name='name']").fill("Pop-out adjustment source");
    await Promise.all([
      page.waitForNavigation({ waitUntil: "load" }),
      addSource.getByRole("button", { name: "Add source" }).click()
    ]);
    await expect(page.locator("[data-overlay-editor]")).toBeVisible();

    const [popup] = await Promise.all([
      page.waitForEvent("popup"),
      preview.getByRole("button", { name: "Pop out" }).click()
    ]);
    await popup.waitForLoadState("domcontentloaded");
    await popup.setViewportSize({ width: 900, height: 500 });
    const popoutFrame = popup.locator("[data-preview-window-frame]");
    await expect(popoutFrame).toBeVisible();
    await expect.poll(() => popoutFrame.evaluate((frame) => {
      const rect = frame.getBoundingClientRect();
      return rect.right <= window.innerWidth + 1 && rect.bottom <= window.innerHeight + 1;
    })).toBe(true);
    const popoutGeometry = await popoutFrame.evaluate((frame) => {
      const rect = frame.getBoundingClientRect();
      return {
        top: rect.top,
        right: rect.right,
        bottom: rect.bottom,
        left: rect.left,
        ratio: rect.width / rect.height,
        viewportWidth: window.innerWidth,
        viewportHeight: window.innerHeight
      };
    });
    expect(popoutGeometry.top).toBeGreaterThanOrEqual(-1);
    expect(popoutGeometry.left).toBeGreaterThanOrEqual(-1);
    expect(popoutGeometry.right).toBeLessThanOrEqual(popoutGeometry.viewportWidth + 1);
    expect(popoutGeometry.bottom).toBeLessThanOrEqual(popoutGeometry.viewportHeight + 1);
    expect(popoutGeometry.ratio).toBeCloseTo(16 / 9, 2);
    await expect(popup.locator("[data-popout-source]")).toHaveValue(/.+/);
    await expect(popup.locator(".lumi-overlay-module.is-selected")).toHaveCount(1);
    const mainX = page.locator("[data-module-editor][data-module-id] input[name='x']").first();
    await expect(mainX).toHaveValue("0");
    const transformResponse = popup.waitForResponse((response) => /\/api\/admin\/overlays\/[^/]+\/modules\/[^/]+\/transform$/.test(new URL(response.url()).pathname));
    await popup.locator("[data-preview-window-canvas]").focus();
    await popup.keyboard.press("ArrowRight");
    expect((await transformResponse).ok()).toBe(true);
    await expect(popup.locator("[data-preview-window-status]")).toContainText("Saved");
    await expect(mainX).toHaveValue("0.1");
    await popup.close();
  }

  const navigationCount = await page.evaluate(() => performance.getEntriesByType("navigation").length);
  await page.getByText("Overlay name and canvas size", { exact: true }).click();
  const name = page.locator("form[data-lumi-enhanced-form] input[name='name']").first();
  await name.fill(`${await name.inputValue()} edited`);
  await name.locator("xpath=ancestor::form").getByRole("button", { name: /save overlay settings/i }).click();
  await expect(name.locator("xpath=ancestor::form").locator("[data-lumi-form-status]")).toContainText("Overlay saved");
  expect(await page.evaluate(() => performance.getEntriesByType("navigation").length)).toBe(navigationCount);
  await expectNoSeriousAxeViolations(page);
});

test("resource storage limits save, reconcile immediately, and persist", async ({ page }) => {
  await loginAsAdmin(page);
  await page.goto("/admin/resources");
  const settings = page.locator(".resource-settings-panel");
  await settings.locator("summary").click();
  const form = settings.locator("[data-resource-settings-form]");
  await form.locator("[name='limit_gib']").fill("3.5");
  await form.locator("[name='reserve_gib']").fill("0.3");
  await form.locator("[name='max_file_mib']").fill("24");
  await form.locator("[name='max_files']").fill("6");
  const [response] = await Promise.all([
    page.waitForResponse((candidate) => new URL(candidate.url()).pathname === "/admin/resources/settings"),
    form.getByRole("button", { name: "Save storage settings" }).click()
  ]);
  expect(response.ok()).toBe(true);
  const payload = await response.json();
  expect(payload.settings).toEqual({ limit_gib: 3.5, reserve_gib: 0.3, max_file_mib: 24, max_files: 6 });
  await expect(form.locator("[data-resource-settings-status]")).toContainText("saved");
  await expect(page.locator("[data-storage-limit]")).toContainText("3.50 GB");
  await expect(page.locator("[data-storage-max-file]")).toContainText("24.0 MB");
  const fileInput = page.locator("[data-resource-file-input]");
  await expect(fileInput).toHaveAttribute("data-max-files", "6");
  await expect(fileInput).toHaveAttribute("data-max-file-bytes", String(24 * 1024 * 1024));

  await page.reload();
  await page.locator(".resource-settings-panel summary").click();
  await expect(page.locator("[data-resource-settings-form] [name='limit_gib']")).toHaveValue("3.5");
  await expect(page.locator("[data-resource-settings-form] [name='reserve_gib']")).toHaveValue("0.3");
  await expect(page.locator("[data-resource-settings-form] [name='max_file_mib']")).toHaveValue("24");
  await expect(page.locator("[data-resource-settings-form] [name='max_files']")).toHaveValue("6");
  await expectNoSeriousAxeViolations(page);
});

test("navigation builder supports keyboard movement and saves without navigation", async ({ page }) => {
  await loginAsAdmin(page);
  await page.goto("/admin/navigation");
  await expect(page.locator("[data-navigation-builder]" )).toBeVisible();
  const navigationCount = await page.evaluate(() => performance.getEntriesByType("navigation").length);

  const firstItem = page.locator("[data-nav-section-card] .nav-dd-item").first();
  const navId = await firstItem.getAttribute("data-nav-id");
  const movedItem = page.locator(`.nav-dd-item[data-nav-id="${navId}"]`);
  const originalGroup = await movedItem.locator("xpath=parent::*").getAttribute("data-group-label");
  await movedItem.getByRole("button", { name: /to the next group/i }).click();
  const movedGroup = await movedItem.locator("xpath=parent::*").getAttribute("data-group-label");
  expect(movedGroup).not.toBe(originalGroup);

  await page.getByRole("button", { name: "Add section" }).click();
  const newLabel = page.locator("[data-nav-section-card] .nav-section-label").last();
  await expect(newLabel).toBeFocused();
  await newLabel.fill("Automation");
  await page.getByRole("button", { name: "Save navigation" }).click();
  await expect(page.locator("[data-nav-builder-status]")).toContainText("Navigation saved");
  expect(await page.evaluate(() => performance.getEntriesByType("navigation").length)).toBe(navigationCount);
  await expect(page.locator("[data-nav-dirty-badge]")).toBeHidden();

  const reflow = await page.evaluate(() => ({
    viewport: document.documentElement.clientWidth,
    documentWidth: document.documentElement.scrollWidth
  }));
  expect(reflow.documentWidth).toBeLessThanOrEqual(reflow.viewport + 1);
  await expectNoSeriousAxeViolations(page);
});

test("theme draft preview survives a targeted custom-theme save", async ({ page }) => {
  await loginAsAdmin(page);
  await page.goto("/admin/theming");
  const sourceTheme = page.locator(".theme-card").first();
  await sourceTheme.locator("summary", { hasText: "More actions" }).click();
  const copyName = `UX Theme ${Date.now()}`;
  await sourceTheme.locator("form[action='/admin/theming/duplicate'] input[name='name']").fill(copyName);
  await Promise.all([
    page.waitForURL(/\/admin\/theming\?edit=.*#theme-editor/),
    sourceTheme.getByRole("button", { name: "Duplicate" }).click()
  ]);

  const form = page.locator("[data-theme-form]");
  await expect(form).toBeVisible();
  const accent = form.locator("input[name='light_accent']");
  const nextAccent = (await accent.inputValue()).toLowerCase() === "#176b76" ? "#176b75" : "#176b76";
  await accent.fill(nextAccent);
  const navigationCount = await page.evaluate(() => performance.getEntriesByType("navigation").length);
  const [saveResponse] = await Promise.all([
    page.waitForResponse((response) => /\/admin\/theming\/custom\/[^/]+\/save$/.test(new URL(response.url()).pathname)),
    form.getByRole("button", { name: "Save theme" }).click()
  ]);
  expect(saveResponse.headers()["content-type"]).toContain("application/json");
  expect((await saveResponse.json()).message).toContain(`${copyName} saved`);
  await expect(form.locator("[data-lumi-form-status]")).toContainText(`${copyName} saved`);
  expect(await page.evaluate(() => performance.getEntriesByType("navigation").length)).toBe(navigationCount);
  await expect(form.getByRole("button", { name: "Saved", exact: true })).toBeVisible();
  await expectNoSeriousAxeViolations(page);
});
