/**
 * Datentypen des amtlichen Fragenkatalogs.
 *
 * Spiegelt exakt das Format von `content/katalog/katalog.json`, das die
 * Datenpipeline (`data-pipeline/parse_catalog.py`) aus dem BVA-PDF erzeugt.
 * Der Fragenwortlaut ist amtlicher Inhalt und wird unverändert dargestellt;
 * eigene didaktische Ergänzungen liegen getrennt davon.
 */

/**
 * Ein Textabschnitt mit optionaler Hervorhebung.
 *
 * Im Katalog sind die Kernelemente der Musterantworten unterstrichen – also
 * jene Bestandteile, die eine Antwort in der Prüfung enthalten muss. Sie
 * werden hier als `h: true` geführt und tragen später das Freitext-Training.
 */
export interface TextSegment {
  /** Textinhalt des Abschnitts. */
  readonly t: string;
  /**
   * `true`, wenn die Stelle im amtlichen Katalog unterstrichen ist.
   *
   * Was das Amt damit meint, sagt es nirgends. In Musterantworten liest die
   * Anwendung es als Hervorhebung, in Fragetexten als Betonung (meist einer
   * Verneinung) – dieselbe Auszeichnung in zwei Rollen. Was sie NICHT ist:
   * eine Vorgabe, was eine richtige Antwort enthalten muss.
   */
  readonly h?: boolean;
}

/** Text mit Auszeichnung; `text` ist dieselbe Zeichenfolge ohne Auszeichnung. */
export interface RichText {
  /** Reiner Text – für Suche, Vorlesefunktion und Textvergleich. */
  readonly text: string;
  /** Derselbe Text, zerlegt in ausgezeichnete Abschnitte. */
  readonly segmente: readonly TextSegment[];
}

/** Abbildung im Katalog – ausschließlich amtliche Prüf- und Zulassungszeichen. */
export interface KatalogBild {
  readonly id: string;
  readonly datei: string;
  readonly breite: number;
  readonly hoehe: number;
  /**
   * Alternativtext. Redaktioneller Inhalt aus `content/alttexte.json`.
   * Bei Fragen, in denen die Zeichen selbst die Antwortoptionen sind,
   * beschreibt er nur die Form und verrät die Lösung nicht.
   */
  readonly alt: string | null;
  /**
   * Erklärende Bedeutung des Zeichens – zweite Stufe des Bildkonzepts aus
   * dem Prüfplan (Szenario S4). Ebenfalls redaktioneller Inhalt aus
   * `content/alttexte.json`. Weil sie die Lösung verraten kann, zeigt die
   * Oberfläche sie erst NACH dem Beantworten und nie im Prüfungslauf.
   */
  readonly beschreibung: string | null;
}

/** Eine Antwortmöglichkeit einer Multiple-Choice-Frage. */
export interface Antwortoption {
  /** Amtliches Label, „a“ bis „h“. */
  readonly label: string;
  readonly inhalt: RichText;
  readonly korrekt: boolean;
  /** Bild-IDs; bei einigen Fragen ist das Zeichen selbst die Antwort. */
  readonly bilder: readonly string[];
}

/**
 * Fragetyp.
 * - `mc`: Multiple Choice. Achtung: 114 der 471 MC-Fragen haben mehr als eine
 *   richtige Antwort, die Auswahl muss also mehrfach zulassen.
 * - `freitext`: offene Frage, wird gegen eine Musterantwort selbst bewertet.
 * - `lueckentext`: Sonderfall (nur Frage 5.01).
 */
export type Fragetyp = 'mc' | 'freitext' | 'lueckentext';

