/**
 * Das Druckdokument, durch einen echten Parser gesehen.
 *
 * Die Bausteine prüft `druck.test.ts` als Zeichenketten. Hier wird das
 * fertige Dokument geparst und mit denselben Regeln geprüft, die auch für die
 * Oberfläche gelten – Überschriftenfolge, Tabellenzuordnung, Sprache. Genau
 * diese Angaben trägt Chromium in den Strukturbaum des getaggten PDF; was
 * hier fehlt, fehlt dort auch.
 *
 * Die Grenze ist ehrlich zu benennen: jsdom rechnet kein Layout und rendert
 * kein CSS. Farbkontraste prüft deshalb `druck.test.ts` rechnerisch an den
 * Farbwerten, und ob der Stil überhaupt greift, misst `e2e/druck.spec.ts` am
 * erzeugten PDF.
 */

import axe from 'axe-core';
import { jsdomOptionen } from '../src/shared/wcag';
import { describe, expect, it } from 'vitest';

import { lernberichtBauen } from '../src/shared/druck/lernbericht';
import type { Quellenangabe } from '../src/shared/druck/dokument';
import type { Lernplan } from '../src/shared/lernplan';
import type { Lernuebersicht } from '../src/shared/lernstand';
import type { Pruefungsverlauf } from '../src/shared/pruefung';

const QUELLE: Quellenangabe = {
  amtlich: 'Amtlicher Fragenkatalog für die Waffensachkundeprüfung, Stand 16.12.2024.',
  herausgeber: 'Bundesverwaltungsamt',
  stand: '2024-12-16',
  quelleUrl: 'https://www.bva.bund.de/',
  umfang: { art: 'kein amtlicher wortlaut' },
};

const UEBERSICHT: Lernuebersicht = {
  fragenGesamt: 575,
  beantwortet: 120,
  belegt: 48,
  reifegrad: 48 / 575,
  stufe: 'zurueck',
  deckelnd: [],
  faellig: 12,
  gemerkt: 5,
  offen: 0,
  fehler: 0,
  heuteRichtig: 8,
  heuteFalsch: 2,
  heuteBearbeitet: 0,
  tageSeitLetzterAntwort: null,
  bereiche: [
    {
      id: 'I',
      titel: 'Waffenrecht und sonstige Vorschriften',
      fragenGesamt: 300,
      beantwortet: 100,
      belegt: 40,
      reifegrad: 40 / 300,
      stufe: 'zurueck',
    },
    {
      id: 'I.1',
      titel: 'Begriffe des Waffenrechts',
      fragenGesamt: 90,
      beantwortet: 40,
      belegt: 22,
      reifegrad: 22 / 90,
      stufe: 'zurueck',
    },
  ],
};

const PLAN: Lernplan = {
  termin: '2026-10-01',
  tageBisTermin: 41,
  gesamtFragen: 575,
  nieBeantwortet: 455,
  faellig: 12,
  zielquote: 0.9,
  prognoseHeute: 0.42,
  prognoseAmTermin: 0.31,
  pensum: { neu: 12, wiederholung: 8, gesamt: 20, minuten: 9 },
  machbarkeit: 'machbar',
  sekundenProFrage: 25,
};

const VERLAUF: Pruefungsverlauf[] = [
  {
    id: 3,
    profilId: 'standard',
    profilName: 'Standard',
    zeitpunkt: '2026-08-20T10:00:00.000Z',
    gesamt: 80,
    richtig: 70,
    quote: 0.875,
    urteil: 'bestanden',
    dauerMs: 71 * 60_000,
    zeitmodus: 'normal',
    unbeantwortet: 0,
    zeitAbgelaufen: false,
    bereiche: null,
    bestehensQuote: 0.8,
  },
];

function dokument(): Document {
  return new DOMParser().parseFromString(
    lernberichtBauen({
      uebersicht: UEBERSICHT,
      plan: PLAN,
      verlauf: VERLAUF,
      profilName: 'Testprofil',
      quelle: QUELLE,
      erstelltAm: '2026-08-21T06:00:00.000Z',
      schriftgroesse: 'normal',
    }),
    'text/html',
  );
}

