/**
 * Die Lader der mitgelieferten Inhaltsdateien.
 *
 * **Warum das geprüft wird.** Normtexte und Themengliederung liegen als JSON
 * im Ressourcenverzeichnis der Anwendung. Was dort steht, hat die Pipeline
 * geschrieben – aber der Lader ist die letzte Stelle vor der Oberfläche, und
 * er muss zwei Dinge leisten: Unbrauchbares **abweisen**, statt es
 * weiterzureichen, und dabei die Anwendung **nicht mitreißen**. Fehlt die
 * Datei oder ist sie kaputt, läuft das Programm ohne – genau wie vor 0.22.0.
 *
 * Geprüft wird gegen echte Dateien in einem Wegwerfverzeichnis, nicht gegen
 * eine Attrappe des Dateisystems: Der Weg vom Pfad bis zum geprüften Objekt
 * ist das, worauf es ankommt.
 */

import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const verzeichnis = mkdtempSync(join(tmpdir(), 'wsk-lader-'));

vi.mock('electron', () => ({
  app: {
    isPackaged: true,
    getAppPath: (): string => verzeichnis,
  },
}));

/* `process.resourcesPath` gibt es nur im gepackten Betrieb; unter Vitest ist
   es nicht gesetzt. Mit `isPackaged: true` oben suchen die Lader genau dort –
   das ist der Weg, der beim Anwender läuft. */
const vorher = process.resourcesPath;
Object.defineProperty(process, 'resourcesPath', { value: verzeichnis, configurable: true });

const { normtexteLaden, normtexteZuruecksetzen, normtextePfad } =
  await import('../src/main/normtexte');
const { themenLaden, themenZuruecksetzen, themenPfad } = await import('../src/main/themen');

/** Schreibt eine Datei ins Wegwerfverzeichnis – oder löscht sie. */
function ablegen(name: string, inhalt: string | null): void {
  const pfad = join(verzeichnis, name);
  if (inhalt === null) {
    rmSync(pfad, { force: true });
  } else {
    writeFileSync(pfad, inhalt, 'utf8');
  }
}

const NORMTEXTE_GUELTIG = {
  meta: { version: 1, stand: '2026-08-29', hinweis: 'Auszug', gesetzesstand: { WaffG: 'Stand' } },
  gesetze: {
    WaffG: {
      bezeichnung: 'Waffengesetz',
      stand: 'Stand',
      quelle: 'https://www.gesetze-im-internet.de/waffg_2002/',
      normen: {
        '§ 1': { titel: 'Zweck', istAnlage: false, absaetze: { '1': 'Text' } },
        'Anlage 1': {
          titel: '(zu § 1 Abs. 4)',
          istAnlage: true,
          bloecke: [{ marke: '1.1', pfad: ['Abschnitt 1'], text: '1.1\nText' }],
        },
      },
    },
  },
};

const THEMEN_GUELTIG = {
  meta: { version: 1, stand: '2026-08-29', hinweis: 'Gliederung dieser Software' },
  gruppen: [{ id: 'II.1', kapitel: 'II', titel: 'Waffenarten', fragen: ['II-01', 'II-02'] }],
};

beforeEach(() => {
  normtexteZuruecksetzen();
  themenZuruecksetzen();
  vi.spyOn(console, 'error').mockImplementation(() => undefined);
});

afterEach(() => {
  vi.restoreAllMocks();
  ablegen('normtexte.json', null);
  ablegen('themen.json', null);
});

afterAll(() => {
  Object.defineProperty(process, 'resourcesPath', { value: vorher, configurable: true });
  rmSync(verzeichnis, { recursive: true, force: true });
});

