import { useId, useState } from 'react';

import {
  SEKUNDEN_PRO_FRAGE_VORGABE,
  type Lernplan,
  type Machbarkeit,
} from '../../../../shared/lernplan';
import type { LernplanZustand } from '../../hooks/useLernplan';
import { belegteFragen } from '../../../../shared/reife';
import { Schalter } from '../Schalter';
import { Hinweis } from './Hinweis';

export interface LernplanungProps {
  readonly zustand: LernplanZustand;
  /** Kennung und Titel des abwählbaren Kapitels, oder `null` ohne Katalog. */
  readonly abwaehlbaresKapitel: { readonly id: string; readonly titel: string } | null;
  /** Lernt dieses Profil das Kapitel mit? */
  readonly mitKapitel: boolean;
  readonly onMitKapitel: (mit: boolean) => void;
}

function tageText(tage: number): string {
  if (tage === 0) {
    return 'heute';
  }
  if (tage === 1) {
    return 'morgen';
  }
  if (tage < 0) {
    const her = Math.abs(tage);
    return her === 1 ? 'gestern' : `vor ${String(her)} Tagen`;
  }
  return `in ${String(tage)} Tagen`;
}

/** Deutsches Datum aus einem ISO-Datum, ohne Zeitzonenverschiebung. */
function datumText(iso: string): string {
  const [jahr, monat, tag] = iso.split('-');
  if (jahr === undefined || monat === undefined || tag === undefined) {
    return iso;
  }
  return `${tag}.${monat}.${jahr}`;
}

function minutenText(minuten: number): string {
  if (minuten < 1) {
    return 'wenige Minuten';
  }
  if (minuten < 60) {
    return `etwa ${String(minuten)} Minuten`;
  }
  const stunden = Math.floor(minuten / 60);
  const rest = minuten % 60;
  const stundenteil = stunden === 1 ? 'eine Stunde' : `${String(stunden)} Stunden`;
  return rest === 0 ? `etwa ${stundenteil}` : `etwa ${stundenteil} und ${String(rest)} Minuten`;
}

/**
 * Einschätzung als ganzer Satz.
 *
 * Bewusst kein Ampelwort allein: „knapp“ sagt für sich genommen nichts
 * darüber, was zu tun ist.
 */
function machbarkeitSatz(machbarkeit: Machbarkeit, plan: Lernplan): string {
  const pensum = minutenText(plan.pensum.minuten);

  switch (machbarkeit) {
    case 'kein_termin':
      return 'Ohne Prüfungstermin plant das Programm ohne Stichtag: Fragen kommen wieder, bevor Sie sie vergessen. Mit Termin kann es Ihnen sagen, wie viel Sie täglich schaffen müssen.';
    case 'termin_vorbei':
      return 'Der eingetragene Termin liegt in der Vergangenheit. Tragen Sie einen neuen ein, damit die Planung wieder greift.';
    case 'entspannt':
      return `Sie liegen gut in der Zeit. ${pensum} täglich genügen.`;
    case 'machbar':
      return `Das ist gut zu schaffen: ${pensum} täglich.`;
    case 'knapp':
      return `Es wird knapp. Sie müssten ${pensum} täglich einplanen.`;
    case 'zu_wenig_zeit':
      return `Bis zu diesem Termin ist der ganze Katalog kaum zu schaffen – es wären ${pensum} täglich. Sinnvoller ist, sich auf die Prüfungssimulation und die Fehlerfragen zu beschränken oder den Termin zu verschieben.`;
  }
}

/** Die Zahlen des Tagespensums als Satz. */
function pensumSatz(plan: Lernplan): string {
  const { neu, wiederholung } = plan.pensum;
  const teile: string[] = [];
  if (neu > 0) {
    teile.push(`${String(neu)} neue ${neu === 1 ? 'Frage' : 'Fragen'}`);
  }
  if (wiederholung > 0) {
    teile.push(`${String(wiederholung)} zur Wiederholung`);
  }
  if (teile.length === 0) {
    return 'Für heute steht nichts an. Alles Fällige ist erledigt.';
  }
  return `Heute: ${teile.join(' und ')}.`;
}

/** „heute“, „morgen“, „Mi, 3.9.“ – die Beschriftung eines Vorschautags. */
function tagbeschriftung(inTagen: number, heute: Date): string {
  if (inTagen === 0) {
    return 'heute';
  }
  if (inTagen === 1) {
    return 'morgen';
  }
  const tag = new Date(heute.getFullYear(), heute.getMonth(), heute.getDate() + inTagen);
  return tag.toLocaleDateString('de-DE', { weekday: 'short', day: 'numeric', month: 'numeric' });
}

