/**
 * Vom Auswahlformular zum Prüfungsauftrag – reine Funktionen ohne React.
 *
 * Der Auftrag ({@link Pruefungsauftrag}) ist Vertragsbestandteil und wird
 * unverändert an den Anwendungskern gereicht. Anpassbare Profile dürfen dabei
 * Fragenzahl, Zeit und Bestehensgrenze überschreiben – alle anderen nicht.
 */

import {
  PRUEFUNGSPROFILE,
  ZEITMODUS_BEZEICHNUNG,
  zeitInMinuten,
  type Pruefungsauftrag,
  type Pruefungsprofil,
  type Zeitmodus,
} from '../../../shared/pruefung';
import type { Pruefungseinstellung } from './typen';

/** Kapitel, das sich abwählen lässt, wenn die Prüfungsstelle es nicht prüft. */
export const KAPITEL_IV = 'IV';

/**
 * Reihenfolge der Zeitstufen in der Oberfläche.
 *
 * „Ohne Zeitbegrenzung“ steht bewusst an erster Stelle und gleichrangig neben
 * den übrigen Stufen (WCAG 2.2.1) – es ist keine versteckte Ausnahme.
 */
export const ZEITMODI: readonly Zeitmodus[] = Object.freeze([
  'aus',
  'normal',
  'plus25',
  'plus50',
] as const);

/** Grenzen der frei einstellbaren Werte. */
export const GRENZEN = Object.freeze({
  fragenMin: 5,
  fragenMax: 575,
  zeitMin: 1,
  zeitMax: 600,
  prozentMin: 50,
  prozentMax: 100,
  offenMin: 0,
  /*
    Bis 100 wählbar, obwohl der Katalog nur 104 offene Fragen von 575 hat.
    Eine feste Obergrenze wäre hier falsch: Auf einem Bogen von 40 Fragen sind
    100 Prozent erreichbar, auf einem von 200 nicht.

    Stattdessen rechnet die Wahl den Wunsch in Fragen um und sagt vorher, wie
    viele davon wirklich da sind. Der Anwendungskern meldet das Auffüllen zwar
    inzwischen auch über `Pruefungsbogen.warnungen` – aber erst am fertigen
    Bogen, wenn die Uhr schon läuft.
  */
  offenMax: 100,
});

/** Hält einen Zahlenwert in seinen Grenzen; unbrauchbare Eingaben ergeben `min`. */
export function begrenzen(wert: number, min: number, max: number): number {
  return Number.isFinite(wert) ? Math.min(max, Math.max(min, Math.round(wert))) : min;
}

/**
 * Anfangswerte des Formulars.
 *
 * Die frei einstellbaren Werte stammen aus dem anpassbaren Profil des
 * Vertrags; nur wenn der Katalog der Profile einmal anders aussähe, greifen
 * die hier genannten Ersatzwerte.
 *
 * `kapitelAusschluss` ist der Ausschluss des **Lernprofils**. Er entscheidet
 * über die Vorbelegung des Kästchens, und das ist keine Bequemlichkeit: Die
 * Erststart-Frage sagt zu, dass abgewählte Fragen „in keiner Sitzung und in
 * keiner Zahl mehr“ vorkommen. Bis Fassung 0.20.0 stand das Kästchen fest auf
 * „einbeziehen“ – wer Kapitel IV abgewählt hatte, musste 89 von 575 Fragen in
 * jeder Simulation von Hand wieder abwählen. Änderbar bleibt es: Der Ausschluss
 * gilt für das tägliche Lernen, die Simulation darf davon abweichen.
 */
export function standardEinstellung(
  kapitelAusschluss: readonly string[] = [],
): Pruefungseinstellung {
  const frei = PRUEFUNGSPROFILE.find((profil) => profil.anpassbar === true);
  return {
    profilId: PRUEFUNGSPROFILE[0]?.id ?? 'standard',
    zeitmodus: 'normal',
    fragenAnzahl: frei?.fragenAnzahl ?? 40,
    zeitMinuten: frei?.zeitMinuten ?? 60,
    bestehensProzent: Math.round((frei?.bestehensQuote ?? 0.75) * 100),
    anteilOffenProzent: Math.round((frei?.anteilOffen ?? 0) * 100),
    kapitelIVEinbeziehen: !kapitelAusschluss.includes(KAPITEL_IV),
  };
}