describe('Druckdokument als geparste Seite', () => {
  const doc = dokument();

  it('lässt sich überhaupt parsen', () => {
    expect(doc.querySelector('parsererror')).toBeNull();
    expect(doc.title).toBe('Lernbericht Waffensachkunde');
  });

  it('trägt die Sprache am Wurzelelement', () => {
    /* Ohne sie liest ein Screenreader deutschen Text mit englischer
       Aussprache – und im PDF fehlt dann /Lang. */
    expect(doc.documentElement.lang).toBe('de');
  });

  it('hat genau eine h1 und keine Sprünge', () => {
    const ueberschriften = [...doc.querySelectorAll('h1, h2, h3, h4, h5, h6')];
    const ebenen = ueberschriften.map((h) => Number(h.tagName.slice(1)));

    expect(ebenen.filter((e) => e === 1)).toHaveLength(1);
    expect(ebenen[0]).toBe(1);

    let vorige = 1;
    for (const [i, ebene] of ebenen.entries()) {
      const wo = ueberschriften[i]?.textContent ?? '(ohne Text)';
      expect(ebene, `Sprung bei „${wo}“`).toBeLessThanOrEqual(vorige + 1);
      vorige = ebene;
    }
  });

  it('gibt jeder Tabelle eine Beschriftung und Kopfzellen mit Richtung', () => {
    const tabellen = [...doc.querySelectorAll('table')];

    expect(tabellen.length).toBeGreaterThan(2);
    for (const t of tabellen) {
      expect(t.querySelector('caption')?.textContent.trim() ?? '').not.toBe('');
      expect(t.querySelectorAll('th[scope="col"]').length).toBeGreaterThan(0);
      for (const th of t.querySelectorAll('th')) {
        expect(th.getAttribute('scope')).toMatch(/^(col|row)$/u);
      }
    }
  });

  it('enthält kein Skript und keinen Verweis nach draußen', () => {
    expect(doc.querySelectorAll('script')).toHaveLength(0);
    expect(doc.querySelectorAll('a[href], link[href]')).toHaveLength(0);

    /*
      Bilder sind erlaubt, aber nur eingebettet. Die Unterscheidung ist der
      eigentliche Punkt: Ein `img` mit `data:`-URI fuehrt nirgendwohin, ein
      `img` mit `http:`- oder `file:`-Adresse verliesse das Dokument. Bis
      Fassung 0.18.0 verbot diese Zusicherung beides zusammen - damit waere
      jede Fragenliste rot gewesen, obwohl bei drei Fragen die Pruefzeichen
      selbst die Antwortmoeglichkeiten sind und die Option ohne Bild leer
      bliebe.
    */
    for (const bild of doc.querySelectorAll('img')) {
      expect(bild.getAttribute('src') ?? '').toMatch(/^data:image\//u);
      expect(bild.getAttribute('alt')).toBeTruthy();
    }
  });

  it('nennt die Herkunft im sichtbaren Text', () => {
    const text = doc.body.textContent;

    expect(text).toContain('Bundesverwaltungsamt');
    expect(text).toContain('2024-12-16');
    expect(text).toContain('EUPL-1.2');
  });
});

describe('Druckdokument – axe-core', () => {
  it('meldet keine Verstöße gegen WCAG 2.1 A/AA', async () => {
    /* Der Inhalt wird in das lebende Dokument übernommen: axe braucht einen
       echten Baum mit einem echten Wurzelelement, kein losgelöstes
       Parse-Ergebnis. */
    const geparst = dokument();
    document.documentElement.lang = geparst.documentElement.lang;
    document.title = geparst.title;
    document.body.innerHTML = geparst.body.innerHTML;

    const ergebnis = await axe.run(document.body, jsdomOptionen());

    expect(ergebnis.violations.map((v) => `${v.id}: ${v.help}`)).toEqual([]);
  });
});
