/**
 * Der Weg vom Lernstand zur gespeicherten PDF-Datei.
 *
 * Getrennt von `druck.ts` (das nur aus HTML ein PDF macht) und von
 * `shared/druck/` (reine Textbausteine ohne Electron). Hier kommt zusammen,
 * was ohne Electron nicht geht: Daten holen, Datei anbieten, schreiben.
 *
 * **Warum der Kern die Zahlen selbst holt.** Die Oberfläche schickt nur, für
 * welches Profil und in welcher Schriftgröße. Reichte sie die Zahlen mit,
 * könnte ein Fehler in der Anzeige in ein Dokument gelangen, das jemand für
 * bare Münze nimmt – und das Dokument nennt eine Quelle, steht also für
 * seinen Inhalt gerade.
 */

import { join } from 'node:path';

import { app, dialog, type BrowserWindow } from 'electron';

import { type Quellenangabe } from '../shared/druck/dokument';
import { dateiname, lernberichtBauen } from '../shared/druck/lernbericht';
import type { Schriftgroesse } from '../shared/druck/stil';
import type { Druckergebnis } from '../shared/ipc';
import type { Katalog } from '../shared/katalog';
import { geschriebenMerken } from './dateizugriff';
import { pdfSchreiben } from './dokument-export';
import { pdfErzeugen } from './druck';
import type { Lernstand } from './lernstand';
import type { Pruefung } from './pruefung';

/**
 * Baut die Quellenangabe aus den Katalogmetadaten.
 *
 * Ohne Katalog gibt es keinen Export: Die Angabe ließe sich dann nicht
 * belegen, und ein Dokument mit erfundener oder fehlender Herkunft ist
 * schlechter als gar keines.
 */
export function quelleAusKatalog(katalog: Katalog, umfang: Quellenangabe['umfang']): Quellenangabe {
  const { quellenangabe, herausgeber, stand, quelle_url } = katalog.meta;
  if (quellenangabe.trim().length === 0) {
    throw new Error('Der Fragenkatalog nennt keine Quellenangabe – es wird nichts exportiert.');
  }
  return { amtlich: quellenangabe, herausgeber, stand, quelleUrl: quelle_url, umfang };
}

function schriftgroessePruefen(wert: unknown): Schriftgroesse {
  return wert === 'gross' ? 'gross' : 'normal';
}

/**
 * Ein Zeitstempel, den Aufrufer setzen können.
 *
 * Ein fest verdrahtetes `new Date()` machte den Export untestbar: Das
 * Dokument trüge bei jedem Lauf ein anderes Datum, und ein Vergleich gegen
 * einen Sollwert wäre unmöglich.
 */
export type Zeitgeber = () => Date;

export interface Exportumgebung {
  readonly lernstand: Lernstand;
  readonly pruefung: Pruefung;
  readonly katalog: Katalog;
  /** Fenster, an dem der Speicherdialog hängt – modal ist für Screenreader klarer. */
  readonly elternfenster: BrowserWindow | null;
  readonly jetzt: Zeitgeber;
}

/**
 * Erzeugt den Lernbericht und speichert ihn dorthin, wohin der Nutzer zeigt.
 *
 * Ein Abbruch im Dialog ist kein Fehler: `gespeichert: false`. Alles andere –
 * fehlendes Schreibrecht, voller Datenträger – wirft, damit die Oberfläche es
 * sagen kann statt so zu tun, als sei alles gut gegangen.
 */
export async function lernberichtExportieren(
  umgebung: Exportumgebung,
  profilIdRoh: unknown,
  schriftgroesseRoh: unknown,
): Promise<Druckergebnis> {
  const { lernstand, pruefung, katalog, elternfenster, jetzt } = umgebung;

  /* Die Prüfung der Profil-ID übernimmt der Lernstand – dieselben Regeln wie
     überall sonst, an einer Stelle. */
  const uebersicht = lernstand.uebersicht(profilIdRoh);
  const verlauf = pruefung.verlauf(profilIdRoh);

  /* Der Lernplan rechnet über den gesamten Katalog. Scheitert er, soll der
     Bericht trotzdem entstehen – die übrigen Zahlen sind davon unberührt,
     und ein Bericht ohne Planabschnitt ist besser als keiner. */
  let plan = null;
  try {
    plan = lernstand.lernplan(profilIdRoh);
  } catch (fehler: unknown) {
    console.warn('[druck] Lernplan nicht ermittelbar, Bericht entsteht ohne ihn:', fehler);
  }

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

  const quelle = quelleAusKatalog(katalog, { art: 'kein amtlicher wortlaut' });
  const html = lernberichtBauen({
    uebersicht,
    plan,
    verlauf,
    profilName,
    quelle,
    erstelltAm,
    schriftgroesse: schriftgroessePruefen(schriftgroesseRoh),
  });

  const vorschlag = join(app.getPath('documents'), dateiname(profilName, erstelltAm));
  const auswahl = await (elternfenster === null
    ? dialog.showSaveDialog(dialogOptionen(vorschlag))
    : dialog.showSaveDialog(elternfenster, dialogOptionen(vorschlag)));

  if (auswahl.canceled || auswahl.filePath === '') {
    return { gespeichert: false, pfad: null, bytes: 0 };
  }

  /* Erst nach der Zusage des Nutzers wird gerechnet: Ein PDF zu erzeugen,
     das niemand haben will, kostet nur Zeit. */
  const pdf = await pdfErzeugen({ html, quelle });
  /* Unteilbar geschrieben – siehe `pdfSchreiben`. Ein Abbruch mittendrin
     hinterließ sonst eine halbe Datei unter dem endgültigen Namen. */
  pdfSchreiben(auswahl.filePath, pdf);

  return {
    gespeichert: true,
    pfad: auswahl.filePath,
    bytes: pdf.byteLength,
    /* Erst nach dem Schreiben gemerkt: Was nicht existiert, soll sich auch
       nicht öffnen lassen. Ohne diese Kennung blieben „Datei öffnen“ und „Im
       Ordner anzeigen“ unter dem Lernbericht unsichtbar – `Dateiknoepfe`
       stand dort seit 0.22.0, bekam aber nie eine. Als einzige der vier
       Ausgaben endete der Bericht damit bei einem Pfad zum Abschreiben. */
    dateiKennung: geschriebenMerken(auswahl.filePath),
  };
}

function dialogOptionen(vorschlag: string): Electron.SaveDialogOptions {
  return {
    title: 'Lernbericht speichern',
    defaultPath: vorschlag,
    buttonLabel: 'Speichern',
    filters: [{ name: 'PDF-Dokument', extensions: ['pdf'] }],
    /* Überschreiben nur nach Rückfrage, und ein neuer Ordner soll sich
       anlegen lassen – sonst muss der Nutzer den Dialog verlassen, um Platz
       für seine Datei zu schaffen. */
    properties: ['showOverwriteConfirmation', 'createDirectory'],
  };
}
