import { useCallback, useId, useMemo, useState } from 'react';

import {
  PRUEFUNGSPROFILE,
  profilFinden,
  type Pruefungsauftrag,
  type Zeitmodus,
} from '../../../../shared/pruefung';
import {
  auftragBilden,
  bestehensText,
  fragenText,
  GRENZEN,
  KAPITEL_IV,
  standardEinstellung,
  wirksamesProfil,
  zeitmodusText,
  zeitText,
  ZEITMODI,
} from '../../pruefung/auftrag';
import type { KatalogIndex } from '../../hooks/useKatalog';
import type { Pruefungseinstellung } from '../../pruefung/typen';
import { Hinweis } from '../lernen/Hinweis';

export interface PruefungswahlProps {
  readonly einstiegRef: React.RefObject<HTMLHeadingElement | null>;
  /** Titel des abwählbaren Kapitels IV, sofern der Katalog geladen ist. */
  readonly kapitelIVTitel: string | null;
  /**
   * Kapitelausschluss des Lernprofils – die Vorbelegung des Kästchens.
   *
   * Die Erststart-Frage und der Schalter unter „Ihr Lernplan“ sagen zu, dass
   * abgewählte Fragen „in keiner Sitzung und in keiner Zahl mehr“ vorkommen.
   * Ohne diesen Wert stand das Kästchen fest auf „einbeziehen“, und wer
   * Kapitel IV abgewählt hatte, musste 89 von 575 Fragen in jeder Simulation
   * von Hand wieder abwählen.
   */
  readonly kapitelAusschluss: readonly string[];
  /**
   * Für die Auskunft, wie viele offene Fragen überhaupt zur Verfügung stehen.
   *
   * Darf `null` sein: Ohne geladenen Katalog entfällt die Auskunft, die Wahl
   * bleibt bedienbar. Eine Zahl zu erfinden wäre hier das Schlechtere.
   */
  readonly katalogIndex: KatalogIndex | null;
  readonly onStarten: (auftrag: Pruefungsauftrag) => void;
  readonly onZurueck: () => void;
}

const VORGABE = standardEinstellung();

/** Zahleneingabe der frei einstellbaren Werte. */
function Zahlenfeld({
  beschriftung,
  wert,
  min,
  max,
  einheit,
  onWert,
}: {
  readonly beschriftung: string;
  readonly wert: string;
  readonly min: number;
  readonly max: number;
  readonly einheit: string;
  readonly onWert: (wert: string) => void;
}): React.JSX.Element {
  const feldId = useId();
  const hinweisId = useId();

  /* Der Auftrag kappt Werte außerhalb der Grenzen ohnehin. Sie stillschweigend
     zu korrigieren wäre aber irreführend: Im Feld stünde 900, gerechnet würde
     mit 575. Deshalb wird der wirksame Wert benannt und das Feld als
     fehlerhaft gemeldet (WCAG 3.3.1 und 3.3.3). */
  const zahl = Number.parseInt(wert, 10);
  const leer = wert.trim().length === 0;
  const wirksam = leer || Number.isNaN(zahl) ? min : Math.min(max, Math.max(min, zahl));
  const ungueltig = leer || Number.isNaN(zahl) || zahl < min || zahl > max;

  return (
    <div className="freiwert">
      <label className="freiwert__beschriftung" htmlFor={feldId}>
        {beschriftung}
      </label>
      <input
        className="freiwert__feld"
        id={feldId}
        type="number"
        inputMode="numeric"
        min={min}
        max={max}
        step={1}
        value={wert}
        aria-describedby={hinweisId}
        aria-invalid={ungueltig}
        onChange={(ereignis) => {
          onWert(ereignis.target.value);
        }}
      />
      <p
        className={`freiwert__hinweis${ungueltig ? ' freiwert__hinweis--fehler' : ''}`}
        id={hinweisId}
      >
        {ungueltig
          ? `Bitte eine Zahl zwischen ${String(min)} und ${String(max)} eintragen. Derzeit wird mit ${String(wirksam)} gerechnet.`
          : `${einheit}, zulässig sind ${String(min)} bis ${String(max)}.`}
      </p>
    </div>
  );
}

