// @vitest-environment jsdom
/**
 * Der Auffangbereich der Oberfläche.
 *
 * Er ist die letzte Sperre vor einem weißen Fenster – und war bis 0.24.1 der
 * einzige Baustein dieser Größe, den kein Test je aufgerufen hat. Genau dort
 * fiel dann auch etwas heraus: `autoFocus` an einer `h1` tut nichts. React
 * wendet das Attribut nur auf `button`, `input`, `select` und `textarea` an;
 * an jedem anderen Element verschwindet es ersatzlos, und der Fokus blieb
 * nach einem Absturz auf `<body>` stehen.
 *
 * Für jemanden am Bildschirmleser ist das die Sackgasse, die dieser Bereich
 * verhindern soll: Der Fokus steht auf einem Element, das es nicht mehr gibt,
 * und die Tastatur hat keinen Anker.
 */

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { Fehlerauffang } from '../src/renderer/src/components/Fehlerauffang';

/** Eine Komponente, die beim Rendern wirft – wie ein echter Absturz. */
function Kaputt({ was }: { readonly was: unknown }): never {
  throw was;
}

beforeEach(() => {
  /* React meldet den gefangenen Fehler selbst auf der Konsole, und der
     Auffangbereich schreibt ihn absichtlich dorthin. Beides ist hier
     erwartet. `restoreMocks` in `vitest.config.ts` räumt danach auf. */
  vi.spyOn(console, 'error').mockImplementation(() => undefined);
});

describe('Fehlerauffang', () => {
  it('lässt den Inhalt stehen, solange nichts schiefgeht', () => {
    render(
      <Fehlerauffang>
        <p>Alles in Ordnung</p>
      </Fehlerauffang>,
    );

    expect(screen.getByText('Alles in Ordnung')).toBeInTheDocument();
  });

  it('zeigt statt eines leeren Fensters eine Seite mit Überschrift und Ausweg', () => {
    render(
      <Fehlerauffang>
        <Kaputt was={new Error('Etwas ist zerbrochen')} />
      </Fehlerauffang>,
    );

    expect(screen.getByRole('heading', { name: 'Die Anzeige ist abgestürzt' })).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Anwendung neu laden' })).toBeInTheDocument();
  });

  it('meldet sich als Warnung, damit ein Bildschirmleser es ungefragt vorliest', () => {
    render(
      <Fehlerauffang>
        <Kaputt was={new Error('Etwas ist zerbrochen')} />
      </Fehlerauffang>,
    );

    expect(screen.getByRole('alert')).toBeInTheDocument();
  });

  /* Der eigentliche Befund. */
  it('holt den Fokus auf die Überschrift, statt ihn auf <body> fallen zu lassen', () => {
    render(
      <Fehlerauffang>
        <Kaputt was={new Error('Etwas ist zerbrochen')} />
      </Fehlerauffang>,
    );

    const titel = screen.getByRole('heading', { name: 'Die Anzeige ist abgestürzt' });
    expect(document.activeElement).toBe(titel);
    expect(document.activeElement).not.toBe(document.body);
  });

  it('nennt den Grund im Klartext', () => {
    render(
      <Fehlerauffang>
        <Kaputt was={new Error('Etwas ist zerbrochen')} />
      </Fehlerauffang>,
    );

    expect(screen.getByText('Etwas ist zerbrochen')).toBeInTheDocument();
  });

  it('kommt auch mit einem geworfenen Wert zurecht, der kein Fehler ist', () => {
    render(
      <Fehlerauffang>
        <Kaputt was="bloß eine Zeichenkette" />
      </Fehlerauffang>,
    );

    expect(screen.getByText('bloß eine Zeichenkette')).toBeInTheDocument();
  });

  it('nennt „Unbekannter Fehler“, wenn der geworfene Wert nichts hergibt', () => {
    render(
      <Fehlerauffang>
        <Kaputt was="" />
      </Fehlerauffang>,
    );

    expect(screen.getByText('Unbekannter Fehler')).toBeInTheDocument();
  });

  it('sagt ausdrücklich, dass der Lernstand nicht betroffen ist', () => {
    /* Wer eine Fehlerseite sieht, fragt sich zuerst, ob die Arbeit weg ist.
       Die Antwort gehört auf die Seite und nicht in eine Anleitung. */
    render(
      <Fehlerauffang>
        <Kaputt was={new Error('Etwas ist zerbrochen')} />
      </Fehlerauffang>,
    );

    expect(screen.getByText(/Ihr Lernstand ist nicht betroffen/u)).toBeInTheDocument();
  });

  it('bietet den Ausweg als Schaltfläche und nennt den zweiten Weg dazu', async () => {
    /* Der Klick selbst ruft `window.location.reload` – in jsdom ist die
       Eigenschaft weder ersetzbar noch überwachbar, und ein echter Neuladen
       risse die Testumgebung ab. Geprüft wird deshalb, dass der Ausweg da und
       bedienbar ist, und dass die Seite den zweiten Weg nennt, falls der
       erste nicht hilft. */
    const nutzer = userEvent.setup();
    render(
      <Fehlerauffang>
        <Kaputt was={new Error('Etwas ist zerbrochen')} />
      </Fehlerauffang>,
    );

    const knopf = screen.getByRole('button', { name: 'Anwendung neu laden' });
    expect(knopf).toBeEnabled();
    await nutzer.tab();
    expect(document.activeElement).toBe(knopf);

    expect(
      screen.getByText(/schließen Sie das Fenster und starten Sie das Programm neu/u),
    ).toBeInTheDocument();
  });
});
