/**
 * Zusammenstellen der Fragen einer Lernsitzung – reine Funktionen.
 *
 * Regelfall ist der Kanal `lernen:sitzung`: der Anwendungskern kennt den
 * Lernstand und plant die Wiedervorlage. Steht er nicht bereit, stellt die
 * Oberfläche eine Ersatzsitzung direkt aus dem Katalog zusammen, damit man
 * trotzdem üben kann (siehe {@link brauchtLernstand} für die Grenzen).
 */

import type { Antwortoption, Frage, Katalog } from '../../../shared/katalog';
import type { SitzungsFilter } from '../../../shared/lernstand';

/** Zufällige Reihenfolge, ohne Zugriff über Indizes. */
export function mischen<T>(liste: readonly T[]): T[] {
  return liste
    .map((wert) => ({ wert, schluessel: Math.random() }))
    .sort((a, b) => a.schluessel - b.schluessel)
    .map((eintrag) => eintrag.wert);
}

/**
 * Braucht dieser Filter den gespeicherten Lernstand?
 * „Nur Fehler“, „Nur Gemerkte“ und „Nur Neue“ lassen sich ohne ihn nicht
 * beantworten – dafür gibt es keinen sinnvollen Ersatz aus dem Katalog.
 *
 * `nurOffene` steht bewusst **nicht** dabei: Der Fragetyp steht im Katalog.
 * Die Ersatzzusammenstellung unten kann ihn deshalb selbst einhalten – und
 * muss es auch, sonst legte sie unter der Überschrift „Offene Fragen“
 * stillschweigend Auswahlfragen vor.
 */
export function brauchtLernstand(filter: SitzungsFilter): boolean {
  return filter.nurFehler === true || filter.nurGemerkte === true || filter.nurNeue === true;
}

/** Ersatzzusammenstellung aus dem Katalog, wenn kein Lernstand verfügbar ist. */
export function fragenAusKatalog(katalog: Katalog, filter: SitzungsFilter): Frage[] {
  const kapitel = new Set(filter.kapitel ?? []);
  const abschnitte = new Set(filter.abschnitte ?? []);

  const passend = katalog.fragen.filter((frage) => {
    if (kapitel.size > 0 && !kapitel.has(frage.kapitel)) {
      return false;
    }
    if (abschnitte.size > 0 && (frage.abschnitt === null || !abschnitte.has(frage.abschnitt))) {
      return false;
    }
    if (filter.nurOffene === true && frage.typ === 'mc') {
      return false;
    }
    return true;
  });

  const geordnet = filter.mischen === false ? passend : mischen(passend);
  const anzahl = filter.anzahl;
  return anzahl !== undefined && anzahl > 0 ? geordnet.slice(0, anzahl) : geordnet;
}

/**
 * Bringt die Antwortoptionen in die vom Anwendungskern vorgegebene
 * Reihenfolge. Optionen, die dort fehlen, hängen hinten an – lieber eine
 * vollständige Frage als eine stillschweigend verkürzte.
 */
export function reihenfolgeAnwenden(
  frage: Frage,
  reihenfolge: readonly string[],
): readonly Antwortoption[] {
  const optionen = frage.optionen ?? [];
  if (reihenfolge.length === 0) {
    return optionen;
  }

  const nachLabel = new Map(optionen.map((option) => [option.label, option]));
  const sortiert = reihenfolge
    .map((label) => nachLabel.get(label))
    .filter((option): option is Antwortoption => option !== undefined);
  const genannt = new Set(sortiert.map((option) => option.label));

  return [...sortiert, ...optionen.filter((option) => !genannt.has(option.label))];
}