/** Baut den Auftrag; feste Profile bekommen keine Überschreibungen. */
export function auftragBilden(
  profil: Pruefungsprofil,
  einstellung: Pruefungseinstellung,
): Pruefungsauftrag {
  const basis: Pruefungsauftrag = {
    profilId: profil.id,
    zeitmodus: einstellung.zeitmodus,
    kapitelAusschluss: einstellung.kapitelIVEinbeziehen ? [] : [KAPITEL_IV],
  };

  if (profil.anpassbar !== true) {
    return basis;
  }

  return {
    ...basis,
    fragenAnzahl: begrenzen(einstellung.fragenAnzahl, GRENZEN.fragenMin, GRENZEN.fragenMax),
    bestehensQuote:
      begrenzen(einstellung.bestehensProzent, GRENZEN.prozentMin, GRENZEN.prozentMax) / 100,
    zeitMinuten: begrenzen(einstellung.zeitMinuten, GRENZEN.zeitMin, GRENZEN.zeitMax),
    anteilOffen:
      begrenzen(einstellung.anteilOffenProzent, GRENZEN.offenMin, GRENZEN.offenMax) / 100,
  };
}

/**
 * Profil mit den tatsächlich geltenden Werten.
 *
 * Damit rechnen Anzeige und Auswertung mit denselben Zahlen – auch beim frei
 * eingestellten Profil.
 */
export function wirksamesProfil(
  profil: Pruefungsprofil,
  auftrag: Pruefungsauftrag,
): Pruefungsprofil {
  if (profil.anpassbar !== true) {
    return profil;
  }

  const basis: Pruefungsprofil = {
    ...profil,
    fragenAnzahl: auftrag.fragenAnzahl ?? profil.fragenAnzahl,
    zeitMinuten: auftrag.zeitMinuten === undefined ? profil.zeitMinuten : auftrag.zeitMinuten,
  };

  // Getrennter Zweig: `exactOptionalPropertyTypes` verbietet ein ausdrückliches
  // `undefined` für eine optionale Eigenschaft.
  /* Getrennte Zweige aus demselben Grund wie oben: `exactOptionalPropertyTypes`
     verbietet ein ausdrückliches `undefined` für eine optionale Eigenschaft. */
  const mitQuote =
    auftrag.bestehensQuote === undefined
      ? basis
      : { ...basis, bestehensQuote: auftrag.bestehensQuote };

  return auftrag.anteilOffen === undefined
    ? mitQuote
    : { ...mitQuote, anteilOffen: auftrag.anteilOffen };
}

/** Tatsächliche Bearbeitungszeit in Minuten; `null` bedeutet ohne Begrenzung. */
export function wirksameZeitMinuten(
  profil: Pruefungsprofil,
  auftrag: Pruefungsauftrag,
): number | null {
  return zeitInMinuten(wirksamesProfil(profil, auftrag), auftrag.zeitmodus);
}

// ─── Klartexte für die Anzeige ──────────────────────────────────────────

export function fragenText(profil: Pruefungsprofil): string {
  return `${String(profil.fragenAnzahl)} Fragen`;
}

export function zeitText(minuten: number | null): string {
  return minuten === null ? 'ohne Zeitbegrenzung' : `${String(minuten)} Minuten`;
}

/** Was zum Bestehen nötig ist – Quote oder Fehlerpunktegrenze. */
export function bestehensText(profil: Pruefungsprofil): string {
  if (profil.wertung === 'fehlerpunkte') {
    return `höchstens ${String(profil.maxFehler ?? 0)} Fehler`;
  }
  return `${String(Math.round((profil.bestehensQuote ?? 0.75) * 100))} Prozent richtig`;
}

/** Beschriftung einer Zeitstufe samt der Zeit, die sich daraus ergibt. */
export function zeitmodusText(profil: Pruefungsprofil, modus: Zeitmodus): string {
  const bezeichnung = ZEITMODUS_BEZEICHNUNG[modus];
  if (modus === 'aus') {
    return bezeichnung;
  }
  const minuten = zeitInMinuten(profil, modus);
  return minuten === null
    ? `${bezeichnung} – dieses Profil sieht keine Zeit vor`
    : `${bezeichnung} – ${zeitText(minuten)}`;
}
