import { expect, test } from '@playwright/test';
import { expectNoA11yViolations } from './support/axe';
import { signIn } from './support/cms';

/**
 * História 22 — Acesso em ponteiro grosso e sem JavaScript (P2.S6/ADR-010/ADR-025).
 *
 * Duas provas que o desktop não dá:
 *
 * 1. **Pixel 7**: usuários, perfis, matriz e perfil próprio não estouram o viewport, mantêm alvo
 *    de toque ≥ 44 px na matriz e passam o axe no viewport real (axe desktop já roda nos specs
 *    19/20/21, no projeto `cms-desktop`).
 * 2. **Sem JS**: a matriz de permissões é um formulário nativo (checkboxes + submit) e a exclusão
 *    usa o Popover nativo — criar e excluir um perfil funciona com `javaScriptEnabled: false`.
 *    O axe exige JS para injetar as regras, então aqui o contrato é estrutural e comportamental.
 *
 * Cada execução cria o próprio perfil (`e2e-mobile-<timestamp>`) e o exclui ao final; nenhum spec
 * depende deste dado e o lote global (`cms-serial`) roda antes por `dependencies`.
 */
test.describe('CMS — Acesso no mobile', () => {
  test('usuários, perfis, matriz e perfil próprio sem overflow e com axe', async ({ page }) => {
    await signIn(page);

    const screens = [
      { path: '/usuarios', heading: 'Usuários' },
      { path: '/papeis', heading: 'Perfis' },
      { path: '/papeis/criar', heading: 'Novo perfil' },
      { path: '/perfil', heading: 'Meu perfil' },
    ];

    for (const screen of screens) {
      await page.goto(screen.path);

      await expect(page.locator('main#cms-content h1')).toHaveText(screen.heading);

      const fits = await page.evaluate(
        () => document.documentElement.scrollWidth <= window.innerWidth + 1,
      );
      expect(fits, `${screen.path} com overflow horizontal`).toBe(true);

      await expectNoA11yViolations(page);
    }

    // A matriz mantém o alvo de toque da casa (2.75rem = 44px) no viewport real.
    await page.goto('/papeis/criar');
    const cell = page.locator('label[for="perm-Noticias-ver"]');
    const box = await cell.boundingBox();
    expect(box, 'célula da matriz sem caixa').not.toBeNull();
    const target = box as NonNullable<typeof box>;
    expect(target.height, 'alvo vertical < 44px').toBeGreaterThanOrEqual(44);
    expect(target.width, 'alvo horizontal < 44px').toBeGreaterThanOrEqual(44);
  });
});

test.describe('CMS — Acesso sem JavaScript', () => {
  test.use({ javaScriptEnabled: false });

  test('a matriz cria e exclui um perfil só com o formulário nativo', async ({ page }) => {
    const stamp = Date.now();
    const name = `Perfil Mobile E2E ${stamp}`;

    await signIn(page);

    await page.goto('/papeis');
    await expect(page.getByRole('table', { name: 'Perfis de acesso' })).toBeVisible();

    await page.getByRole('link', { name: 'Novo perfil' }).click();
    await expect(page).toHaveURL(/\/papeis\/criar$/);
    await expect(page.getByRole('table', { name: 'Matriz de permissões por módulo' })).toBeVisible();

    // Ação não declarada continua presente e desabilitada — sem script para montar a matriz.
    await expect(page.locator('input[value="blueprint.editar"]')).toBeDisabled();

    await page.locator('#field-nome').fill(name);

    // Teclado no controle nativo (sem JS de aplicação): foco no checkbox e Espaço alternam o
    // estado. Evita depender do empilhamento do `<label>` sobre a célula no viewport estreito.
    await page.locator('#perm-Noticias-ver').focus();
    await page.keyboard.press('Space');
    await expect(page.locator('#perm-Noticias-ver')).toBeChecked();

    // Enter no botão focado: submit nativo, sem depender do sticky header após o scroll.
    const save = page.getByRole('button', { name: 'Salvar' });
    await save.focus();
    await page.keyboard.press('Enter');
    await expect(page).toHaveURL(/\/papeis$/);
    await expect(page.getByText('Perfil criado.')).toBeVisible();

    // A lista é paginada por nome e o banco dev acumula execuções: filtra a própria linha.
    await page.goto(`/papeis?q=${encodeURIComponent(name)}`);

    const row = page.locator('tbody tr', { hasText: name });
    await expect(row).toHaveCount(1);

    // O que a jornada promete é a **persistência** da matriz: um perfil sem permissões teria o
    // mesmo URL/flash/linha, então o valor gravado é a asserção que pode falhar.
    await expect(row.locator('td').nth(2)).toHaveText('1');

    // Exclusão pelo Popover nativo (HTML, não JS), operado por teclado (mesmo motivo do sticky
    // header: o botão fica abaixo da busca e o clique por ponteiro é interceptado no scroll).
    const trigger = row.getByRole('button', { name: 'Excluir' });
    const popoverId = await trigger.getAttribute('popovertarget');
    expect(popoverId).toBeTruthy();

    await trigger.focus();
    await page.keyboard.press('Enter');

    const dialog = page.locator(`#${popoverId}`);
    await expect(dialog).toBeVisible();
    const confirm = dialog.getByRole('button', { name: 'Excluir' });
    await confirm.focus();
    await page.keyboard.press('Enter');

    await expect(page).toHaveURL(/\/papeis$/);
    await expect(page.getByText('Perfil excluído.')).toBeVisible();
    await expect(row).toHaveCount(0);
  });
});
