// Phase 1 smoke tests. Frontend-only — no Tauri IPC.
// Visual screenshots are saved as artifacts; they do NOT fail the build at
// this stage (see playwright.config.ts header). Promotion to failing visual
// diffs happens after Phase 7 stabilises the UI.
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";

const ZONES = ["cave", "energy", "reset"] as const;
const THEMES = ["light", "dark"] as const;

test.describe("Lumotia frontend smoke (no Tauri IPC)", () => {
  test("app loads without Tauri runtime", async ({ page }) => {
    // Suppress noisy invoke-rejected promises from the layout's onMount;
    // they are expected without Tauri. We only fail on unhandled crashes.
    const fatalErrors: string[] = [];
    page.on("pageerror", (err) => fatalErrors.push(err.message));

    await page.goto("/");
    await expect(page.locator("main")).toBeVisible();
    expect(fatalErrors).toEqual([]);
  });

  test("keyboard navigation reaches main + sidebar surface", async ({ page }) => {
    await page.goto("/");
    await page.locator("main").waitFor({ state: "visible" });
    // Tab once and confirm focus moves to a focusable element somewhere in
    // the document, not nothing. We don't assert the specific element to
    // keep the test robust through chrome migration.
    await page.keyboard.press("Tab");
    const focused = await page.evaluate(() =>
      document.activeElement && document.activeElement !== document.body
        ? document.activeElement.tagName
        : null,
    );
    expect(focused).not.toBeNull();
  });

  test("axe scan on / has no critical structural violations", async ({ page }) => {
    await page.goto("/");
    await page.locator("main").waitFor({ state: "visible" });
    const results = await new AxeBuilder({ page })
      .disableRules([
        // svelte-i18n initialises asynchronously; <html lang> is set after
        // first paint in browser-preview mode.
        "html-has-lang",
        // Colour-contrast is owned by docs/release/v0.1-contrast-audit.md
        // and gets re-enabled in Phase 7 after the wrapper palette settles.
        // Phase 1's smoke baseline focuses on structural/semantic a11y.
        "color-contrast",
      ])
      .analyze();
    const criticals = results.violations.filter(
      (v) => v.impact === "critical" || v.impact === "serious",
    );
    expect(criticals).toEqual([]);
  });

  for (const theme of THEMES) {
    test(`theme smoke — ${theme}`, async ({ page }, testInfo) => {
      await page.goto("/");
      await page.locator("main").waitFor({ state: "visible" });
      // Apply theme without depending on the preferences store, which
      // needs Tauri persistence. The data-theme attribute is what the CSS
      // tokens key off; the preferences store also writes this attribute
      // in production.
      await page.evaluate((t) => {
        document.documentElement.dataset.theme = t;
      }, theme);
      await page.waitForTimeout(50);
      await page.screenshot({
        path: `test-results/theme-${theme}-${testInfo.project.name}.png`,
        fullPage: false,
      });
    });
  }

  for (const zone of ZONES) {
    test(`sensory zone smoke — ${zone}`, async ({ page }, testInfo) => {
      await page.goto("/");
      await page.locator("main").waitFor({ state: "visible" });
      await page.evaluate((z) => {
        document.documentElement.dataset.zone = z;
      }, zone);
      await page.waitForTimeout(50);
      await page.screenshot({
        path: `test-results/zone-${zone}-${testInfo.project.name}.png`,
        fullPage: false,
      });
    });
  }
});
