import { expect, test } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
import path from 'node:path';

test('Homepage hat keine schweren automatisiert erkennbaren Accessibility-Verstösse', async ({ page }) => {
  await page.goto('/');
  const results = await new AxeBuilder({ page }).analyze();
  const serious = results.violations.filter((violation) => ['serious', 'critical'].includes(violation.impact ?? ''));
  expect(serious).toEqual([]);
});

test('Logo-Intro läuft bei jedem neuen Hauptseitenaufruf auf Desktop und Mobil', async ({ page }) => {
  await page.goto('/');
  const intro = page.locator('[data-site-intro]');
  await expect(intro).toBeVisible();
  await expect(intro).toHaveClass(/is-animating/);
  await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
  await expect(intro).toBeHidden({ timeout: 2500 });
  await page.reload();
  await expect(intro).toBeVisible();
  await expect(intro).toBeHidden({ timeout: 2500 });
  await page.goto('/faq/');
  await page.locator('a[href="/"]').first().click();
  await expect(page).toHaveURL(/\/$/);
  await expect(page.locator('[data-site-intro]')).toBeVisible();
  await expect(page.locator('[data-site-intro]')).toBeHidden({ timeout: 2500 });
});

test('Reduced Motion zeigt das G statisch und morpht ohne Raumbewegung', async ({ browser }, testInfo) => {
  test.skip(testInfo.project.name !== 'desktop', 'Ein reduzierter Kontext genügt');
  const context = await browser.newContext({ reducedMotion: 'reduce', viewport: { width: 390, height: 844 }, isMobile: true, hasTouch: true });
  const page = await context.newPage();
  await page.goto('/');
  const intro = page.locator('[data-site-intro]');
  await expect(intro).toBeVisible();
  await expect(intro).toHaveClass(/is-reduced/);
  await expect(page.locator('.site-intro__mark')).toHaveCSS('animation-name', 'none');
  await expect(intro).toBeHidden({ timeout: 1500 });
  const compare = page.locator('[data-hero-compare]');
  await page.evaluate(() => scrollTo(0, 160));
  await expect.poll(() => compare.evaluate((element) => Number.parseFloat(getComputedStyle(element).getPropertyValue('--mobile-reality')))).toBeGreaterThan(.4);
  await expect.poll(() => compare.evaluate((element) => Number.parseFloat(getComputedStyle(element).getPropertyValue('--mobile-reality')))).toBeLessThan(.6);
  await expect.poll(() => compare.evaluate((element) => getComputedStyle(element).getPropertyValue('--mobile-sketch-blur').trim())).toBe('0.00px');
  await context.close();
});

test('Desktop-Hero führt die G-Maske von Bildrand zu Bildrand', async ({ page }, testInfo) => {
  test.skip(testInfo.project.name !== 'desktop', 'Desktop pointer interaction');
  await page.goto('/');
  await expect(page.locator('[data-site-intro]')).toBeHidden({ timeout: 2500 });
  const compare = page.locator('[data-hero-compare]');
  const toggle = page.locator('[data-reality-toggle]');
  await expect(toggle).toHaveAccessibleName('Ausführung vollständig ansehen');
  const box = await compare.boundingBox();
  if (!box) throw new Error('Hero-Vergleichsfläche fehlt');
  const y = box.y + box.height * .48;
  const readPosition = () => compare.evaluate((element) => ({
    left: Number.parseFloat(getComputedStyle(element).getPropertyValue('--reveal-left')),
    size: Number.parseFloat(getComputedStyle(element).getPropertyValue('--reveal-size')),
  }));
  await page.mouse.move(box.x + 4, y);
  await expect(compare).toHaveClass(/is-pointing/);
  await expect.poll(async () => (await readPosition()).left).toBeLessThan(0);
  const left = await readPosition();
  expect(left.left + left.size / 2).toBeLessThan(10);
  await page.mouse.move(box.x + box.width / 2, y);
  await expect.poll(async () => {
    const position = await readPosition();
    return Math.abs(position.left + position.size / 2 - box.width / 2);
  }).toBeLessThan(6);
  await page.mouse.move(box.x + box.width - 4, y);
  await expect.poll(async () => {
    const position = await readPosition();
    return Math.abs(position.left + position.size / 2 - (box.width - 4));
  }).toBeLessThan(6);
  await expect(page.locator('.hero-layer--reality-mask')).toHaveCSS('mask-image', /gasser-emblem-mark/);
  await expect(toggle).toHaveAttribute('aria-pressed', 'false');
  await toggle.click();
  await expect(toggle).toHaveAttribute('aria-pressed', 'true');
  await expect(page.getByRole('button', { name: 'Zeichnung wieder ansehen' })).toBeVisible();
  await expect(compare).toHaveClass(/show-reality/);
});