/**
 * Was in den nächsten Tagen ansteht.
 *
 * **Wozu.** Der Plan kannte bis 0.22.0 nur „heute“. Weil die Zielquote vor
 * dem Termin steigt und die Intervalle dadurch zusammenrücken, türmt sich ein
 * Wiederholungsberg, den der Lernende erst am jeweiligen Morgen erfuhr. Wer
 * Schichtdienst, Familie oder einen vollen Kalender hat, kann Lerntage nur
 * planen, wenn er die kommende Last kennt.
 *
 * **Warum die Zahl als Text und der Balken nur daneben.** Der Balken ist
 * Zugabe; jede Zahl steht ausgeschrieben da, und die Liste ist eine
 * Definitionsliste, keine Grafik. Farbe trägt nichts (WCAG 1.4.1).
 *
 * **Warum ausdrücklich „mindestens“.** Was hier steht, ist der heutige
 * Kalenderstand — jede Antwort von heute setzt neue Termine. Mit wachsendem
 * Abstand wird die Zahl systematisch zu klein, und eine leere Woche in der
 * Vorschau hieße nicht, dass eine leere Woche kommt. Das sagt der Satz
 * darunter, statt es dem Leser zu überlassen.
 */
function Arbeitslastvorschau({ plan }: { readonly plan: Lernplan }): React.JSX.Element | null {
  const vorschau = plan.vorschau ?? [];
  /* Ohne einen einzigen Termin gibt es nichts zu zeigen – und „lauter
     Nullen“ wäre die missverständlichste Auskunft von allen. */
  if (vorschau.length === 0 || vorschau.every((anzahl) => anzahl === 0)) {
    return null;
  }

  const heute = new Date();
  const hoechste = Math.max(...vorschau);

  return (
    <div className="vorschau">
      <h4 className="vorschau__titel">Was in den nächsten Tagen ansteht</h4>
      <dl className="vorschau__tage">
        {vorschau.map((anzahl, inTagen) => (
          <div key={inTagen} className="vorschau__tag">
            <dt>{tagbeschriftung(inTagen, heute)}</dt>
            <dd>
              {anzahl === 0 ? 'nichts angesetzt' : `${String(anzahl)} zur Wiederholung`}
              <span
                className="vorschau__balken"
                aria-hidden="true"
                style={{ inlineSize: `${String(hoechste === 0 ? 0 : (anzahl / hoechste) * 100)}%` }}
              />
            </dd>
          </div>
        ))}
      </dl>
      <p className="vorschau__fussnote">
        Das sind die Wiederholungen, die <strong>jetzt schon</strong> im Kalender stehen –
        mindestens so viele werden es. Jede Antwort von heute setzt weitere Termine, und je weiter
        der Tag entfernt ist, desto unvollständiger ist die Zahl. Neue Fragen sind nicht mitgezählt:
        Von ihnen kommen{' '}
        {plan.pensum.neu === 1 ? 'täglich eine' : `täglich bis zu ${String(plan.pensum.neu)}`} dazu.
      </p>
    </div>
  );
}

/**
 * Lernplan zum Prüfungstermin.
 *
 * Beantwortet drei Fragen, die sonst niemand beantwortet: Wo stehe ich
 * wirklich? Was kostet mich Nichtstun bis zur Prüfung? Und was muss ich heute
 * tun?
 *
 * Jede Zahl steht als Satz da. Der Fortschrittsbalken ist Zugabe; Farbe trägt
 * nirgends Information, die nicht auch im Text steht (WCAG 1.4.1).
 */
