/**
 * Was ginge beim Ersetzen verloren? – reine Rechnerei, ohne Electron.
 *
 * ## Warum es diese Auszählung gibt
 *
 * Das Ersetzen legt eine Sicherheitskopie an und lässt sich zurückrollen –
 * aber nur, solange jemand die Kopie findet und weiss, dass es sie gibt. Die
 * bessere Sicherung ist, vorher zu wissen, was man aufgibt.
 *
 * Bisher stellte die Rückfrage nur zwei Gesamtzahlen nebeneinander. Das
 * genügt nicht: Wer hier zwei Profile mit zusammen 1.000 Antworten hat und
 * eine Datei mit einem Profil und 1.200 Antworten einliest, liest „mehr“ –
 * und verliert trotzdem eines seiner beiden Profile vollständig.
 *
 * ## Was hier NICHT behauptet wird
 *
 * Dass ein Befund ohne Fund gleichbedeutend mit „verlustfrei“ wäre. Verglichen
 * werden Profile über ihre **Namen** und je Profil vier Zahlen. Nicht
 * verglichen werden – und das steht auch so in der Anzeige:
 *
 * - **Prüfungstermin und Kapitelabwahl.** Beide werden mitersetzt. Die
 *   Kapitelabwahl verschiebt den Nenner des Reifegrads: Nachgerechnet fällt
 *   derselbe Bestand von 85,0 auf 71,9 Prozent, sobald Kapitel IV wieder
 *   mitzählt – über zwei Ampelschwellen, ohne dass eine Antwort verloren ging.
 * - **Welche** Antworten es sind. Zwei Dateien können dieselbe Zahl tragen
 *   und verschiedene Fragen meinen. Eine Zeile im Protokoll lässt sich über
 *   Geräte hinweg nicht zuverlässig wiedererkennen; das ist derselbe Grund,
 *   aus dem das Verschmelzen zweier Lernstände abgelehnt wurde
 *   (`docs/entscheidung-lernstand-verschmelzen.md`).
 * - Der unterbrochene Prüfungsbogen. Er wird gesondert genannt, weil er sich
 *   nicht in einer Zahl ausdrücken lässt.
 */

import type { Kennzahlen, Profilkennzahlen } from './sicherung';

/** Ein Profil, das es hier gibt und in der Datei nicht. */
export interface FehlendesProfil {
  readonly name: string;
  readonly antworten: number;
}

/** Ein Profil, das es in beiden gibt, hier aber mit mehr Inhalt. */
export interface GeschrumpftesProfil {
  readonly name: string;
  /** Antworten hier minus Antworten in der Datei; immer grösser als 0. */
  readonly antwortenWeniger: number;
  readonly gemerktWeniger: number;
  readonly laeufeWeniger: number;
}

export interface Verlustbefund {
  /** Profile, die die Datei überhaupt nicht kennt. */
  readonly fehlendeProfile: readonly FehlendesProfil[];
  /** Profile, die in der Datei weniger enthalten als hier. */
  readonly geschrumpfteProfile: readonly GeschrumpftesProfil[];
  /** Ein unterbrochener Bogen läge hier und ginge verloren. */
  readonly offenerBogenGehtVerloren: boolean;
  /** Nichts von dem, was gezählt wurde, fehlt in der Datei. */
  readonly ohneBefund: boolean;
}

function nachNamen(zahlen: readonly Profilkennzahlen[]): ReadonlyMap<string, Profilkennzahlen> {
  return new Map(zahlen.map((p) => [p.name, p]));
}

/**
 * Vergleicht den jetzigen Stand mit dem aus der Datei.
 *
 * Der Name ist der einzige Anker. Die Nummern werden auf jedem Gerät
 * unabhängig vergeben und sagen über die Zugehörigkeit nichts – genau
 * deshalb wechselt die Oberfläche nach dem Ersetzen zwingend ins erste
 * Profil der eingespielten Datei (siehe `Einspielergebnis.ersteProfilId`).
 */
