import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";

/**
 * Accessibilité des vues RENDUES.
 *
 * Le test statique (apps/web/test/a11y.test.js) analyse le HTML livré : pour le
 * dashboard, ce n'est qu'une coquille vide, puisque tout est construit en
 * JavaScript. C'est donc ici, dans un vrai navigateur après rendu, que
 * l'accessibilité du dashboard est réellement vérifiée — contraste des couleurs
 * compris, ce que jsdom ne sait pas calculer.
 */

const VIEWS = [
  ["pilotage", "Pilotage"],
  ["bands", "Groupes"],
  ["locations", "Localisations"],
  ["activity", "Journal"],
  ["llm", "LLM & coûts"],
];

async function analyse(page) {
  return new AxeBuilder({ page })
    .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
    .analyze();
}

function describeViolations(results) {
  return results.violations.map((v) =>
    `[${v.impact}] ${v.id}: ${v.help}\n    ${v.nodes.map((n) => n.target.join(" ")).join("\n    ")}`);
}

test.describe("Accessibilité des vues", () => {
  for (const [view, title] of VIEWS) {
    test(`${title} — aucune violation critique ou sérieuse`, async ({ page }) => {
      await page.goto("/");
      await page.click(`.nav a[data-view="${view}"]`);
      await expect(page.locator(".view-head h1")).toHaveText(title);
      // Laisse les chargements asynchrones peindre leur contenu.
      await page.waitForLoadState("networkidle");

      const results = await analyse(page);
      const blocking = results.violations.filter((v) => ["critical", "serious"].includes(v.impact));
      expect(describeViolations({ violations: blocking }), describeViolations({ violations: blocking }).join("\n")).toEqual([]);
    });
  }

  test("l'écran de connexion est accessible", async ({ page }) => {
    await page.context().clearCookies();
    await page.goto("/");
    await expect(page.locator("#login-screen")).toBeVisible();
    const results = await analyse(page);
    const blocking = results.violations.filter((v) => ["critical", "serious"].includes(v.impact));
    expect(describeViolations({ violations: blocking }), describeViolations({ violations: blocking }).join("\n")).toEqual([]);
  });

  test("la fiche d'un groupe reste accessible une fois ouverte", async ({ page }) => {
    await page.goto("/");
    await page.click('.nav a[data-view="bands"]');
    await page.locator('tr[data-ma-id="1"]').click();
    await expect(page.locator(".modal-backdrop .modal")).toBeVisible();

    const results = await analyse(page);
    const blocking = results.violations.filter((v) => ["critical", "serious"].includes(v.impact));
    expect(describeViolations({ violations: blocking }), describeViolations({ violations: blocking }).join("\n")).toEqual([]);
  });
});

test.describe("Navigation au clavier", () => {
  test("on atteint la navigation puis le contenu à la tabulation", async ({ page }) => {
    await page.goto("/");
    await page.keyboard.press("Tab");
    const firstFocus = await page.evaluate(() => document.activeElement?.getAttribute("data-view"));
    expect(firstFocus).toBe("pilotage");
  });

  test("le focus est piégé dans la fiche ouverte", async ({ page }) => {
    await page.goto("/");
    await page.click('.nav a[data-view="bands"]');
    await page.locator('tr[data-ma-id="1"]').click();
    await expect(page.locator(".modal-backdrop")).toBeVisible();

    // Vingt tabulations doivent toutes rester à l'intérieur de la modale :
    // sinon le focus repart derrière le voile, invisible pour l'utilisateur.
    for (let i = 0; i < 20; i++) await page.keyboard.press("Tab");
    const inside = await page.evaluate(() => !!document.activeElement?.closest(".modal-backdrop"));
    expect(inside).toBe(true);
  });
});
