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

/**
 * História 24 — Estatísticas privacidade-first (P4.S5/ADR-047).
 *
 * Jornada: uma visita ao portal gera o agregado (middleware em `terminate`); a equipe entra no
 * painel, vê a rota nomeada na visão geral e no ranking, filtra o período e baixa o CSV como
 * anexo. Nenhum dado pessoal no caminho; axe A/AA nas telas. Cada teste cria o próprio dado (a
 * visita) e a ordem dentro do arquivo é contrato (`fullyParallel: false`).
 */

/** 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();
}

/** Dia UTC corrente: o agregado diário usa UTC (ADR-047 §4), não o fuso do host. */
function utcToday(): string {
  return new Date().toISOString().slice(0, 10);
}

/** Linha do ranking de páginas para uma rota nomeada (a primeira célula é `th scope="row"`). */
function routeRow(page: Page, route: string) {
  return page.locator('tbody tr', { hasText: route });
}

test.describe('CMS — Estatísticas', () => {
  test('a visita ao portal aparece no relatório e o filtro de período é preservado', async ({ page }) => {
    // Dia UTC capturado **antes** da visita: o agregado usa o dia do `terminate`; recalcular
    // depois de signIn/duas telas pode cruzar a meia-noite UTC e perder a linha.
    const today = utcToday();

    // 1. Visita anônima ao portal: o middleware grava o agregado depois da resposta.
    const visit = await page.goto(portal('/politica-de-privacidade'));
    expect(visit?.status()).toBe(200);

    await signIn(page);

    // 2. Visão geral: a série e os totais do período (7 dias por padrão).
    await page.goto('/estatisticas');
    await expect(page.getByRole('heading', { level: 1, name: 'Visão geral' })).toBeVisible();
    await expect(page.getByRole('heading', { name: 'Série diária' })).toBeVisible();
    await expect(page.getByRole('heading', { name: 'Status HTTP' })).toBeVisible();
    await expect(page.getByText('Métricas agregadas, sem dados pessoais.')).toBeVisible();
    await expectNoA11yViolations(page);

    // 3. Ranking: a rota **nomeada** visitada está no período e tem contagem > 0. O filtro por
    //    módulo evita depender da página 1 de um ranking global acumulado (estado compartilhado).
    await page.goto('/estatisticas/paginas?modulo=lgpd');

    const row = routeRow(page, 'lgpd.privacy');
    await expect(row).toHaveCount(1);

    const requests = Number.parseInt((await row.locator('td').nth(1).innerText()).trim(), 10);
    expect(requests).toBeGreaterThan(0);

    // Nunca path nem query string no relatório (só o nome canônico da rota).
    await expect(page.getByText('/politica-de-privacidade', { exact: false })).toHaveCount(0);

    // 4. Filtro de período pela própria UI: o estado vai para a URL e o resumo acompanha.
    await page.getByLabel('Período').selectOption('30');

    await Promise.all([
      page.waitForURL(/periodo=30/),
      page.getByRole('button', { name: 'Filtrar' }).click(),
    ]);

    await expect(page.locator('p').filter({ hasText: '— 30 dias' })).toBeVisible();
    await expect(routeRow(page, 'lgpd.privacy')).toHaveCount(1);

    // 5. Período personalizado inclusivo no dia UTC capturado antes da visita.
    await page.goto(`/estatisticas/paginas?periodo=custom&de=${today}&ate=${today}&modulo=lgpd`);

    await expect(page.getByText(`De ${today} a ${today}`)).toBeVisible();
    await expect(routeRow(page, 'lgpd.privacy')).toHaveCount(1);

    await expectNoA11yViolations(page);
  });

  test('o export CSV responde como anexo com o mesmo recorte da tela', async ({ page }) => {
    // O spec cria o próprio dado: visita ao portal antes de exportar.
    const visit = await page.goto(portal('/politica-de-privacidade'));
    expect(visit?.status()).toBe(200);

    await signIn(page);
    await page.goto('/estatisticas/paginas?periodo=7');

    const [download] = await Promise.all([
      page.waitForEvent('download'),
      page.getByRole('link', { name: 'Páginas (ranking)' }).click(),
    ]);

    // Nome derivado do relatório + datas do período (nunca da query string).
    expect(download.suggestedFilename()).toMatch(
      /^estatisticas-paginas-\d{4}-\d{2}-\d{2}-\d{4}-\d{2}-\d{2}\.csv$/,
    );

    const path = await download.path();
    expect(path).not.toBeNull();

    const body = await readFile(path as string, 'utf8');
    const lines = body.split('\n').filter((line) => line !== '');

    expect(lines[0]).toBe('Rota,Módulo,Requisições,Robôs,"Duração média (ms)",4xx,5xx,"Erros (4xx / 5xx)"');
    expect(body).toContain('lgpd.privacy');
    expect(body).not.toContain('/politica-de-privacidade');
    expect(body).not.toContain('?');

    // A visão geral oferece os três relatórios exportáveis (páginas, módulos, série diária).
    await page.goto('/estatisticas');

    for (const report of ['paginas', 'modulos', 'serie']) {
      await expect(page.locator(`a[data-export-report="${report}"]`)).toBeVisible();
    }

    await expectNoA11yViolations(page);
  });
});
