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

/**
 * História 27 — Páginas do site no painel (P5.S3.1): lista + formulário com título por locale,
 * slug único e conteúdo em AST JSON; um slug que colidiria com rota viva é recusado.
 *
 * Pré-condições: `make up` + `php artisan db:seed` (o perfil demo recebe `paginas.*`) +
 * `php artisan migrate` (tabela `paginas.pages`) + assets compilados.
 *
 * Dados próprios: cada execução cria a sua página com um endereço único (projeto + timestamp), então
 * o spec é independente entre `cms-desktop` e `cms-mobile` e entre execuções. A página é **arquivada**
 * no `finally` pela própria UI (S3.2): o banco de dev não acumula páginas de teste.
 */

/** Arquiva a página criada pela UI (a URL fica reservada, mas o estado de teste deixa de acumular). */
async function archivePage(page: Page, editPath: string): Promise<void> {
  await page.goto(editPath);

  const archive = page.getByRole('button', { name: 'Arquivar página' });

  if ((await archive.count()) > 0) {
    await archive.focus();
    await archive.press('Enter');
    await expect(page.getByText('Página arquivada.')).toBeVisible();
  }
}

const document = (text: string): string =>
  JSON.stringify({
    type: 'doc',
    content: [{ type: 'paragraph', content: [{ type: 'text', text }] }],
  });

test.describe('CMS — páginas do site', () => {
  test('cria uma página com título por idioma e recusa um endereço de rota viva', async ({
    page,
  }, testInfo) => {
    const suffix = `${testInfo.project.name}-${Date.now()}`;
    const slug = `e2e-pagina-${suffix}`;
    let editPath: string | null = null;

    await signIn(page);

    try {
      await page.goto('/paginas');
      await expect(page.getByRole('heading', { level: 1, name: 'Páginas do site' })).toBeVisible();
      await expectNoA11yViolations(page);

      await page.getByRole('link', { name: 'Nova página' }).click();
      await expect(page.getByRole('heading', { level: 1, name: 'Nova página' })).toBeVisible();

      // O rótulo do campo carrega o `*` (aria-hidden) — o nome acessível é só o do idioma.
      const primaryTitle = page.getByRole('textbox', { name: 'Português (Brasil)', exact: true });

      await primaryTitle.fill(`Página e2e ${suffix}`);
      await page.getByRole('textbox', { name: 'Endereço (slug)' }).fill(slug);
      await page
        .getByRole('textbox', { name: 'Descrição de SEO — Português (Brasil)' })
        .fill('Página criada pelo teste de ponta a ponta.');

      // Envio pelo **ponteiro** (clique real no botão de salvar): o Playwright só conclui quando o
      // alvo está estável e não interceptado. Cobre a suspeita levantada na S3.1 (formulário longo +
      // editor rico no viewport móvel) nos dois projetos; o teclado continua provado no bloco sem JS.
      await page.getByRole('button', { name: 'Salvar página' }).click();

      await expect(page.getByText('Página criada.')).toBeVisible();
      await expect(page.getByRole('textbox', { name: 'Endereço (slug)' })).toHaveValue(slug);
      await expect(primaryTitle).toHaveValue(`Página e2e ${suffix}`);
      await expectNoA11yViolations(page);

      editPath = new URL(page.url()).pathname;

      // Regra de slug: uma rota viva do site nunca é sombreada (422 no campo, nada gravado).
      await page.goto('/paginas/criar');
      const slugField = page.getByRole('textbox', { name: 'Endereço (slug)' });

      await page
        .getByRole('textbox', { name: 'Português (Brasil)', exact: true })
        .fill('Tentativa de rota viva');
      await slugField.fill('noticias');
      await page.getByRole('textbox', { name: 'Endereço (slug)' }).press('Enter');

      await expect(page.getByRole('alert').first()).toContainText('usado por uma rota do site');
      await expect(slugField).toHaveValue('noticias');
      await expect(slugField).toHaveAttribute('aria-invalid', 'true');
    } finally {
      if (editPath !== null) {
        await archivePage(page, editPath);
      }
    }
  });

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

    test('o campo de conteúdo aceita o AST JSON e o documento é preservado', async ({
      page,
    }, testInfo) => {
      const suffix = `nojs-${testInfo.project.name}-${Date.now()}`;
      const slug = `e2e-pagina-${suffix}`;
      let editPath: string | null = null;

      await signIn(page);

      try {
        await page.goto('/paginas/criar');
        await page
          .getByRole('textbox', { name: 'Português (Brasil)', exact: true })
          .fill(`Página e2e ${suffix}`);
        await page.getByRole('textbox', { name: 'Endereço (slug)' }).fill(slug);
        await page
          .getByRole('textbox', { name: 'Conteúdo — Português (Brasil)' })
          .fill(document('Texto sem JS'));
        await page.getByRole('textbox', { name: 'Endereço (slug)' }).press('Enter');

        await expect(page.getByText('Página criada.')).toBeVisible();

        editPath = new URL(page.url()).pathname;

        // O `jsonb` reordena as chaves do AST — a prova é o documento continuar legível e íntegro.
        await expect(
          page.getByRole('textbox', { name: 'Conteúdo — Português (Brasil)' }),
        ).toHaveValue(/"type":"doc".*Texto sem JS/);
        // O axe é executado no caminho com JS (a mesma tela): com scripts desativados a injeção do
        // axe não é confiável, e a estrutura é a mesma — o caminho sem JS prova o comportamento.
      } finally {
        if (editPath !== null) {
          await archivePage(page, editPath);
        }
      }
    });
  });
});
