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

import {
  ANZEIGEGROESSE_STANDARD,
  anzeigegroesseBereinigen,
  anzeigegroesseText,
  groesser,
  istGroesstmoeglich,
  istKleinstmoeglich,
  kleiner,
} from '../../../shared/ansicht';
import { bridge } from '../bridge';
import { Hinweis } from './lernen/Hinweis';

export interface AnzeigegroessenwahlProps {
  /** Zuletzt gespeicherte Größe; die Anzeige folgt danach dem Anwendungskern. */
  readonly gespeichert: number | undefined;
}

/**
 * Größer, kleiner, normal – für die ganze Anwendung.
 *
 * **Warum sichtbare Schaltflächen, obwohl es das Menü gibt.** Über „Ansicht →
 * Größer" und `Strg` `+` geht es auch. Wer eine größere Schrift braucht,
 * findet aber gerade das Menü schlecht – es ist das Kleinste auf dem
 * Bildschirm. Ein Knopf, den man sieht, ist die zugänglichere Antwort; das
 * Menü bleibt für alle, die Tastenkürzel gewohnt sind.
 *
 * **Warum `aria-disabled` statt `disabled`.** An den Enden der Skala ist der
 * jeweilige Knopf nicht mehr zu gebrauchen. Wäre er `disabled`, verschwände
 * er im letzten Klick unter dem Finger aus der Tabulatorreihenfolge, und der
 * Fokus fiele auf `<body>` – wer sich die Schrift größer stellt, säße danach
 * am Seitenanfang. So bleibt er stehen, meldet sich als nicht verfügbar, und
 * der Klickbehandler kehrt früh zurück.
 *
 * **Warum keine Anzeige ohne Rückmeldung.** Die Größe lässt sich an drei
 * Stellen ändern: hier, im Menü und per Tastenkürzel. Damit die angezeigte
 * Zahl nie überholt ist, hört diese Komponente auf Meldungen des
 * Anwendungskerns statt ihren eigenen Stand fortzuschreiben.
 */
export function Anzeigegroessenwahl({ gespeichert }: AnzeigegroessenwahlProps): React.JSX.Element {
  const [prozent, setzeProzent] = useState(() => anzeigegroesseBereinigen(gespeichert));
  const [meldung, setzeMeldung] = useState('');
  const standId = useId();

  /* Der gespeicherte Wert trifft erst nach dem IPC-Aufruf ein. Angepasst beim
     Rendern statt in einem Effekt – React sieht das für genau diesen Fall
     vor, und ein Effekt bräuchte eine zusätzliche Renderrunde. */
  const [gesehen, setzeGesehen] = useState(gespeichert);
  if (gesehen !== gespeichert) {
    setzeGesehen(gespeichert);
    setzeProzent(anzeigegroesseBereinigen(gespeichert));
  }

  useEffect(() => {
    const beobachten = bridge()?.anzeigegroesseBeobachten;
    return beobachten?.((neu) => {
      setzeProzent(anzeigegroesseBereinigen(neu));
    });
  }, []);

  const setzen = useCallback((ziel: number) => {
    const setzenAmKern = bridge()?.anzeigegroesseSetzen;
    /* Ohne Anwendungskern bleibt die Anzeige stehen – die Größe hängt am
       Fenster, nicht am Dokument, und lässt sich hier nicht selbst ändern. */
    if (!setzenAmKern) {
      setzeMeldung('Die Anzeigegröße lässt sich zurzeit nicht ändern.');
      return;
    }

    void setzenAmKern(ziel)
      .then((gesetzt) => {
        setzeProzent(gesetzt);
        setzeMeldung('');
      })
      .catch((fehler: unknown) => {
        console.warn('[anzeige] Größe konnte nicht gesetzt werden:', fehler);
        setzeMeldung('Die Anzeigegröße konnte nicht geändert werden.');
      });
  }, []);

  return (
    <div className="anzeigegroesse">
      {/*
        Der sichtbare Stand IST die Ansage.

        Der erste Entwurf hatte zusätzlich eine unsichtbare Live-Region – zwei
        Stellen für dieselbe Aussage, und der Test der Themenumschaltung fand
        plötzlich zwei Regionen. Eine `aria-live` auf dem sichtbaren Text sagt
        genau das an, was auch dasteht.
      */}
      <p id={standId} className="anzeigegroesse__stand" role="status" aria-live="polite">
        Aktuell: <strong>{anzeigegroesseText(prozent)}</strong>
      </p>

      <div className="anzeigegroesse__knoepfe">
        <button
          type="button"
          className="knopf"
          aria-describedby={standId}
          aria-disabled={istKleinstmoeglich(prozent)}
          onClick={() => {
            if (!istKleinstmoeglich(prozent)) {
              setzen(kleiner(prozent));
            }
          }}
        >
          <span aria-hidden="true">A−</span> Kleiner
        </button>

        <button
          type="button"
          className="knopf"
          aria-describedby={standId}
          aria-disabled={prozent === ANZEIGEGROESSE_STANDARD}
          onClick={() => {
            if (prozent !== ANZEIGEGROESSE_STANDARD) {
              setzen(ANZEIGEGROESSE_STANDARD);
            }
          }}
        >
          Normalgröße
        </button>

        <button
          type="button"
          className="knopf"
          aria-describedby={standId}
          aria-disabled={istGroesstmoeglich(prozent)}
          onClick={() => {
            if (!istGroesstmoeglich(prozent)) {
              setzen(groesser(prozent));
            }
          }}
        >
          <span aria-hidden="true">A+</span> Größer
        </button>
      </div>

      <p className="anzeigegroesse__hinweis">
        Wirkt auf die ganze Anwendung, auch auf die Prüfzeichen in den Fragen. Über die Tastatur
        geht es mit <kbd>Strg</kbd> <kbd>+</kbd> und <kbd>Strg</kbd> <kbd>−</kbd>, zurück mit{' '}
        <kbd>Strg</kbd> <kbd>0</kbd>.
      </p>

      {/* Nur der Fehlerfall braucht noch eine eigene Meldung – und die
          gehört sichtbar, nicht versteckt. */}
      {meldung === '' ? null : <Hinweis art="fehler">{meldung}</Hinweis>}
    </div>
  );
}