export function Lernplanung({
  zustand,
  abwaehlbaresKapitel,
  mitKapitel,
  onMitKapitel,
}: LernplanungProps): React.JSX.Element {
  const { plan, laedt, meldung, terminSetzen } = zustand;
  const feldId = useId();
  const [entwurf, setzeEntwurf] = useState<string | null>(null);
  const [ansage, setzeAnsage] = useState('');

  /* Solange nichts getippt wurde, zeigt das Feld den gespeicherten Termin.
     Ab der ersten Eingabe gilt der Entwurf – sonst würde das erneute Laden
     des Plans die halb fertige Eingabe überschreiben. */
  const feldwert = entwurf ?? plan?.termin ?? '';

  async function uebernehmen(wert: string): Promise<void> {
    setzeEntwurf(wert);
    /* Ein leeres Feld löscht den Termin. Ein unvollständiges Datum
       (der Nutzer tippt noch) wird stillschweigend ignoriert. */
    if (wert === '' || /^\d{4}-\d{2}-\d{2}$/u.test(wert)) {
      await terminSetzen(wert === '' ? null : wert);
      setzeEntwurf(null);
    }
  }

  return (
    <div className="lernplan">
      {abwaehlbaresKapitel === null ? null : (
        <div className="lernplan__umfang">
          <Schalter
            beschriftung={`Kapitel ${abwaehlbaresKapitel.id} – ${abwaehlbaresKapitel.titel} mitlernen`}
            aktiv={mitKapitel}
            onAktiv={(mit) => {
              onMitKapitel(mit);
              /*
                Die Ansage ist nicht Zierde, sondern nötig: Der Schalter steht
                unter „Ihr Lernplan“, die Zahlen, die er ändert, stehen im
                Dokument darüber – Fortschrittsbalken und Lernstand. Wer mit
                Bildschirmleser arbeitet, hat sie beim Umlegen längst passiert
                und merkte sonst überhaupt nichts.

                Und sie nennt den Grund für einen Sprung, der sonst wie ein
                Fehler aussieht: Die Prognose ist ein Mittelwert über alle
                Fragen des Umfangs. Fallen 89 nie beantwortete heraus, steigt
                sie über Nacht – ohne dass jemand etwas gelernt hätte.
              */
              setzeAnsage(
                mit
                  ? `Kapitel ${abwaehlbaresKapitel.id} wird wieder mitgelernt. Lernstand und Prognose rechnen es ab sofort mit.`
                  : `Kapitel ${abwaehlbaresKapitel.id} ist für dieses Profil abgewählt. Lernstand und Prognose rechnen ohne diese Fragen; nichts davon geht verloren.`,
              );
            }}
            erklaerung={
              <>
                Nicht jede Prüfungsstelle prüft dieses Kapitel. Ausgeschaltet kommen daraus keine
                Fragen mehr in Ihren Lernsitzungen vor, und Fortschritt, Tagespensum und Prognose
                rechnen ohne sie. Die Prüfungssimulation übernimmt diese Wahl als Vorgabe; dort
                lässt sie sich für einen einzelnen Durchgang wieder umstellen, ohne dass sich am
                Lernplan etwas ändert. Was Sie hier bereits gelernt haben, bleibt gespeichert und
                kehrt beim Wiedereinschalten vollständig zurück. Nachschlagen können Sie die Fragen
                jederzeit über „Fragen durchsuchen“.
              </>
            }
          />
          {/* Von Anfang an im Baum – eine erst später eingehängte Region
              verschluckt ihre erste Meldung. */}
          <p className="nur-screenreader" role="status">
            {ansage}
          </p>
        </div>
      )}

      <div className="lernplan__termin">
        <label htmlFor={feldId}>Ihr Prüfungstermin</label>
        <input
          id={feldId}
          type="date"
          className="lernplan__terminfeld"
          value={feldwert}
          aria-describedby={`${feldId}-hilfe`}
          onChange={(ereignis) => {
            void uebernehmen(ereignis.target.value);
          }}
        />
        <p id={`${feldId}-hilfe`} className="lernplan__hilfe">
          Format Tag.Monat.Jahr. Leer lassen, wenn noch kein Termin feststeht – dann plant das
          Programm ohne Stichtag.
        </p>
      </div>

      {meldung !== null ? <Hinweis art="hinweis">{meldung}</Hinweis> : null}

      {laedt && plan === null ? <p>Ihr Lernplan wird berechnet …</p> : null}

      {plan !== null ? (
        <>
          {/* Die Einschätzung ist die Antwort auf „reicht meine Zeit?“ und
              steht deshalb vor allen Zahlen. */}
          <p className="lernplan__urteil" role="status">
            {machbarkeitSatz(plan.machbarkeit, plan)}
          </p>

          {/*
            Ohne eigenen Balken für „heute“. `prognoseHeute` ist seit dem
            Umbau derselbe Wert wie der Reifegrad der Ampel weiter oben –
            zweimal dieselbe Größe auf einem Bildschirm, einmal als Fragenzahl
            und einmal als Prozentwert. Genau diese Doppelung war der Befund
            in docs/stand.md 7.1; sie hier stehen zu lassen hieße, ihn an
            anderer Stelle zu wiederholen.
          */}
          <dl className="lernstand__zahlen">
            <dt>Heute zu tun</dt>
            <dd>
              {pensumSatz(plan)}
              {plan.pensum.gesamt > 0 ? ` Das sind ${minutenText(plan.pensum.minuten)}.` : ''}
            </dd>

            {plan.termin !== null && plan.tageBisTermin !== null ? (
              <>
                <dt>Termin</dt>
                <dd>
                  {datumText(plan.termin)} – {tageText(plan.tageBisTermin)}
                </dd>
              </>
            ) : null}

            {plan.prognoseAmTermin !== null ? (
              <>
                <dt>Wenn Sie ab heute nichts mehr täten</dt>
                <dd>
                  {/* In derselben Einheit wie die Ampel oben: Fragen, nicht
                      Prozent. Wer zwei Einheiten für dieselbe Größe liest,
                      hält sie für zwei Größen. */}
                  Am Prüfungstag säßen noch etwa{' '}
                  {belegteFragen(plan.prognoseAmTermin, plan.gesamtFragen)} von {plan.gesamtFragen}{' '}
                  Fragen belegt.
                </dd>
              </>
            ) : null}

            <dt>Noch nie beantwortet</dt>
            <dd>
              {plan.nieBeantwortet} von {plan.gesamtFragen} Fragen
            </dd>
          </dl>

          <Arbeitslastvorschau plan={plan} />

          <p className="lernplan__fussnote">
            {/* Nur noch das Tempo. Was die Zahl bedeutet und wie vorsichtig
                sie gerechnet ist, steht einmal – bei der Ampel unter „Was
                heißt ‚belegt‘?“. */}
            Gerechnet wird mit {String(Math.round(plan.sekundenProFrage))} Sekunden je Frage
            {plan.sekundenProFrage === SEKUNDEN_PRO_FRAGE_VORGABE
              ? ' (Erfahrungswert)'
              : ' (Ihr bisheriges Tempo)'}
            .
          </p>
        </>
      ) : null}
    </div>
  );
}