export function verlustbefund(jetzt: Kennzahlen, ausDatei: Kennzahlen): Verlustbefund {
  const inDatei = nachNamen(ausDatei.jeProfil);

  const fehlendeProfile: FehlendesProfil[] = [];
  const geschrumpfteProfile: GeschrumpftesProfil[] = [];

  for (const hier of jetzt.jeProfil) {
    const dort = inDatei.get(hier.name);
    if (dort === undefined) {
      fehlendeProfile.push({ name: hier.name, antworten: hier.antworten });
      continue;
    }

    const antwortenWeniger = Math.max(0, hier.antworten - dort.antworten);
    const gemerktWeniger = Math.max(0, hier.gemerkt - dort.gemerkt);
    const laeufeWeniger = Math.max(0, hier.pruefungslaeufe - dort.pruefungslaeufe);

    if (antwortenWeniger > 0 || gemerktWeniger > 0 || laeufeWeniger > 0) {
      geschrumpfteProfile.push({
        name: hier.name,
        antwortenWeniger,
        gemerktWeniger,
        laeufeWeniger,
      });
    }
  }

  return {
    fehlendeProfile,
    geschrumpfteProfile,
    offenerBogenGehtVerloren: jetzt.offenerBogen,
    ohneBefund:
      fehlendeProfile.length === 0 && geschrumpfteProfile.length === 0 && !jetzt.offenerBogen,
  };
}

/** Ein Wort für „n Antworten“, im richtigen Numerus. */
function antwortwort(n: number): string {
  return n === 1 ? '1 Antwort' : `${String(n)} Antworten`;
}

/**
 * Der Befund als Sätze.
 *
 * Bewusst keine Gesamtzahl und keine Note: Was zählt, ist welches Profil
 * betroffen ist. Eine Summe über verschiedene Profile hinweg sagte nichts.
 */
export function verlustsaetze(befund: Verlustbefund): readonly string[] {
  const saetze: string[] = [];

  for (const p of befund.fehlendeProfile) {
    saetze.push(
      `Das Profil „${p.name}“ kommt in der Datei nicht vor. Es geht mit ${antwortwort(p.antworten)} verloren.`,
    );
  }

  for (const p of befund.geschrumpfteProfile) {
    const teile: string[] = [];
    if (p.antwortenWeniger > 0) {
      teile.push(antwortwort(p.antwortenWeniger));
    }
    if (p.gemerktWeniger > 0) {
      teile.push(
        `${String(p.gemerktWeniger)} gemerkte ${p.gemerktWeniger === 1 ? 'Frage' : 'Fragen'}`,
      );
    }
    if (p.laeufeWeniger > 0) {
      teile.push(
        `${String(p.laeufeWeniger)} ${p.laeufeWeniger === 1 ? 'Prüfungslauf' : 'Prüfungsläufe'}`,
      );
    }
    saetze.push(
      `Im Profil „${p.name}“ enthält die Datei ${teile.join(', ')} weniger als dieser Rechner.`,
    );
  }

  if (befund.offenerBogenGehtVerloren) {
    saetze.push(
      'Ein unterbrochener Prüfungsbogen liegt hier und lässt sich danach nicht fortsetzen.',
    );
  }

  return saetze;
}

/**
 * Der Satz für den Fall ohne Befund.
 *
 * Er sagt ausdrücklich, **was** verglichen wurde – nicht „alles stimmt“.
 * Ein unbedingter Satz über einen Inhalt, der nicht vollständig geprüft
 * wurde, ist genau der Fehler, den `docs/stand.md` 7.6 zuletzt behoben hat.
 */
export const OHNE_BEFUND_SATZ =
  'Verglichen wurden Profile, Antworten, gemerkte Fragen und Prüfungsläufe. ' +
  'Davon fehlt in der Datei nichts. Nicht verglichen wurden Prüfungstermin und ' +
  'Kapitelabwahl – beide werden mitersetzt, und die Kapitelabwahl verändert Ihren ' +
  'angezeigten Lernstand, ohne dass eine Antwort verloren geht.';
