import { createContext, useCallback, useContext, useEffect, useState, type ReactNode } from 'react';

import {
  abmelden as abmeldenApi,
  aktuellesKonto,
  brandSicherstellen,
  eigeneBrand,
  type Brand,
  type Konto,
} from './auth';
import { ordnerListe, type Ordner } from './folders';

type Session = {
  /** true, solange noch nicht feststeht, ob jemand angemeldet ist. */
  laedt: boolean;
  konto: Konto | null;
  brand: Brand | null;
  /**
   * Der aktive Ordner bestimmt, welches Wissen in den Prompt wandert. Er ist
   * deshalb App-Zustand und kein Bildschirm-lokaler Wert (app-aufbau.md §2.2) –
   * sonst generiert der Nutzer im falschen Scope und versteht das Ergebnis nicht.
   */
  aktiverOrdner: Ordner | null;
  ordner: Ordner[];
  setzeAktivenOrdner: (o: Ordner | null) => void;
  ordnerNeuLaden: () => Promise<void>;
  neuLaden: () => Promise<void>;
  ausloggen: () => Promise<void>;
};

const Ctx = createContext<Session | null>(null);

export function SessionProvider({ children }: { children: ReactNode }) {
  const [laedt, setLaedt] = useState(true);
  const [konto, setKonto] = useState<Konto | null>(null);
  const [brand, setBrand] = useState<Brand | null>(null);
  const [ordner, setOrdner] = useState<Ordner[]>([]);
  const [aktiverOrdner, setzeAktivenOrdner] = useState<Ordner | null>(null);

  const ordnerFuer = useCallback(async (b: Brand | null) => {
    if (!b) {
      setOrdner([]);
      setzeAktivenOrdner(null);
      return;
    }
    const liste = await ordnerListe(b.$id);
    setOrdner(liste);
    setzeAktivenOrdner((bisher) => {
      if (bisher) return liste.find((o) => o.$id === bisher.$id) ?? liste[0] ?? null;
      return liste.find((o) => o.ist_default) ?? liste[0] ?? null;
    });
  }, []);

  const neuLaden = useCallback(async () => {
    const k = await aktuellesKonto();
    setKonto(k);
    if (!k) {
      setBrand(null);
      await ordnerFuer(null);
      setLaedt(false);
      return;
    }
    // Konto ohne Brand kann entstehen, wenn die Registrierung mittendrin
    // abgebrochen ist – hier wird das stillschweigend nachgeholt.
    let b = await eigeneBrand();
    if (!b) b = await brandSicherstellen(k.name || k.email);
    setBrand(b);
    await ordnerFuer(b);
    setLaedt(false);
  }, [ordnerFuer]);

  const ordnerNeuLaden = useCallback(() => ordnerFuer(brand), [brand, ordnerFuer]);

  const ausloggen = useCallback(async () => {
    await abmeldenApi();
    setKonto(null);
    setBrand(null);
    setOrdner([]);
    setzeAktivenOrdner(null);
  }, []);

  useEffect(() => {
    void neuLaden();
  }, [neuLaden]);

  return (
    <Ctx.Provider
      value={{
        laedt, konto, brand, ordner, aktiverOrdner,
        setzeAktivenOrdner, ordnerNeuLaden, neuLaden, ausloggen,
      }}>
      {children}
    </Ctx.Provider>
  );
}

export function useSession(): Session {
  const v = useContext(Ctx);
  if (!v) throw new Error('useSession muss innerhalb von <SessionProvider> stehen.');
  return v;
}
