/**
 * Der Lernstand-Hook, unmittelbar am Zustand geprüft.
 *
 * Hier geht es um etwas, das sich über die Oberfläche nicht fassen lässt: ein
 * Aufblitzen. Die Profil-ID lief beim Neuladen für einen Renderdurchlauf
 * durch `null`, und jeder Verbraucher las das als „kein Profil" – die
 * Prüfungsauswertung meldete kurz „Ohne Lernprofil wird kein Verlauf
 * geführt", der Lernbericht wurde unbedienbar. Ein Test über die Oberfläche
 * käme immer zu spät: Bis er hinsieht, ist die Zahl längst wieder da.
 * `renderHook` sieht genau den Durchlauf dazwischen.
 */

import { act, renderHook, waitFor } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';

import { useLernstand } from '../src/renderer/src/hooks/useLernstand';
import { EINSTELLUNGEN_STANDARD, type Einstellungen } from '../src/shared/ipc';
import type { Lernuebersicht, Profil } from '../src/shared/lernstand';

function profil(id: number, name: string): Profil {
  return {
    id,
    name,
    pruefungstermin: null,
    kapitelAusschluss: [],
    erstelltAm: '2026-01-01T08:00:00.000Z',
  };
}

const OLAF = profil(1, 'Olaf');
const ANNA = profil(2, 'Anna');

const UEBERSICHT: Lernuebersicht = {
  fragenGesamt: 575,
  beantwortet: 0,
  belegt: 0,
  reifegrad: 0 / 575,
  stufe: 'ohne_beleg',
  deckelnd: [],
  faellig: 0,
  gemerkt: 0,
  offen: 0,
  fehler: 0,
  heuteRichtig: 0,
  heuteFalsch: 0,
  heuteBearbeitet: 0,
  tageSeitLetzterAntwort: null,
  bereiche: [],
};

interface Bruecke {
  profilListe: () => Promise<Profil[]>;
  lernUebersicht: (profilId: number) => Promise<Lernuebersicht>;
  einstellungenLesen: () => Promise<Einstellungen>;
  einstellungenSchreiben: (aenderung: Partial<Einstellungen>) => Promise<Einstellungen>;
}

function stelle(gespeichert?: number): Bruecke {
  const bruecke: Bruecke = {
    profilListe: vi.fn(() => Promise.resolve([OLAF, ANNA])),
    lernUebersicht: vi.fn(() => Promise.resolve(UEBERSICHT)),
    einstellungenLesen: vi.fn(() =>
      Promise.resolve(
        gespeichert === undefined
          ? EINSTELLUNGEN_STANDARD
          : { ...EINSTELLUNGEN_STANDARD, profilId: gespeichert },
      ),
    ),
    einstellungenSchreiben: vi.fn(() => Promise.resolve(EINSTELLUNGEN_STANDARD)),
  };
  vi.stubGlobal('lernApp', bruecke);
  return bruecke;
}

afterEach(() => {
  vi.unstubAllGlobals();
});

describe('useLernstand', () => {
  it('nimmt das zuletzt benutzte Profil aus den Einstellungen', async () => {
    const bruecke = stelle(2);
    const { result } = renderHook(() => useLernstand());

    await waitFor(() => {
      expect(result.current.profilId).toBe(2);
    });
    expect(bruecke.lernUebersicht).toHaveBeenCalledWith(2);
  });

  it('führt die Profil-Liste mit, damit die Oberfläche wählen lassen kann', async () => {
    stelle();
    const { result } = renderHook(() => useLernstand());

    await waitFor(() => {
      expect(result.current.profile).toHaveLength(2);
    });
    expect(result.current.profile.map((p) => p.name)).toEqual(['Olaf', 'Anna']);
  });

  it('lässt die Profil-ID beim Neuladen stehen', async () => {
    stelle();
    const { result } = renderHook(() => useLernstand());

    await waitFor(() => {
      expect(result.current.profilId).toBe(1);
    });

    act(() => {
      result.current.neuLaden();
    });

    /* Genau hier lag der Fehler: `neuLaden` setzte den ganzen Zustand auf
       „lädt", und „lädt" hieß `profilId: null`. */
    expect(result.current.profilId).toBe(1);

    await waitFor(() => {
      expect(result.current.uebersicht).not.toBeNull();
    });
    expect(result.current.profilId).toBe(1);
  });

  it('springt beim Wechsel direkt auf die neue Zahl, nie über null', async () => {
    const bruecke = stelle();
    const { result } = renderHook(() => useLernstand());

    await waitFor(() => {
      expect(result.current.profilId).toBe(1);
    });

    act(() => {
      result.current.wechselnZu(2);
    });

    /* Ohne den direkten Sprung schriebe ein Verbraucher in genau diesem
       Augenblick ins Leere – die Antwort landete bei keinem Profil. */
    expect(result.current.profilId).toBe(2);

    await waitFor(() => {
      expect(bruecke.lernUebersicht).toHaveBeenCalledWith(2);
    });
    expect(bruecke.einstellungenSchreiben).toHaveBeenCalledWith({ profilId: 2 });
  });

  it('merkt sich auch die Bestätigung des schon laufenden Profils', async () => {
    const bruecke = stelle();
    const { result } = renderHook(() => useLernstand());

    await waitFor(() => {
      expect(result.current.profilId).toBe(1);
    });
    (bruecke.lernUebersicht as ReturnType<typeof vi.fn>).mockClear();

    act(() => {
      result.current.wechselnZu(1);
    });

    /* Beim ersten Start steht in den Einstellungen noch nichts – die
       Bestätigung „ja, ich bin das" gehört hinein. Neu geladen wird
       deshalb aber nichts: Es hat sich nichts geändert. */
    expect(bruecke.einstellungenSchreiben).toHaveBeenCalledWith({ profilId: 1 });
    expect(bruecke.lernUebersicht).not.toHaveBeenCalled();
    expect(result.current.laedt).toBe(false);
  });

  it('übergeht ein gespeichertes Profil, das es nicht mehr gibt', async () => {
    stelle(99);
    const { result } = renderHook(() => useLernstand());

    await waitFor(() => {
      expect(result.current.profilId).toBe(1);
    });
    expect(result.current.meldung).toBeNull();
  });
});
