/**
 * Der Meldetext zu einer Frage.
 *
 * Zwei Dinge stehen hier auf dem Spiel: dass die Meldung **eindeutig** ist –
 * die amtliche Nummer allein ist es nicht – und dass **nichts** über die
 * meldende Person hineingerät. Der Text verlässt später das Gerät.
 */

import { readFileSync } from 'node:fs';
import { join } from 'node:path';

import { describe, expect, it } from 'vitest';

import { KONTAKT } from '../src/shared/kontakt';
import type { Katalog } from '../src/shared/katalog';
import { meldetextBauen } from '../src/shared/meldetext';

const FRAGE = { id: 'I.1-01', amtliche_nummer: '1.01', seite: 6 };
const KATALOG = {
  herausgeber: 'Bundesverwaltungsamt',
  stand: '2024-12-16',
  quelldatei_sha256: '7e5e41faee0a9198b6ba3e2fe3b46ef313448b06753516579596f98212b1d108',
};
const PROGRAMM = { fassung: '0.12.0', baukennung: '0218791', baustand: '2026-08-22' };

/** Der echte Katalog – für die Aussagen, die nur an ihm zu prüfen sind. */
function echterKatalog(): Katalog {
  const pfad = join(__dirname, '..', '..', 'content', 'katalog', 'katalog.json');
  return JSON.parse(readFileSync(pfad, 'utf-8')) as Katalog;
}

describe('Der Meldetext', () => {
  it('nennt Frage, Katalogstand und Programmfassung', () => {
    const text = meldetextBauen(FRAGE, KATALOG, PROGRAMM);

    expect(text).toContain('1.01');
    expect(text).toContain('I.1-01');
    expect(text).toContain('Seite 6');
    expect(text).toContain('Bundesverwaltungsamt');
    expect(text).toContain('16.12.2024');
    expect(text).toContain('0.12.0');
    expect(text).toContain('0218791');
    expect(text).toContain(KONTAKT.epost);
  });

  it('kürzt die Prüfsumme und sagt, dass sie gekürzt ist', () => {
    /* Die volle Summe hat 64 Zeichen und spränge die Zeile. Gekürzt genügt
       sie, um zwei Katalogfassungen auseinanderzuhalten – aber das muss
       dastehen, sonst hält der Empfänger sie für die ganze. */
    const text = meldetextBauen(FRAGE, KATALOG, PROGRAMM);

    expect(text).toContain('7e5e41faee0a');
    expect(text).toContain('(gekürzt)');
    expect(text).not.toContain(KATALOG.quelldatei_sha256);
  });

  it('kommt ohne Baukennung aus', () => {
    /* Ohne Git zur Bauzeit gibt es keine. Dann steht dort nichts statt eines
       nichtssagenden „unbekannt" – dasselbe Verhalten wie im Systemzustand. */
    const text = meldetextBauen(FRAGE, KATALOG, { ...PROGRAMM, baukennung: '', baustand: '' });

    expect(text).toContain('Fassung 0.12.0');
    expect(text).not.toContain('Bau ');
  });

  it('führt zu jeder der 575 Fragen ihre eindeutige Kennung', () => {
    /*
      Der eigentliche Prüfstein. Nachgezählt am echten Katalog teilen 227 der
      575 Fragen ihre amtliche Nummer mit mindestens einer anderen – „01" gibt
      es als II-01, III-01 und IV-01. Wer nur die Nummer meldet, meldet
      dreideutig.
    */
    const katalog = echterKatalog();

    const mehrdeutig = katalog.fragen.filter(
      (frage) =>
        katalog.fragen.filter((andere) => andere.amtliche_nummer === frage.amtliche_nummer).length >
        1,
    );
    expect(mehrdeutig.length).toBeGreaterThan(200);

    for (const frage of katalog.fragen) {
      const text = meldetextBauen(frage, katalog.meta, PROGRAMM);
      expect(text).toContain(frage.id);
    }
  });

  it('steht Zeichen für Zeichen fest – nichts kann versehentlich mitreisen', () => {
    /*
      Der stärkste Wächter, den es hier geben kann, und die eigentliche Sperre
      gegen den Fehler, der beim nächsten Umbau naheliegt: schnell noch den
      Profilnamen oder die gegebene Antwort mit hineinnehmen, weil es bei der
      Bearbeitung hülfe.

      Ein Wortfilter fängt das nicht – er kennt nur die Wörter, an die jemand
      vorher gedacht hat. Der vollständige Vergleich fängt jede Ergänzung,
      auch die, die niemand vorhergesehen hat. Ändert sich der Wortlaut
      absichtlich, ist diese Zeile mitzuändern; das ist der Sinn.
    */
    expect(meldetextBauen(FRAGE, KATALOG, PROGRAMM)).toBe(
      [
        'Fehlermeldung zur Waffensachkunde-Lernsoftware',
        `Senden an: ${KONTAKT.epost}`,
        '',
        'Frage: 1.01 (Kennung I.1-01), Seite 6 der Vorlage',
        'Fragenkatalog: Bundesverwaltungsamt, Stand 16.12.2024, Prüfsumme 7e5e41faee0a (gekürzt)',
        'Programm: Fassung 0.12.0, Bau 0218791 vom 22.08.2026',
        '',
        'Diese Angaben hat die Lernsoftware zusammengestellt. Sie enthalten nichts über die',
        'meldende Person und nichts über ihren Lernstand.',
        '',
        'Was nicht stimmt (bitte hier beschreiben):',
        '',
      ].join('\n'),
    );
  });

  it('nennt keinen Pfad und keinen Rechnernamen', () => {
    /* Die Angaben kommen aus vier Skalaren, nicht aus `AnwendungsInfo` – dort
       hängt `datenbank.meldung` mit dran, die im Fehlerfall einen Dateipfad
       enthalten kann. Diese Prüfung hält fest, dass die Trennung wirkt. */
    const text = meldetextBauen(FRAGE, KATALOG, PROGRAMM).toLowerCase();

    for (const verboten of ['appdata', 'users', '.db', 'sqlite']) {
      expect(text).not.toContain(verboten);
    }
  });

  it('trennt Zeilen einheitlich und richtet nichts mit Leerzeichen aus', () => {
    /* Der Text soll in einer Nur-Text-Mail genauso aussehen wie in einem
       Forum. Ausgerichtete Spalten halten nur in Festbreitenschrift. */
    const text = meldetextBauen(FRAGE, KATALOG, PROGRAMM);

    expect(text).not.toContain('\r');
    expect(text).not.toMatch(/\S {2,}\S/u);
  });
});
