/**
 * Wie weit das Glossar den Katalog wirklich abdeckt.
 *
 * **Der Anlass.** Die 87 Einträge vor 0.22.0 waren fast durchweg
 * Rechtsbegriffe. In Kapitel II – Waffentechnik – erreichten nur zwei von fünf
 * Fragen überhaupt einen Treffer, und selbst diese Treffer waren meist
 * Rechtsbegriffe wie „Schusswaffe“. Das Glossar ist die WCAG-3.1.3-Zusage
 * dieser Software; eingelöst wurde sie ausgerechnet in dem Kapitel nicht,
 * dessen Vokabular Einsteigern am fremdesten ist.
 *
 * **Warum gemessen und nicht gezählt.** Eine Zahl wie „118 Einträge“ sagt
 * nichts darüber, ob sie dort greifen, wo gelernt wird. Gemessen wird deshalb
 * mit **derselben Begriffssuche**, die auch unter der Frage läuft: Wie viele
 * Fragen eines Kapitels finden mindestens einen Begriff?
 */

import { readFileSync } from 'node:fs';
import { join } from 'node:path';

import { describe, expect, it } from 'vitest';

import { begriffssucheBauen, type Glossar } from '../src/shared/glossar';
import type { Katalog } from '../src/shared/katalog';

const WURZEL = join(process.cwd(), '..');
const GLOSSAR = JSON.parse(
  readFileSync(join(WURZEL, 'content', 'glossar.json'), 'utf8'),
) as Glossar;
const KATALOG = JSON.parse(
  readFileSync(join(WURZEL, 'content', 'katalog', 'katalog.json'), 'utf8'),
) as Katalog;

const SUCHE = begriffssucheBauen(GLOSSAR);

/** Der Text, den die Oberfläche nach Begriffen durchsucht. */
function volltext(frage: Katalog['fragen'][number]): string {
  const optionen = (frage.optionen ?? []).map((option) => option.inhalt.text).join(' ');
  return `${frage.frage.text} ${optionen} ${frage.musterantwort?.text ?? ''}`;
}

/** Anteil der Fragen eines Kapitels mit mindestens einem Begriff, in Prozent. */
function abdeckung(kapitelId: string): number {
  const fragen = KATALOG.fragen.filter((frage) => frage.kapitel === kapitelId);
  const getroffen = fragen.filter((frage) => SUCHE.imText(volltext(frage)).length > 0);
  return Math.round((100 * getroffen.length) / fragen.length);
}

describe('Abdeckung des Glossars', () => {
  it('erreicht in der Waffentechnik mindestens vier von fünf Fragen', () => {
    /* Der eigentliche Befund: Kapitel II lag bei 40 Prozent. Die Schwelle
       steht bewusst unter dem gemessenen Wert – sie soll einen Rückfall
       melden, nicht jede Schwankung. */
    expect(abdeckung('II')).toBeGreaterThanOrEqual(80);
  });

  it('fällt in keinem Kapitel unter drei von vier Fragen', () => {
    for (const kapitel of KATALOG.kapitel) {
      expect(abdeckung(kapitel.id), kapitel.id).toBeGreaterThanOrEqual(75);
    }
  });

  it('kennt die tragenden Begriffe der Waffentechnik', () => {
    /* Namentlich, damit ein stilles Verschwinden auffällt: Ein Schwellwert
       allein bliebe grün, wenn ein Begriff wegfiele und ein anderer
       dazukäme. */
    const begriffe = new Set(GLOSSAR.eintraege.map((eintrag) => eintrag.begriff));
    for (const begriff of [
      'Munition',
      'Patrone',
      'Kaliber',
      'Lauf',
      'Verschluss',
      'Patronenlager',
      'Geschoss',
      'Magazin',
      'Trommel',
      'Revolver',
      'Pistole',
      'Büchse',
      'Flinte',
      'Züge und Felder',
      'Drall',
      'Mündungsenergie',
      'Höchstreichweite',
    ]) {
      expect(begriffe.has(begriff), begriff).toBe(true);
    }
  });

  it('kennt die beiden Maßstäbe der Aufbewahrung und hält sie auseinander', () => {
    /* Widerstandsgrad ist geltendes Recht, Sicherheitsstufe lebt nur über die
       Übergangsregelung fort. Wer beides verwechselt, verwechselt
       Bestandsschutz mit Anforderung. */
    const widerstand = GLOSSAR.eintraege.find((e) => e.begriff === 'Widerstandsgrad');
    const stufe = GLOSSAR.eintraege.find((e) => e.begriff === 'Sicherheitsstufe');

    expect(widerstand?.fundstellen.some((f) => f.gesetz === 'AWaffV')).toBe(true);
    expect(stufe?.text).toContain('Übergangsregelung');
    expect(stufe?.kurz).toContain('nicht mehr vor');
  });

  it('begründet jeden Eintrag ohne Fundstelle', () => {
    /* Bei der Waffentechnik ist das der Regelfall – sie ist fachliche Praxis,
       kein Gesetzestext. Das auszusprechen ist die Alternative dazu, eine
       Fundstelle zu dehnen. */
    const stumm = GLOSSAR.eintraege
      .filter(
        (eintrag) => eintrag.fundstellen.length === 0 && eintrag.ohneFundstelleGrund === undefined,
      )
      .map((eintrag) => eintrag.begriff);

    expect(stumm).toEqual([]);
  });

  it('lässt keinen „Siehe auch“-Verweis ins Leere laufen', () => {
    const begriffe = new Set(GLOSSAR.eintraege.map((eintrag) => eintrag.begriff));
    const kaputt = GLOSSAR.eintraege.flatMap((eintrag) =>
      (eintrag.siehe ?? [])
        .filter((ziel) => !begriffe.has(ziel))
        .map((ziel) => `${eintrag.begriff} -> ${ziel}`),
    );

    expect(kaputt).toEqual([]);
  });
});
