/**
 * Der Weg vom Katalog zu Fehlerprotokoll und Fragenliste.
 *
 * Beide Dokumente holen ihre Fragen aus dem Anwendungskern und nicht aus der
 * Oberfläche – aus demselben Grund wie der Lernbericht: Ein Dokument nennt
 * eine Quelle und steht damit für seinen Inhalt gerade. Reichte die Oberfläche
 * die Fragen mit, könnte ein Anzeigefehler in ein Papier gelangen, das jemand
 * für bare Münze nimmt.
 *
 * **Die Fehlermenge kommt aus `lernstand.sitzung`**, nicht aus einer eigenen
 * Abfrage. Damit ist zugesichert, was der Auftrag verlangt: Was auf dem
 * Startbildschirm hinter „Nur Fehler“ steht, steht hier auf dem Papier. Eine
 * zweite Zählweise daneben wäre die Art Widerspruch, die in dieser Anwendung
 * schon zweimal eine Kennzahl gekostet hat.
 */

import type { BrowserWindow } from 'electron';

import { fundstelleText } from '../shared/erklaerungen';
import {
  fehlerprotokollBauen,
  fehlerprotokollDateiname,
  type Erklaerungstiefe,
} from '../shared/druck/fehlerprotokoll';
import { fragenlisteBauen, fragenlisteDateiname } from '../shared/druck/fragenliste';
import type { Papierfrage } from '../shared/druck/frageblock';
import type { Druckergebnis } from '../shared/ipc';
import type { Frage, Katalog } from '../shared/katalog';
import {
  papierfragen,
  quelleAusKatalog,
  schriftgroessePruefen,
  speichern,
  type Zeitgeber,
} from './dokument-export';
import { erklaerungenLaden } from './erklaerungen';
import type { Lernstand } from './lernstand';

export interface Fragendruckumgebung {
  readonly lernstand: Lernstand;
  readonly katalog: Katalog;
  readonly elternfenster: BrowserWindow | null;
  readonly jetzt: Zeitgeber;
}

/** Alternativtexte je Bild-ID; sie liegen bereits im Katalog. */
function alttexteAus(katalog: Katalog): ReadonlyMap<string, string> {
  return new Map(katalog.bilder.map((b) => [b.id, b.alt ?? '']));
}

/** Erklärungen in der Form, die der Papierbaustein erwartet. */
function erklaerungenFuer(
  ids: ReadonlySet<string>,
): ReadonlyMap<string, Papierfrage['erklaerung']> {
  const bestand = erklaerungenLaden().zuFrage;
  const karte = new Map<string, Papierfrage['erklaerung']>();
  for (const id of ids) {
    const e = bestand[id];
    if (e === undefined) {
      continue;
    }
    karte.set(id, {
      kurz: e.kurz,
      text: e.text,
      ...(e.merksatz === undefined ? {} : { merksatz: e.merksatz }),
      fundstellen: e.fundstellen.map(fundstelleText),
    });
  }
  return karte;
}

function tiefePruefen(wert: unknown): Erklaerungstiefe {
  return wert === 'voll' ? 'voll' : 'kurz';
}

// ── Fehlerprotokoll ─────────────────────────────────────────────────────

export async function fehlerprotokollExportieren(
  umgebung: Fragendruckumgebung,
  profilIdRoh: unknown,
  schriftgroesseRoh: unknown,
  tiefeRoh: unknown,
): Promise<Druckergebnis> {
  const { lernstand, katalog, elternfenster, jetzt } = umgebung;

  /* Derselbe Weg wie der Startbildschirm, nur ohne Deckel und ohne Mischen:
     Das Dokument muss ALLE betroffenen Fragen enthalten, und zwar in
     Katalogreihenfolge. `sitzung` prüft die Profil-ID mit denselben Regeln
     wie überall sonst. */
  const auswahl = lernstand.sitzung(profilIdRoh, {
    nurFehler: true,
    mischen: false,
    anzahl: katalog.fragen.length,
  });

  const nachId = new Map(katalog.fragen.map((f) => [f.id, f]));
  const fragen = auswahl
    .map((s) => nachId.get(s.frageId))
    .filter((f): f is Frage => f !== undefined);

  const profilId = typeof profilIdRoh === 'number' ? profilIdRoh : -1;
  const profilName = lernstand.profile().find((p) => p.id === profilId)?.name ?? 'Ohne Profil';
  const erstelltAm = jetzt().toISOString();
  const tiefe = tiefePruefen(tiefeRoh);

  const html = fehlerprotokollBauen({
    fragen: papierfragen(
      fragen,
      katalog,
      alttexteAus(katalog),
      erklaerungenFuer(new Set(fragen.map((f) => f.id))),
    ),
    profilName,
    erstelltAm,
    tiefe,
    schriftgroesse: schriftgroessePruefen(schriftgroesseRoh),
    quelle: quelleAusKatalog(katalog, {
      art: 'wiedergabe',
      fragen: fragen.length,
      fragenGesamt: katalog.fragen.length,
      loesungen: 'alle',
      optionen: 'alle',
    }),
  });

  return speichern({
    html,
    quelle: quelleAusKatalog(katalog, {
      art: 'wiedergabe',
      fragen: fragen.length,
      fragenGesamt: katalog.fragen.length,
      loesungen: 'alle',
      optionen: 'alle',
    }),
    dialogtitel: 'Fehlerprotokoll speichern',
    dateiname: fehlerprotokollDateiname(profilName, erstelltAm),
    elternfenster,
  });
}

