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

/**
 * História 28 — Página estática no portal (P5.S3.2): o CMS publica, o fallback `/{slug}` serve o
 * AST no shell real com canônica/`hreflang`, a prévia autenticada mostra o rascunho com `noindex` e
 * arquivar fecha a URL com 404 `noindex` — sem sombrear rota viva (`/noticias` continua respondendo).
 *
 * Pré-condições: `make up` + `php artisan db:seed` (perfil demo com `paginas.*`) +
 * `php artisan migrate` + assets compilados.
 *
 * Dados próprios: cada execução cria a sua página com 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
 * fim (o estado final some do sitemap e a URL responde 404 noindex).
 */

/** Portal público: a base do `BASE_URL` (o painel vive em `cms.<base>`). */
function portal(path: string): string {
  return new URL(path, process.env.BASE_URL ?? 'http://localhost:50700').toString();
}

test.describe('Portal — página estática publicada', () => {
  test('prévia, publicação, canônica e arquivamento com 404 noindex', async ({ page }, testInfo) => {
    const suffix = `${testInfo.project.name}-${Date.now()}`;
    const slug = `e2e-portal-pagina-${suffix}`;
    const title = `Página pública ${suffix}`;

    await signIn(page);

    // 1. Rascunho criado pelo CMS (título no locale default + endereço próprio).
    await page.goto('/paginas/criar');
    await page.getByRole('textbox', { name: 'Português (Brasil)', exact: true }).fill(title);
    await page.getByRole('textbox', { name: 'Endereço (slug)' }).fill(slug);
    await page.getByRole('textbox', { name: 'Endereço (slug)' }).press('Enter');
    await expect(page.getByText('Página criada.')).toBeVisible();

    // A edição/prévia usam o **id** da página (binding por UUID), não o slug público.
    const editPath = new URL(page.url()).pathname;

    // O rascunho não tem URL pública: a rota do fallback responde 404 real com `noindex`.
    const draft = await page.goto(portal(`/${slug}?e2e=rascunho-${suffix}`));
    expect(draft?.status(), 'GET rascunho').toBe(404);
    expect(draft?.headers()['x-robots-tag']).toContain('noindex');
    await page.goto(editPath);

    // 2. A prévia autenticada renderiza o rascunho no shell real, sem indexação.
    const visibility = page.getByRole('region', { name: 'Visibilidade' });
    await expect(visibility).toBeVisible();
    await expectNoA11yViolations(page);

    // O link existe e aponta para a prévia autenticada; a navegação é direta porque o clique é
    // instável no viewport móvel (o editor rico acima muda de altura durante o scroll).
    const previewLink = visibility.getByRole('link', { name: 'Português (Brasil)', exact: true });
    await expect(previewLink).toHaveAttribute('href', /\/preview\?locale=pt_BR$/);
    const previewHref = await previewLink.getAttribute('href');

    await page.goto(String(previewHref));
    await expect(page.getByRole('heading', { level: 1, name: title })).toBeVisible();
    await expect(page.locator('meta[name="robots"]')).toHaveAttribute('content', /noindex/);
    await expect(page.locator('link[rel="canonical"]')).toHaveCount(0);

    // 3. Publicar: a URL pública passa a responder 200 com o `<h1>` único.
    await page.goto(editPath);
    // Clique real (ponteiro): o Playwright só conclui quando o alvo está estável e não
    // interceptado — prova a ausência de sobreposição no viewport móvel (suspeita da S3.2).
    await page.getByRole('button', { name: 'Publicar página' }).click();
    await expect(page.getByText('Página publicada.')).toBeVisible();

    const published = await page.goto(portal(`/${slug}`));
    expect(published?.status()).toBe(200);
    await expect(page.getByRole('heading', { level: 1, name: title })).toBeVisible();
    await expect(page.locator('link[rel="canonical"]')).toHaveAttribute('href', portal(`/${slug}`));
    await expect(page.locator('link[rel="alternate"][hreflang="x-default"]')).toHaveCount(1);
    await expectNoA11yViolations(page);

    // Sitemap: a fonte `paginas` só publica o que está publicado; o índice anuncia a fonte e o
    // `urlset` traz a URL absoluta do portal (ADR-027/ADR-043). `page.request` não usa o cache HTTP
    // do navegador, então cada leitura é o estado atual do servidor.
    const index = await page.request.get(portal(`/sitemap.xml?e2e=${suffix}`));
    expect(index.status()).toBe(200);
    expect(await index.text()).toContain('/sitemap-paginas.xml');

    const publishedSitemap = await page.request.get(portal(`/sitemap-paginas.xml?e2e=${suffix}`));
    expect(publishedSitemap.status()).toBe(200);
    expect(await publishedSitemap.text()).toContain(portal(`/${slug}`));

    // Rota viva não é sombreada pelo fallback da página.
    const noticias = await page.goto(portal('/noticias'));
    expect(noticias?.status()).toBe(200);

    // 4. Arquivar: a URL fica fechada (404 real, `X-Robots-Tag: noindex`).
    await page.goto(editPath);
    await page.getByRole('button', { name: 'Arquivar página' }).click();
    await expect(page.getByText('Página arquivada.')).toBeVisible();

    // `?e2e=` evita a resposta em cache HTTP do navegador da visita anterior (o servidor já foi
    // invalidado; o cache público de borda segue o TTL — ADR-022).
    const archived = await page.goto(portal(`/${slug}?e2e=arquivada`));
    expect(archived?.status()).toBe(404);
    expect(archived?.headers()['x-robots-tag']).toContain('noindex');

    // Arquivada sai do sitemap no request seguinte (não espera TTL).
    const archivedSitemap = await page.request.get(portal(`/sitemap-paginas.xml?e2e=arquivada-${suffix}`));
    expect(archivedSitemap.status()).toBe(200);
    expect(await archivedSitemap.text()).not.toContain(portal(`/${slug}`));
  });
});
