import type { Einstellungen, Software } from 'bibliothek-types';
import { alleSoftware, softwareAnlegen, softwareLoeschen, umzugsAngabenSetzen } from './datenbank';
import { aktuelleEinstellungen, speichereEinstellungen } from './konfiguration';
import { ladeGeheimnisse, loescheGeheimnisse, speichereGeheimnisse, type Geheimniswert } from './geheimnisse';

/**
 * Umzug auf einen anderen Rechner.
 *
 * Bewusst als lesbares JSON und nicht als Ordner-Kopie:
 *  - Die geholten Dateien (mehrere hundert MB) und die Git-Arbeitskopien (GB)
 *    sind jederzeit reproduzierbar — ein Abgleich holt sie neu. Nur Katalog,
 *    Einstellungen und Geheimnisse sind unersetzlich, und das sind wenige KB.
 *  - Die Datenbank im laufenden Betrieb zu kopieren wäre unsicher: die jüngsten
 *    Änderungen stehen im WAL und fehlten in der Kopie.
 *  - JSON übersteht Schema-Änderungen: beim Einlesen werden Felder zugeordnet,
 *    statt eine Binärdatei einer bestimmten Version vorauszusetzen.
 */

/** Aktuelle Fassung des Austauschformats — erlaubt später sauberes Nachziehen. */
const FORMAT_FASSUNG = 1;

export interface UmzugsPaket {
  fassung: number;
  erstelltAm: string;
  einstellungen: Einstellungen;
  software: Software[];
  /** Nur enthalten, wenn ausdrücklich mit exportiert (enthält Klartext-Schlüssel!). */
  geheimnisse?: Record<string, Geheimniswert[]>;
}

export function erstelleUmzugsPaket(mitGeheimnissen: boolean): UmzugsPaket {
  const eintraege = alleSoftware();
  const paket: UmzugsPaket = {
    fassung: FORMAT_FASSUNG,
    erstelltAm: new Date().toISOString(),
    einstellungen: aktuelleEinstellungen(),
    software: eintraege,
  };
  if (mitGeheimnissen) {
    paket.geheimnisse = {};
    for (const eintrag of eintraege) {
      const werte = ladeGeheimnisse(eintrag.id);
      if (werte.length > 0) {
        paket.geheimnisse[String(eintrag.id)] = werte;
      }
    }
  }
  return paket;
}

export interface UmzugsErgebnis {
  uebernommen: number;
  mitGeheimnissen: number;
  hinweis: string;
}

/**
 * Liest ein Paket ein und ersetzt den bisherigen Katalog vollständig.
 * Die eigentlichen Programm-Dateien kommen nicht mit — sie werden beim
 * nächsten Abgleich neu geholt bzw. gebaut.
 */
export function uebernehmeUmzugsPaket(paket: UmzugsPaket): UmzugsErgebnis {
  if (!paket || typeof paket !== 'object' || !Array.isArray(paket.software)) {
    throw new Error('Die Datei ist kein gültiges Umzugs-Paket.');
  }
  if (paket.fassung > FORMAT_FASSUNG) {
    throw new Error(`Das Paket stammt aus einer neueren Fassung (${paket.fassung}) — bitte die Anwendung aktualisieren.`);
  }

  // Bisherigen Bestand räumen, damit keine Mischung aus altem und neuem Stand entsteht.
  for (const vorhandene of alleSoftware()) {
    loescheGeheimnisse(vorhandene.id);
    softwareLoeschen(vorhandene.id);
  }

  // Die Ids ändern sich beim Neuanlegen — für die Eltern-Zuordnung mitführen.
  const idZuordnung = new Map<number, number>();
  for (const eintrag of paket.software.filter((eintrag) => !eintrag.elternId)) {
    idZuordnung.set(eintrag.id, uebernehmeEintrag(eintrag, 0).id);
  }
  for (const eintrag of paket.software.filter((eintrag) => eintrag.elternId)) {
    idZuordnung.set(eintrag.id, uebernehmeEintrag(eintrag, idZuordnung.get(eintrag.elternId) ?? 0).id);
  }

  let mitGeheimnissen = 0;
  for (const [alteId, werte] of Object.entries(paket.geheimnisse ?? {})) {
    const neueId = idZuordnung.get(Number(alteId));
    if (neueId) {
      speichereGeheimnisse(neueId, werte);
      mitGeheimnissen++;
    }
  }

  if (paket.einstellungen) {
    // Datenordner und Port gehören zum Zielrechner und werden bewusst nicht überschrieben.
    speichereEinstellungen({
      gitBenutzer: paket.einstellungen.gitBenutzer ?? '',
      gitSchluessel: paket.einstellungen.gitSchluessel ?? '',
    });
  }

  return {
    uebernommen: idZuordnung.size,
    mitGeheimnissen,
    hinweis: 'Die Programm-Dateien wurden nicht mit übertragen — sie werden beim nächsten Abgleich neu geholt bzw. gebaut.',
  };
}

/** Legt einen Eintrag neu an und stellt den bekannten Abgleich-Stand wieder her. */
function uebernehmeEintrag(eintrag: Software, elternId: number): Software {
  const angelegt = softwareAnlegen({
    name: eintrag.name ?? '',
    beschreibung: eintrag.beschreibung ?? '',
    elternId,
    quelleTyp: eintrag.quelleTyp ?? '',
    quelleWert: eintrag.quelleWert ?? '',
    bauBefehl: eintrag.bauBefehl ?? '',
    bauErgebnis: eintrag.bauErgebnis ?? '',
    einstufung: eintrag.einstufung ?? 'lizenzfrei',
    programmart: eintrag.programmart ?? 'unbestimmt',
    hinweis: eintrag.hinweis ?? '',
    anleitungUrl: eintrag.anleitungUrl ?? '',
    erwarteterHerausgeber: eintrag.erwarteterHerausgeber ?? '',
  });
  // Version und Datei bleiben leer (Standard beim Anlegen): Die Datei fehlt auf dem
  // neuen Rechner noch, der nächste Abgleich soll sie wirklich holen. Bewusst KEIN
  // fehlgeschlagener Abgleich — „noch nicht geholt" ist kein Problemfall und soll
  // nicht in der Warnleiste auftauchen.
  umzugsAngabenSetzen(angelegt.id, eintrag.veroeffentlichtAm ?? '', eintrag.benoetigteGeheimnisse ?? []);
  return angelegt;
}
