import { execFile } from 'child_process';
import { promisify } from 'util';
import * as path from 'path';
import { abgleichMisserfolg, alsIsoDatum, dateiVorhanden, mitTemporaeremOrdner, type Abgleichauftrag, type Bezugsquelle, type QuellenAbgleich } from './basis';

const execFileAsync = promisify(execFile);

/**
 * SVN: exportiert eine Datei aus einem Repository, erkennt Änderungen über die Revision.
 * Anmeldedaten kommen aus dem Windows-Anmeldespeicher des Dienstbenutzers —
 * die Erstanmeldung muss einmal interaktiv als dieser Benutzer erfolgt sein.
 */
export class SvnQuelle implements Bezugsquelle {
  async gleicheAb(auftrag: Abgleichauftrag): Promise<QuellenAbgleich> {
    const dateiname = decodeURIComponent(auftrag.wert.split('/').pop() ?? 'datei');
    let revision = '';
    let veroeffentlichtAm = '';
    try {
      const svnInfo = (element: string) =>
        execFileAsync('svn', ['info', '--show-item', element, '--non-interactive', auftrag.wert], { timeout: 60000, windowsHide: true });
      revision = (await svnInfo('last-changed-revision')).stdout.trim();
      veroeffentlichtAm = alsIsoDatum((await svnInfo('last-changed-date')).stdout.trim());
    } catch (fehler) {
      return abgleichMisserfolg(auftrag.aktuelleVersion, svnFehlertext(fehler));
    }

    if (revision && revision === auftrag.aktuelleVersion && dateiVorhanden(auftrag.zielordner, dateiname)) {
      return { erfolg: true, geaendert: false, version: revision, dateiname, meldung: 'Bereits aktuell.', veroeffentlichtAm };
    }

    const ablage = mitTemporaeremOrdner(auftrag.zielordner);
    try {
      await execFileAsync('svn', ['export', '--force', '--non-interactive', auftrag.wert, path.join(ablage.ordner, dateiname)], {
        timeout: 300000,
        windowsHide: true,
      });
      ablage.uebernehmen();
      return { erfolg: true, geaendert: true, version: revision || new Date().toISOString(), dateiname, meldung: `SVN-Revision ${revision} exportiert.`, veroeffentlichtAm };
    } catch (fehler) {
      ablage.verwerfen();
      return abgleichMisserfolg(auftrag.aktuelleVersion, svnFehlertext(fehler));
    }
  }
}

/** Macht fehlende Anmeldedaten als häufigste Ursache kenntlich. */
function svnFehlertext(fehler: unknown): string {
  const text = fehler instanceof Error ? fehler.message : String(fehler);
  if (/authoriz|authentic|E170001|credentials/i.test(text)) {
    return `SVN-Anmeldung fehlt oder ist ungültig. Einmalig als Dienstbenutzer anmelden (die Anmeldung wird dann im Windows-Speicher hinterlegt). Ursprüngliche Meldung: ${text}`;
  }
  return `SVN fehlgeschlagen: ${text}`;
}