describe('normtexteLaden', () => {
  it('sucht im gepackten Betrieb neben der Anwendung', () => {
    expect(normtextePfad()).toBe(join(verzeichnis, 'normtexte.json'));
  });

  it('liest eine gültige Datei', () => {
    ablegen('normtexte.json', JSON.stringify(NORMTEXTE_GUELTIG));

    const geladen = normtexteLaden();

    expect(geladen.gesetze['WaffG']?.normen['§ 1']?.absaetze?.['1']).toBe('Text');
    expect(geladen.gesetze['WaffG']?.normen['Anlage 1']?.bloecke?.[0]?.marke).toBe('1.1');
  });

  it('läuft ohne die Datei weiter', () => {
    /* Der Lernbetrieb hängt nicht daran: Ohne Normtexte steht die Fundstelle
       wie vor 0.22.0 als reines Zitat da. */
    expect(normtexteLaden().gesetze).toEqual({});
  });

  it('reißt bei kaputtem JSON nichts mit', () => {
    ablegen('normtexte.json', '{ das ist kein JSON');

    expect(normtexteLaden().gesetze).toEqual({});
    expect(console.error).toHaveBeenCalled();
  });

  it('weist eine Anlage ohne Blöcke ab, statt sie halb zu übernehmen', () => {
    ablegen(
      'normtexte.json',
      JSON.stringify({
        ...NORMTEXTE_GUELTIG,
        gesetze: {
          WaffG: {
            ...NORMTEXTE_GUELTIG.gesetze.WaffG,
            normen: { 'Anlage 1': { titel: 'T', istAnlage: true } },
          },
        },
      }),
    );

    expect(normtexteLaden().gesetze).toEqual({});
  });

  it('weist eine Fassung ohne Versionsangabe ab', () => {
    ablegen(
      'normtexte.json',
      JSON.stringify({ ...NORMTEXTE_GUELTIG, meta: { ...NORMTEXTE_GUELTIG.meta, version: 0 } }),
    );

    expect(normtexteLaden().gesetze).toEqual({});
  });

  it('liest die Datei nur einmal', () => {
    ablegen('normtexte.json', JSON.stringify(NORMTEXTE_GUELTIG));
    const erste = normtexteLaden();
    ablegen('normtexte.json', null);

    // Ohne Zwischenspeicher käme jetzt der leere Bestand zurück.
    expect(normtexteLaden()).toBe(erste);
  });
});

describe('themenLaden', () => {
  it('sucht im gepackten Betrieb neben der Anwendung', () => {
    expect(themenPfad()).toBe(join(verzeichnis, 'themen.json'));
  });

  it('liest eine gültige Datei', () => {
    ablegen('themen.json', JSON.stringify(THEMEN_GUELTIG));

    expect(themenLaden().gruppen[0]).toMatchObject({ id: 'II.1', titel: 'Waffenarten' });
  });

  it('läuft ohne die Datei weiter', () => {
    // Dann steht in den Kapiteln II bis IV nur der Knopf für das ganze Kapitel.
    expect(themenLaden().gruppen).toEqual([]);
  });

  it('weist eine leere Gruppe ab', () => {
    /* Eine leere Gruppe erschiene als Knopf, der eine Sitzung ohne Fragen
       startet – schlimmer als eine fehlende Gruppe. */
    ablegen(
      'themen.json',
      JSON.stringify({
        ...THEMEN_GUELTIG,
        gruppen: [{ id: 'II.1', kapitel: 'II', titel: 'T', fragen: [] }],
      }),
    );

    expect(themenLaden().gruppen).toEqual([]);
  });

  it('weist eine Frage in zwei Gruppen ab', () => {
    /* Sonst zöge ein Thema Fragen eines anderen mit, und die Zählung neben
       dem Knopf stimmte für beide nicht. */
    ablegen(
      'themen.json',
      JSON.stringify({
        ...THEMEN_GUELTIG,
        gruppen: [
          { id: 'II.1', kapitel: 'II', titel: 'A', fragen: ['II-01'] },
          { id: 'II.2', kapitel: 'II', titel: 'B', fragen: ['II-01'] },
        ],
      }),
    );

    expect(themenLaden().gruppen).toEqual([]);
  });

  it('weist doppelte Gruppenkennungen ab', () => {
    ablegen(
      'themen.json',
      JSON.stringify({
        ...THEMEN_GUELTIG,
        gruppen: [
          { id: 'II.1', kapitel: 'II', titel: 'A', fragen: ['II-01'] },
          { id: 'II.1', kapitel: 'II', titel: 'B', fragen: ['II-02'] },
        ],
      }),
    );

    expect(themenLaden().gruppen).toEqual([]);
  });

  it('reißt bei kaputtem JSON nichts mit', () => {
    ablegen('themen.json', 'null');

    expect(themenLaden().gruppen).toEqual([]);
    expect(console.error).toHaveBeenCalled();
  });
});