/**
 * Auswahl des Prüfungsprofils, der Zeitstufe und des Umfangs.
 *
 * Es gibt bewusst kein „richtiges“ Profil: Die AWaffV schreibt weder
 * Fragenzahl noch Zeit oder Bestehensgrenze vor, die Prüfungsstellen handhaben
 * das unterschiedlich. Darum stehen die Eckdaten jedes Profils im Klartext –
 * und die Zeitstufen einschließlich „Ohne Zeitbegrenzung“ gleichrangig
 * nebeneinander (WCAG 2.2.1).
 */
export function Pruefungswahl({
  einstiegRef,
  kapitelIVTitel,
  kapitelAusschluss,
  katalogIndex,
  onStarten,
  onZurueck,
}: PruefungswahlProps): React.JSX.Element {
  const profilGruppe = useId();
  const zeitGruppe = useId();
  const kapitelFeldId = useId();
  const kapitelHinweisId = useId();
  const grundId = useId();

  const [profilId, setzeProfilId] = useState(VORGABE.profilId);
  const [zeitmodus, setzeZeitmodus] = useState<Zeitmodus>(VORGABE.zeitmodus);
  const [fragenRoh, setzeFragenRoh] = useState(String(VORGABE.fragenAnzahl));
  const [zeitRoh, setzeZeitRoh] = useState(String(VORGABE.zeitMinuten));
  const [prozentRoh, setzeProzentRoh] = useState(String(VORGABE.bestehensProzent));
  const [offenRoh, setzeOffenRoh] = useState(String(VORGABE.anteilOffenProzent));
  /* Vorbelegt aus dem Lernprofil, danach je Simulation frei änderbar. Der Wert
     wird einmal beim Aufbau gelesen: Diese Ansicht wird vom Startbildschirm
     aus geöffnet, das Profil steht dort längst fest. */
  const [kapitelIVEinbeziehen, setzeKapitelIV] = useState(
    () => standardEinstellung(kapitelAusschluss).kapitelIVEinbeziehen,
  );
  /* Ob das Lernprofil Kapitel IV überhaupt abgewählt hat – nur dann ist der
     Satz über die Herkunft der Vorbelegung wahr. */
  const imProfilAbgewaehlt = kapitelAusschluss.includes(KAPITEL_IV);

  const gewaehltesProfil = profilFinden(profilId);

  /*
    Wie viele offene Fragen wirklich zur Verfügung stehen – gerechnet mit der
    gerade getroffenen Kapitelwahl, nicht mit dem ganzen Katalog. Wer Kapitel
    IV abwählt, verliert 29 der 104; ihm eine Zahl zu nennen, die für ihn nicht
    gilt, wäre die schlechtere Auskunft.
  */
  const offeneImVorrat = useMemo(() => {
    if (katalogIndex === null) {
      return null;
    }
    return katalogIndex.katalog.fragen.filter(
      (frage) => frage.typ !== 'mc' && (kapitelIVEinbeziehen || frage.kapitel !== KAPITEL_IV),
    ).length;
  }, [katalogIndex, kapitelIVEinbeziehen]);

  const einstellung: Pruefungseinstellung = useMemo(
    () => ({
      profilId,
      zeitmodus,
      fragenAnzahl: Number.parseInt(fragenRoh, 10),
      zeitMinuten: Number.parseInt(zeitRoh, 10),
      bestehensProzent: Number.parseInt(prozentRoh, 10),
      anteilOffenProzent: Number.parseInt(offenRoh, 10),
      kapitelIVEinbeziehen,
    }),
    [fragenRoh, kapitelIVEinbeziehen, offenRoh, profilId, prozentRoh, zeitmodus, zeitRoh],
  );

  const auftrag = useMemo(
    () => (gewaehltesProfil ? auftragBilden(gewaehltesProfil, einstellung) : null),
    [einstellung, gewaehltesProfil],
  );

  /* Vorschau mit den Werten, die tatsächlich gelten – beim frei eingestellten
     Profil also mit den eingetippten Zahlen und nicht mit den Vorgaben. */
  const vorschau = useMemo(
    () => (gewaehltesProfil && auftrag ? wirksamesProfil(gewaehltesProfil, auftrag) : null),
    [auftrag, gewaehltesProfil],
  );

  /*
    Der Wunsch in Fragen, und daneben, was davon wirklich zu haben ist.

    Diese Auskunft steht bewusst VOR dem Start. `Pruefung.zieheTeil` meldet es
    inzwischen ebenfalls, wenn es mit Auswahlfragen auffüllen musste – über
    `Pruefungsbogen.warnungen` bis an den Bildschirm –, aber erst im laufenden
    Bogen. Vorher Bescheid zu wissen schlägt eine Meldung hinterher: Wer 50
    Prozent einstellt und 18 bekommt, soll das entscheiden können, bevor die
    Uhr läuft.
  */
  const offenAuskunft = useMemo(() => {
    if (offeneImVorrat === null || vorschau === null) {
      return null;
    }
    const gewuenscht = Math.round(vorschau.fragenAnzahl * (vorschau.anteilOffen ?? 0));
    return { gewuenscht, verfuegbar: offeneImVorrat, reicht: gewuenscht <= offeneImVorrat };
  }, [offeneImVorrat, vorschau]);

  /* Kehrt bei unbekanntem Profil früh zurück, statt den Knopf zu sperren:
     `aria-disabled` hält ihn erreichbar (Projektregel, docs/stand.md 7.13). */
  const starten = useCallback(() => {
    if (auftrag === null) {
      return;
    }
    onStarten(auftrag);
  }, [auftrag, onStarten]);

  const anpassbar = gewaehltesProfil?.anpassbar === true;

  return (
    <section className="pruefungswahl abschnitt" aria-labelledby="pruefungswahl-titel">
      <h1 id="pruefungswahl-titel" ref={einstiegRef} tabIndex={-1}>
        Prüfungssimulation vorbereiten
      </h1>

      <p>
        In einer Simulation bearbeiten Sie einen Bogen – den Fragensatz eines Durchgangs – am Stück,
        ohne Rückmeldung zu den einzelnen Antworten. Erst nach der Abgabe erfahren Sie, wie Sie
        abgeschnitten haben.
      </p>

      <Hinweis art="hinweis">
        Keines dieser Profile ist „die“ amtliche Prüfung. Reale Prüfungen unterscheiden sich je nach
        Prüfungsstelle in Fragenzahl, Zeit und Bestehensgrenze. Sie haben außerdem einen praktischen
        Teil, den diese Anwendung nicht abbildet, und vielerorts mündliche Fragen. Über das Bestehen
        entscheidet allein der zuständige Prüfungsausschuss.
      </Hinweis>

      <fieldset className="profilwahl">
        <legend className="profilwahl__titel">Prüfungsprofil</legend>
        <div className="profilwahl__liste">
          {PRUEFUNGSPROFILE.map((profil) => {
            const feldId = `${profilGruppe}-${profil.id}`;
            const erklaerungId = `${profilGruppe}-${profil.id}-erklaerung`;
            return (
              <div className="profilwahl__option" key={profil.id}>
                <input
                  className="profilwahl__feld"
                  id={feldId}
                  type="radio"
                  name={profilGruppe}
                  value={profil.id}
                  checked={profil.id === profilId}
                  aria-describedby={erklaerungId}
                  onChange={() => {
                    setzeProfilId(profil.id);
                  }}
                />
                <label className="profilwahl__beschriftung" htmlFor={feldId}>
                  <span className="profilwahl__name">{profil.name}</span>
                  {/* Beim anpassbaren Profil steht hier nichts: Seine Eckdaten
                      stehen ja nicht fest, und der Satz „… frei wählbar“ sagte
                      dasselbe wie die Beschreibung eine Zeile darunter („…
                      frei wählen.“). Ein Screenreader las beides nacheinander
                      vor. */}
                  {profil.anpassbar === true ? null : (
                    <span className="profilwahl__fakten">
                      {`${fragenText(profil)}, ${zeitText(profil.zeitMinuten)}, bestanden ab ${bestehensText(profil)}`}
                    </span>
                  )}
                </label>
                <p className="profilwahl__erklaerung" id={erklaerungId}>
                  {profil.beschreibung}
                </p>
              </div>
            );
          })}
        </div>
      </fieldset>

      {anpassbar ? (
        <fieldset className="freiwerte">
          <legend className="freiwerte__titel">Eigene Vorgaben</legend>
          <div className="freiwerte__liste">
            <Zahlenfeld
              beschriftung="Fragen im Bogen"
              wert={fragenRoh}
              min={GRENZEN.fragenMin}
              max={GRENZEN.fragenMax}
              einheit="Anzahl der Fragen"
              onWert={setzeFragenRoh}
            />
            <Zahlenfeld
              beschriftung="Bearbeitungszeit"
              wert={zeitRoh}
              min={GRENZEN.zeitMin}
              max={GRENZEN.zeitMax}
              einheit="Minuten"
              onWert={setzeZeitRoh}
            />
            <Zahlenfeld
              beschriftung="Bestehensgrenze"
              wert={prozentRoh}
              min={GRENZEN.prozentMin}
              max={GRENZEN.prozentMax}
              einheit="Prozent richtiger Antworten"
              onWert={setzeProzentRoh}
            />
            <Zahlenfeld
              beschriftung="Anteil offener Fragen"
              wert={offenRoh}
              min={GRENZEN.offenMin}
              max={GRENZEN.offenMax}
              einheit="Prozent des Bogens"
              onWert={setzeOffenRoh}
            />
          </div>
          <p className="freiwerte__hinweis">
            Die Bearbeitungszeit wirkt nur, wenn Sie unten eine Zeitstufe außer „Ohne
            Zeitbegrenzung“ wählen.
          </p>
          <p className="freiwerte__hinweis">
            Offene Fragen beantworten Sie selbst und bewerten sich danach. Auswahlfragen wertet die
            Anwendung.
            {offenAuskunft === null
              ? ''
              : offenAuskunft.gewuenscht === 0
                ? ' Der Bogen besteht damit ausschließlich aus Auswahlfragen.'
                : offenAuskunft.reicht
                  ? ` Das ergibt ${String(offenAuskunft.gewuenscht)} offene Fragen; im Katalog stehen dafür ${String(offenAuskunft.verfuegbar)} zur Auswahl.`
                  : ` Das ergibt ${String(offenAuskunft.gewuenscht)} offene Fragen – im Katalog stehen aber nur ${String(offenAuskunft.verfuegbar)}. Der Rest wird mit Auswahlfragen aufgefüllt.`}
          </p>
        </fieldset>
      ) : null}

      <fieldset className="zeitwahl">
        <legend className="zeitwahl__titel">Zeitvorgabe</legend>
        <p className="zeitwahl__erklaerung">
          Die verlängerten Stufen entsprechen dem Nachteilsausgleich, den reale Prüfungen vorsehen.
          Ohne Zeitbegrenzung zu üben ist gleichwertig und jederzeit möglich.
        </p>
        <div className="zeitwahl__liste">
          {ZEITMODI.map((modus) => {
            const feldId = `${zeitGruppe}-${modus}`;
            return (
              <div className="zeitwahl__option" key={modus}>
                <input
                  className="zeitwahl__feld"
                  id={feldId}
                  type="radio"
                  name={zeitGruppe}
                  value={modus}
                  checked={modus === zeitmodus}
                  onChange={() => {
                    setzeZeitmodus(modus);
                  }}
                />
                <label className="zeitwahl__beschriftung" htmlFor={feldId}>
                  {vorschau === null ? modus : zeitmodusText(vorschau, modus)}
                </label>
              </div>
            );
          })}
        </div>
        {zeitmodus === 'aus' ? (
          <p className="zeitwahl__folge">Es läuft keine Uhr mit. Sie geben ab, wann Sie möchten.</p>
        ) : (
          <p className="zeitwahl__folge">
            Läuft die Zeit ab, wird die Simulation automatisch abgegeben und ausgewertet. Fünf
            Minuten vorher weist die Anwendung Sie darauf hin.
          </p>
        )}
      </fieldset>

      <div className="kapitelwahl-pruefung">
        <div className="kapitelwahl-pruefung__schalter">
          {/* `aria-describedby`: Woher die Vorbelegung kommt, gehört zum
              Kästchen selbst. Ohne die Verbindung stünde der Satz zwar da,
              aber wer sich per Tabulator durch das Formular arbeitet, hörte
              nur „einbeziehen, nicht angehakt“ und nicht, warum. */}
          <input
            className="kapitelwahl-pruefung__feld"
            id={kapitelFeldId}
            type="checkbox"
            checked={kapitelIVEinbeziehen}
            aria-describedby={kapitelHinweisId}
            onChange={(ereignis) => {
              setzeKapitelIV(ereignis.target.checked);
            }}
          />
          <label className="kapitelwahl-pruefung__beschriftung" htmlFor={kapitelFeldId}>
            Kapitel {KAPITEL_IV}
            {kapitelIVTitel === null ? '' : ` – ${kapitelIVTitel}`} in die Simulation einbeziehen
          </label>
        </div>
        <p className="kapitelwahl-pruefung__hinweis" id={kapitelHinweisId}>
          {imProfilAbgewaehlt
            ? 'Sie haben dieses Kapitel unter „Ihr Lernplan“ abgewählt; die Simulation übernimmt das als Vorgabe. Abgewählt kommen daraus keine Fragen im Bogen vor. Für diese eine Simulation dürfen Sie es wieder einbeziehen – am Lernplan ändert das nichts.'
            : 'Nicht jede Prüfungsstelle prüft dieses Kapitel. Abgewählt kommen daraus keine Fragen im Bogen vor – nur für diese eine Simulation, nicht für Ihren Lernplan.'}
        </p>
      </div>

      {/* Der Grund steht vor der Schaltfläche, nicht dahinter: Wer nicht
          weiterkommt, soll ihn gelesen haben, bevor er den gesperrten Knopf
          erreicht. Der Knopf verweist zusätzlich über `aria-describedby`
          darauf. */}
      {auftrag === null ? (
        <div id={grundId}>
          <Hinweis art="fehler">
            Das gewählte Prüfungsprofil ist unbekannt. Bitte wählen Sie ein anderes; ohne Profil
            lässt sich keine Simulation starten.
          </Hinweis>
        </div>
      ) : null}

      <div className="pruefungswahl__aktionen">
        {/* `aria-disabled` statt `disabled` (Projektregel, docs/stand.md 7.13):
            Ein gesperrter Knopf fällt aus der Tabulatorreihenfolge, und der
            Fokus landet auf `<body>`. So bleibt er erreichbar, meldet sich als
            nicht verfügbar und nennt den Grund. */}
        <button
          type="button"
          className="knopf knopf--haupt"
          aria-disabled={auftrag === null}
          {...(auftrag === null ? { 'aria-describedby': grundId } : {})}
          onClick={starten}
        >
          Simulation starten
        </button>
        <button type="button" className="knopf knopf--leise" onClick={onZurueck}>
          Zum Start
        </button>
      </div>
    </section>
  );
}