test('Mobiler Hero morpht ab dem ersten Scrollen fliessend zur registrierten Ausführung', async ({ page }, testInfo) => {
  test.skip(testInfo.project.name !== 'mobile', 'Mobile scroll interaction');
  await page.goto('/');
  const compare = page.locator('[data-hero-compare]');
  await expect(page.locator('[data-reality-toggle]')).toBeHidden();
  await expect(page.locator('.hero-layer--sketch img')).toHaveJSProperty('naturalWidth', 720);
  await expect(page.locator('.hero-layer--reality-base img')).toHaveJSProperty('naturalWidth', 720);
  await page.evaluate(() => scrollTo(0, 2));
  await expect.poll(() => compare.evaluate((element) => Number.parseFloat(getComputedStyle(element).getPropertyValue('--mobile-reality')))).toBeGreaterThan(0);
  await page.evaluate(() => scrollTo(0, 160));
  await expect.poll(() => compare.evaluate((element) => Number.parseFloat(getComputedStyle(element).getPropertyValue('--mobile-reality')))).toBeGreaterThan(.45);
  await expect.poll(() => compare.evaluate((element) => Number.parseFloat(getComputedStyle(element).getPropertyValue('--mobile-sketch')))).toBeLessThan(.55);
  await page.evaluate(() => scrollTo(0, 340));
  await expect.poll(() => compare.evaluate((element) => Number.parseFloat(getComputedStyle(element).getPropertyValue('--mobile-reality')))).toBeGreaterThan(.95);
  await expect(page.locator('[data-swipe-cue]')).toHaveCount(0);
  await page.evaluate(() => scrollTo(0, 0));
  await expect.poll(() => compare.evaluate((element) => Number.parseFloat(getComputedStyle(element).getPropertyValue('--mobile-reality')))).toBeLessThan(.1);
  await compare.evaluate((element) => {
    const target = element.closest('.hero') || element;
    const start = new Touch({ identifier: 1, target, clientX: 195, clientY: 700 });
    target.dispatchEvent(new TouchEvent('touchstart', { touches: [start], bubbles: true }));
    const move = new Touch({ identifier: 1, target, clientX: 195, clientY: 570 });
    target.dispatchEvent(new TouchEvent('touchmove', { touches: [move], bubbles: true }));
  });
  await expect.poll(() => compare.evaluate((element) => Number.parseFloat(getComputedStyle(element).getPropertyValue('--mobile-reality')))).toBeGreaterThan(.35);
});