export interface Frage {
  /** Stabile ID, z. B. „I.1-01“ oder „IV-89“. */
  readonly id: string;
  /** Nummer wie im Katalog, z. B. „1.01“ – bleibt für Nutzer sichtbar. */
  readonly amtliche_nummer: string;
  readonly kapitel: string;
  readonly abschnitt: string | null;
  readonly typ: Fragetyp;
  /** Seite im Original-PDF – erlaubt das Nachschlagen in der Vorlage. */
  readonly seite: number;
  readonly frage: RichText;
  readonly bilder: readonly string[];
  /** Nur bei `typ === 'mc'`. */
  readonly optionen?: readonly Antwortoption[];
  /** Nur bei offenen Fragen und beim Lückentext. */
  readonly musterantwort?: RichText;
  /** Hinweise aus der Extraktion, die eine Sichtprüfung verdienen. */
  readonly warnungen?: readonly string[];
}

export interface Abschnitt {
  readonly id: string;
  readonly titel: string;
}

export interface Kapitel {
  readonly id: string;
  readonly titel: string;
  readonly abschnitte: readonly Abschnitt[];
}

export interface KatalogMeta {
  readonly titel: string;
  readonly herausgeber: string;
  /** Katalogstand als ISO-Datum, z. B. „2024-12-16“. */
  readonly stand: string;
  /** Pflichtangabe, die in der Oberfläche sichtbar geführt wird. */
  readonly quellenangabe: string;
  readonly quelle_url: string;
  readonly quelldatei_sha256: string;
  readonly fragen_gesamt: number;
}

export interface Katalog {
  readonly meta: KatalogMeta;
  readonly kapitel: readonly Kapitel[];
  readonly bilder: readonly KatalogBild[];
  readonly fragen: readonly Frage[];
}

// ─── Hilfsfunktionen ────────────────────────────────────────────────────────

/** Ist bei dieser Frage mehr als eine Antwort richtig? */
export function istMehrfachauswahl(frage: Frage): boolean {
  return anzahlRichtiger(frage) > 1;
}

export function anzahlRichtiger(frage: Frage): number {
  return frage.optionen?.filter((o) => o.korrekt).length ?? 0;
}

/** Labels aller richtigen Optionen, aufsteigend sortiert. */
export function richtigeLabels(frage: Frage): string[] {
  return (frage.optionen ?? []).filter((o) => o.korrekt).map((o) => o.label);
}

/**
 * Bewertet eine Multiple-Choice-Auswahl.
 *
 * Eine Antwort gilt nur als richtig, wenn genau die richtigen Optionen gewählt
 * wurden – nicht weniger und nicht mehr. Das entspricht der Prüfungspraxis:
 * eine unvollständige Auswahl ist keine richtige Antwort.
 */
export function bewerteAuswahl(
  frage: Frage,
  auswahl: readonly string[],
): { richtig: boolean; fehlend: string[]; zuviel: string[] } {
  const soll = new Set(richtigeLabels(frage));
  const ist = new Set(auswahl);
  const fehlend = [...soll].filter((l) => !ist.has(l)).sort();
  const zuviel = [...ist].filter((l) => !soll.has(l)).sort();
  return { richtig: fehlend.length === 0 && zuviel.length === 0, fehlend, zuviel };
}

/**
 * Die im amtlichen Katalog unterstrichenen Stellen einer Musterantwort.
 *
 * Hieß bis Fassung 0.17.0 „Kernelemente – die Bestandteile, die enthalten
 * sein müssen“. Der Name blieb, die Behauptung ist weg: Nachgemessen
 * schwankt der unterstrichene Anteil zwischen 3 und 100 Prozent der
 * Musterantwort, und bei 63 der 104 offenen Fragen ist gar nichts
 * unterstrichen. Als Pflichtinhalt taugt das nicht, als Lesehilfe schon.
 */
export function kernelemente(text: RichText | undefined): string[] {
  if (!text) return [];
  return text.segmente
    .filter((s) => s.h)
    .map((s) => s.t.trim())
    .filter((s) => s.length > 0);
}

/** Menschlich lesbare Bezeichnung eines Fragetyps. */
export function fragetypBezeichnung(typ: Fragetyp): string {
  switch (typ) {
    case 'mc':
      return 'Multiple Choice';
    case 'freitext':
      return 'Frage zum Ausformulieren';
    case 'lueckentext':
      return 'Lückentext';
  }
}