// ── Fragenliste ─────────────────────────────────────────────────────────

/**
 * Grenzt den Katalog auf die gewählten Bereiche ein.
 *
 * Eine leere Auswahl bedeutet **alles**. Das ist die einzige sinnvolle
 * Lesart: Wer nichts einschränkt, will den ganzen Bogen – und der
 * Quellenblock sagt dann „alle 575 Fragen“ statt „einen Teil“.
 */
function eingegrenzt(katalog: Katalog, bereiche: readonly string[]): Frage[] {
  if (bereiche.length === 0) {
    return [...katalog.fragen];
  }
  const gewaehlt = new Set(bereiche);
  return katalog.fragen.filter(
    (f) => gewaehlt.has(f.kapitel) || (f.abschnitt !== null && gewaehlt.has(f.abschnitt)),
  );
}

/** Überschrift der Auswahl – nennt Bereiche beim Titel, nicht bei der Kennung. */
function auswahlTitel(katalog: Katalog, bereiche: readonly string[]): string {
  if (bereiche.length === 0) {
    return 'gesamter Fragenkatalog';
  }
  const titel = new Map<string, string>();
  for (const kapitel of katalog.kapitel) {
    titel.set(kapitel.id, `${kapitel.id} – ${kapitel.titel}`);
    for (const abschnitt of kapitel.abschnitte) {
      titel.set(abschnitt.id, `${abschnitt.id} – ${abschnitt.titel}`);
    }
  }
  return bereiche.map((b) => titel.get(b) ?? b).join(', ');
}

export async function fragenlisteExportieren(
  umgebung: Fragendruckumgebung,
  bereicheRoh: unknown,
  schriftgroesseRoh: unknown,
  mitLoesungenRoh: unknown,
): Promise<Druckergebnis> {
  const { katalog, elternfenster, jetzt } = umgebung;

  const bereiche = Array.isArray(bereicheRoh)
    ? bereicheRoh.filter((b): b is string => typeof b === 'string')
    : [];
  const fragen = eingegrenzt(katalog, bereiche);

  /* Ohne ausdrückliche Angabe MIT Lösungsanhang: Der Fall „ganzer Katalog
     ohne Lösungen“ ist der einzige, in dem gar keine Auszugserwägung mehr
     trägt – 575 von 575 Fragen, 0 von 629 Markierungen. Solange die Anfrage
     beim Bundesverwaltungsamt aussteht, ist die vorsichtige Vorgabe die
     richtige. Siehe LICENSES/LicenseRef-Amtliches-Werk-BVA.txt Nummer 2a. */
  const mitLoesungen = mitLoesungenRoh !== false;
  const erstelltAm = jetzt().toISOString();
  const auswahl = auswahlTitel(katalog, bereiche);

  const umfang = {
    art: 'wiedergabe',
    fragen: fragen.length,
    fragenGesamt: katalog.fragen.length,
    loesungen: mitLoesungen ? ('alle' as const) : ('keine' as const),
    optionen: 'alle' as const,
  } as const;

  const html = fragenlisteBauen({
    fragen: papierfragen(fragen, katalog, alttexteAus(katalog), new Map()),
    auswahl,
    erstelltAm,
    mitLoesungen,
    kapitel: katalog.kapitel,
    schriftgroesse: schriftgroessePruefen(schriftgroesseRoh),
    quelle: quelleAusKatalog(katalog, umfang),
  });

  return speichern({
    html,
    quelle: quelleAusKatalog(katalog, umfang),
    dialogtitel: 'Fragenliste speichern',
    dateiname: fragenlisteDateiname(auswahl, erstelltAm),
    elternfenster,
  });
}
