import { test, expect, type Page } from '@playwright/test';
import { writeFileSync, mkdirSync } from 'node:fs';
import path from 'node:path';

/**
 * User History: "Explorador casual"
 * Ver docs: ../histories/explorer.md
 *
 * Fluxo: homepage → header "Notícias" → /noticias → 2ª notícia (lê até o fim)
 * → 1ª notícia de destaque na sidebar → badge da unidade regional (acima da
 * data) → página da regional (lê até o fim) → 1º associado abre modal →
 * clicar fora do modal retorna à visualização principal da regional.
 *
 * A cada destino visitado, o teste registra um "checkpoint" no manifesto
 * `sniff/manifest.json`. O gate visual (scripts/sniff-visuals.sh) usa esse
 * manifesto para capturar cada destino com o sniffCSS, comparar contra o
 * baseline e rodar as regras de acessibilidade.
 */
interface Checkpoint {
  label: string;
  url: string;
  selector: string;
}

const checkpoints: Checkpoint[] = [];
const MANIFEST_PATH = path.join(__dirname, '..', 'sniff', 'manifest.json');

test.describe('Explorador casual', () => {
  test('navega de ponta a ponta e valida conteúdo e modal do associado', async ({ page }) => {
    const jsErrors: string[] = [];
    const resource404: string[] = [];
    page.on('pageerror', (err) => jsErrors.push(`pageerror: ${err.message}`));
    page.on('console', (msg) => {
      // "Failed to load resource" = 404 de asset (dado), não erro de JS
      if (msg.type() === 'error' && !msg.text().includes('Failed to load resource')) {
        jsErrors.push(`console.error: ${msg.text()}`);
      }
    });
    page.on('response', (res) => {
      if (res.status() === 404) resource404.push(res.url());
    });

    // 1. Homepage — link "Notícias" no header
    await page.goto('/');
    await expect(page.locator('body')).toBeVisible();
    checkpoint('home', page.url(), '.hero-section-1');
    const newsLink = page.locator('nav a[href="/noticias"]').first();
    await expect(newsLink).toBeVisible();
    await newsLink.click();

    // 2. /noticias — 2ª notícia
    await expect(page).toHaveURL(/\/noticias$/);
    checkpoint('news-list', page.url(), '[data-news-grid]');
    const secondCard = page.locator('[data-news-card]').nth(1);
    await expect(secondCard).toBeVisible();
    await secondCard.locator('h3 a').click();

    // 3. Ler o artigo até o final
    await expect(page.locator('article#noticia-conteudo')).toBeVisible();
    checkpoint('article-2nd', page.url(), 'article#noticia-conteudo');
    await lerAteOFim(page, '.news-article__source');
    await expect(page.locator('.news-article__tags')).toBeVisible();

    // 4. 1ª notícia de destaque na barra lateral
    const firstFeatured = page.locator('.sidebar-news-item').first().locator('a.sidebar-news-link');
    await expect(firstFeatured).toBeVisible();
    await firstFeatured.click();
    await expect(page.locator('article#noticia-conteudo')).toBeVisible();
    checkpoint('article-featured', page.url(), 'article#noticia-conteudo');

    // 5. Badge da unidade regional, acima da data
    const regionalBadge = page.locator('article#noticia-conteudo a[href*="/regional/"]');
    await expect(regionalBadge).toBeVisible();
    await regionalBadge.click();

    // 6. Página da regional — ler até o final (seção de associados)
    await expect(page).toHaveURL(/\/regional\//);
    checkpoint('regional', page.url(), 'main');
    const grid = page.locator('.unidade-regional-associados-grid');
    await expect(grid).toBeVisible();
    checkpoint('regional-associates', page.url(), '.unidade-regional-associados-grid');
    await grid.scrollIntoViewIfNeeded();
    await expect(grid).toBeInViewport();

    // 7. 1º associado abre o modal (descrição, benefícios, site, LinkedIn, tag)
    const firstAssociate = page.locator('.unidade-regional-associado-card').first();
    await expect(firstAssociate).toBeVisible();
    await firstAssociate.click();

    const modal = page.locator('#associadoRegionalModal');
    await expect(modal).toBeVisible();
    const modalBody = modal.locator('#associadoRegionalModalBody');

    // AJAX substitui o spinner pelo conteúdo do associado
    await expect(modalBody.locator('.spinner-border')).toBeHidden({ timeout: 15_000 });
    await expect(modalBody.locator('h2')).toBeVisible();
    await expect(modalBody.locator('p.text-muted')).toBeVisible(); // descrição
    await expect(modalBody.getByText('Benefícios')).toBeVisible();
    await expect(modalBody.getByRole('link', { name: /Visitar site/i })).toBeVisible();
    await expect(modalBody.locator('[title="LinkedIn"], .bi-linkedin').first()).toBeVisible();
    await expect(modalBody.locator('span.badge').last()).toBeVisible(); // categoria/tag abaixo

    // 8. Clicar fora do modal (backdrop, não o X) → volta para a página da regional
    // O widget VLibras e o wrapper .modal cobrem partes do viewport; testa pontos
    // fora do diálogo até o modal fechar (cliques em backdrop/modal-wrapper).
    for (const [x, y] of [[20, 300], [20, 600], [1420, 300], [720, 20]]) {
      await page.mouse.click(x, y);
      try {
        await expect(modal).toBeHidden({ timeout: 1500 });
        break;
      } catch {
        /* tenta o próximo ponto */
      }
    }
    await expect(modal).not.toBeVisible();
    await expect(grid).toBeVisible();
    await expect(page).toHaveURL(/\/regional\//);

    // Validação global: o fluxo inteiro não quebrou JS.
    // 404s de assets (news-default.jpg, variants/*.webp) são conhecidos e
    // reportados como diagnóstico, não como falha de fluxo.
    expect(jsErrors, `erros de JS durante o fluxo: ${jsErrors.join(' | ')}`).toEqual([]);
    test.info().annotations.push({
      type: 'diagnóstico',
      description: `404s de recursos (não bloqueiam o fluxo): ${[...new Set(resource404)].join(' | ') || 'nenhum'}`,
    });

    // Registra os checkpoints para o gate visual sniffCSS
    writeManifest(checkpoints);
  });
});

/** Registra um checkpoint do fluxo para captura visual posterior. */
function checkpoint(label: string, url: string, selector: string): void {
  checkpoints.push({ label, url, selector });
}

/** Persiste o manifesto que o gate sniffCSS vai consumir. */
function writeManifest(items: Checkpoint[]): void {
  mkdirSync(path.dirname(MANIFEST_PATH), { recursive: true });
  writeFileSync(MANIFEST_PATH, JSON.stringify(items, null, 2) + '\n');
}

/** Rola até o alvo e garante que ele está dentro do viewport (leitura até o fim). */
async function lerAteOFim(page: Page, target: string): Promise<void> {
  await page.locator(target).last().scrollIntoViewIfNeeded();
  await expect(page.locator(target).last()).toBeInViewport();
}