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

import type { Frage, Katalog, KatalogBild } from '../../../shared/katalog';
import { fehlerMeldung, kanalFehltMeldung, lernBruecke } from '../lernen/bruecke';

/** Nachschlagehilfen über den geladenen Katalog. */
export interface KatalogIndex {
  readonly katalog: Katalog;
  readonly frage: (frageId: string) => Frage | undefined;
  readonly bild: (bildId: string) => KatalogBild | undefined;
  /** Titel zu einer Kapitel- oder Abschnitts-ID, z. B. „I“ oder „I.1“. */
  readonly bereichTitel: (bereichId: string) => string;
}

export type KatalogZustand =
  | { readonly art: 'laedt' }
  | { readonly art: 'fertig'; readonly index: KatalogIndex }
  | { readonly art: 'fehler'; readonly meldung: string };

export interface KatalogSteuerung {
  readonly zustand: KatalogZustand;
  readonly neuLaden: () => void;
}

type Innen =
  | { readonly art: 'laedt' }
  | { readonly art: 'fertig'; readonly katalog: Katalog }
  | { readonly art: 'fehler'; readonly meldung: string };

const OHNE_KANAL = kanalFehltMeldung(
  'katalog:laden',
  'Der Fragenkatalog konnte nicht geladen werden.',
);

function anfangszustand(): Innen {
  /* Ob überhaupt geladen werden kann, steht schon beim ersten Render fest.
     Der Fall gehört deshalb in den Initialwert und nicht in einen Effekt. */
  return lernBruecke()?.katalogLaden ? { art: 'laedt' } : { art: 'fehler', meldung: OHNE_KANAL };
}

/** Lädt den amtlichen Fragenkatalog einmalig und hält ihn samt Index bereit. */
export function useKatalog(): KatalogSteuerung {
  const [innen, setzeInnen] = useState<Innen>(anfangszustand);
  const [runde, setzeRunde] = useState(0);

  useEffect(() => {
    const laden = lernBruecke()?.katalogLaden;
    if (!laden) {
      return undefined;
    }

    let abgebrochen = false;

    void laden()
      .then((katalog) => {
        if (!abgebrochen) {
          setzeInnen({ art: 'fertig', katalog });
        }
      })
      .catch((fehler: unknown) => {
        if (!abgebrochen) {
          setzeInnen({
            art: 'fehler',
            meldung: fehlerMeldung(fehler, 'Der Fragenkatalog konnte nicht geladen werden.'),
          });
        }
      });

    return () => {
      abgebrochen = true;
    };
  }, [runde]);

  const zustand = useMemo<KatalogZustand>(() => {
    if (innen.art !== 'fertig') {
      return innen;
    }

    const { katalog } = innen;
    const fragen = new Map(katalog.fragen.map((frage) => [frage.id, frage]));
    const bilder = new Map(katalog.bilder.map((bild) => [bild.id, bild]));
    const titel = new Map<string, string>();
    for (const kapitel of katalog.kapitel) {
      titel.set(kapitel.id, kapitel.titel);
      for (const abschnitt of kapitel.abschnitte) {
        titel.set(abschnitt.id, abschnitt.titel);
      }
    }

    const index: KatalogIndex = {
      katalog,
      frage: (frageId) => fragen.get(frageId),
      bild: (bildId) => bilder.get(bildId),
      bereichTitel: (bereichId) => titel.get(bereichId) ?? bereichId,
    };
    return { art: 'fertig', index };
  }, [innen]);

  const neuLaden = useCallback(() => {
    setzeInnen(anfangszustand());
    setzeRunde((bisher) => bisher + 1);
  }, []);

  return { zustand, neuLaden };
}