test('Homepage lädt und zentrale Desktop-Navigation funktioniert', async ({ page }, testInfo) => {
  test.skip(testInfo.project.name !== 'desktop', 'Desktop smoke');
  await page.goto('/');
  await expect(page.getByRole('heading', { level: 1 })).toContainText('Umbauten und Kundenarbeiten');
  await expect(page.getByRole('navigation', { name: 'Hauptnavigation' })).toBeVisible();
  await page.getByRole('link', { name: 'Referenzen', exact: true }).first().click();
  await expect(page).toHaveURL(/\/referenzen\//);
});

test('Desktop-Leistungsmenü schliesst per Escape', async ({ page }, testInfo) => {
  test.skip(testInfo.project.name !== 'desktop', 'Desktop smoke');
  await page.goto('/');
  const toggle = page.getByRole('button', { name: 'Leistungen anzeigen' });
  const menu = page.locator('[data-services-dropdown]');
  await toggle.click();
  await expect(menu).toBeVisible();
  await page.keyboard.press('Escape');
  await expect(menu).toBeHidden();
  await toggle.click();
  await expect(menu).toBeVisible();
});

test('Mobiles Drawer-Menü öffnet, hält Fokus und schliesst per Escape', async ({ page }, testInfo) => {
  test.skip(testInfo.project.name !== 'mobile', 'Mobile smoke');
  await page.goto('/');
  const toggle = page.locator('[data-menu-toggle]');
  await expect(toggle).toHaveAttribute('aria-label', 'Menü öffnen');
  await toggle.click();
  const drawer = page.locator('#mobile-navigation');
  await expect(drawer).toBeVisible();
  await expect(toggle).toHaveAttribute('aria-expanded', 'true');
  await page.keyboard.press('Escape');
  await expect(drawer).toHaveAttribute('aria-hidden', 'true');
});

test('Leistungslink führt zur passenden Seite', async ({ page }) => {
  await page.goto('/');
  await page.getByRole('link', { name: /Leistung ansehen/ }).first().click();
  await expect(page).toHaveURL(/\/leistungen\/kundenarbeiten\//);
  await expect(page.getByRole('heading', { level: 1 })).toHaveText('Kundenarbeiten');
});

test('Referenzfilter unterstützt Klick und Query-Parameter', async ({ page }) => {
  await page.goto('/referenzen/?kategorie=umbauten');
  await expect(page.getByRole('button', { name: 'Umbauten' })).toHaveAttribute('aria-pressed', 'true');
  const visibleCards = page.locator('[data-reference-card]:visible');
  await expect(visibleCards).toHaveCount(3);
  await page.getByRole('button', { name: 'Alle', exact: true }).click();
  await expect(page).not.toHaveURL(/kategorie=/);
  await expect(page.locator('[data-reference-card]:visible')).toHaveCount(7);
});

test('Projektanfrage validiert, nimmt Fotos an und nutzt mobil die Teilen-Funktion', async ({ page }, testInfo) => {
  if (testInfo.project.name === 'mobile') {
    await page.addInitScript(() => {
      Object.defineProperty(navigator, 'canShare', { configurable: true, value: () => true });
      Object.defineProperty(navigator, 'share', { configurable: true, value: async (data: ShareData) => { (window as typeof window & { __sharedFileCount?: number }).__sharedFileCount = data.files?.length ?? 0; } });
    });
  }
  await page.goto('/projekt-anfrage/');
  if (testInfo.project.name === 'mobile') await expect(page.getByRole('navigation', { name: 'Schnellkontakt' })).toBeHidden();
  const next = page.getByRole('button', { name: 'Weiter' });
  await expect(next).toBeDisabled();
  await page.getByLabel('Kundenarbeit / Kleinauftrag').check();
  await expect(next).toBeEnabled();
  await next.click();
  await expect(next).toBeDisabled();
  await page.getByLabel('Ort / Gemeinde').fill('Windisch');
  await page.getByLabel('Objekttyp').selectOption('Einfamilienhaus');
  await expect(next).toBeEnabled();
  await next.click();
  await page.locator('[data-photo-gallery]').setInputFiles(path.resolve('public/images/brand/gasser-emblem-mark.png'));
  await expect(page.locator('[data-photo-list] li')).toHaveCount(1);
  await expect(page.locator('[data-summary]')).toContainText('gasser-emblem-mark.png');
  await next.click();
  await page.getByLabel('Gewünschter Zeitraum').selectOption('In den nächsten 1–3 Monaten');
  await page.getByLabel('Kurze Beschreibung').fill('Umbau eines bewohnten Einfamilienhauses mit sorgfältiger Etappierung.');
  await next.click();
  await page.getByLabel('Name').fill('Max Muster');
  await page.getByRole('textbox', { name: 'E-Mail', exact: true }).fill('max@example.ch');
  await page.getByText(/Ich habe die Datenschutzerklärung/).click();
  const submit = page.getByRole('button', { name: testInfo.project.name === 'mobile' ? 'Anfrage mit Fotos teilen' : 'E-Mail-Anfrage vorbereiten' });
  await expect(submit).toBeEnabled();
  await expect(page.locator('[data-summary]')).toContainText('Max Muster');
  if (testInfo.project.name === 'mobile') {
    const mobileSummary = page.locator('.mobile-summary');
    await expect(mobileSummary).toBeVisible();
    await mobileSummary.locator('summary').click();
    await expect(mobileSummary.locator('pre')).toContainText('Max Muster');
    await submit.click();
    await expect(page.locator('[data-success-title]')).toContainText('zum Teilen übergeben');
    await expect.poll(() => page.evaluate(() => (window as typeof window & { __sharedFileCount?: number }).__sharedFileCount)).toBe(1);
  }
});

test('Validierungsfehler werden feldnah ausgegeben', async ({ page }) => {
  await page.goto('/projekt-anfrage/');
  await page.getByLabel('Noch unklar').check();
  await page.getByRole('button', { name: 'Weiter' }).click();
  const location = page.getByLabel('Ort / Gemeinde');
  await location.focus();
  await page.getByLabel('Objekttyp').focus();
  await expect(page.locator('#error-location')).toContainText('Ort oder Gemeinde');
  await expect(location).toHaveAttribute('aria-invalid', 'true');
});

test('Mobile Kontaktleiste hat korrekte Links und Seite keine Überbreite', async ({ page }, testInfo) => {
  test.skip(testInfo.project.name !== 'mobile', 'Mobile smoke');
  await page.goto('/');
  const bar = page.getByRole('navigation', { name: 'Schnellkontakt' });
  await expect(bar).toBeHidden();
  await page.evaluate(() => window.scrollTo(0, (document.querySelector('.hero')?.getBoundingClientRect().height ?? 700) + 120));
  await expect(bar).toBeVisible();
  await expect(bar.getByRole('link', { name: /anrufen/i })).toHaveAttribute('href', 'tel:+41772682529');
  await expect(bar.getByRole('link', { name: /WhatsApp/i })).toHaveAttribute('href', /wa\.me\/41772682529/);
  await expect(bar.getByRole('link', { name: 'Anfrage' })).toHaveAttribute('href', '/projekt-anfrage/');
  const overflow = await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth);
  expect(overflow).toBeLessThanOrEqual(1);
  const trustHeight = await page.locator('.trust-strip').evaluate((element) => element.getBoundingClientRect().height);
  expect(trustHeight).toBeLessThanOrEqual(52);
});

test('Inhaltsbesprechung ist als Noindex-Checkliste verfügbar', async ({ page }) => {
  await page.goto('/inhalte-ergaenzen/');
  await expect(page.locator('meta[name="robots"]')).toHaveAttribute('content', 'noindex, nofollow');
  await expect(page.getByRole('heading', { level: 1 })).toContainText('Welche Inhalte fehlen');
  await expect(page.getByText('Porträt ergänzen')).toBeVisible();
});

test('Alle geforderten Zielgrössen bleiben ohne horizontale Überbreite', async ({ page }, testInfo) => {
  test.skip(testInfo.project.name !== 'desktop', 'Die Viewports werden gesammelt in einem Browser geprüft');
  const sizes = [
    { width: 360, height: 800 }, { width: 375, height: 812 }, { width: 390, height: 844 },
    { width: 430, height: 932 }, { width: 768, height: 1024 }, { width: 820, height: 1180 },
    { width: 1024, height: 768 }, { width: 1366, height: 768 }, { width: 1440, height: 900 },
    { width: 1920, height: 1080 },
  ];
  for (const size of sizes) {
    await page.setViewportSize(size);
    await page.goto('/');
    await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
    const overflow = await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth + 1);
    expect(overflow, `${size.width}x${size.height} darf nicht horizontal überlaufen`).toBe(false);
  }
});
