import {
  copyFileSync,
  existsSync,
  mkdtempSync,
  mkdirSync,
  readFileSync,
  rmSync,
  writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { applyReleaseManifest, buildReleaseManifest, MANIFEST_FILE, readAndValidateManifest } from './release-manifest-lib.mjs';
import { createPackageManifest, PACKAGE_MANIFEST_SCHEMA } from './updater-lifecycle-3.4.mjs';

const temp = mkdtempSync(join(tmpdir(), 'vendoo-release-manifest-'));
const source = join(temp, 'source');
const destination = join(temp, 'repo');
const requiredBodies = new Map([
  ['package.json', '{"name":"vendoo","version":"1.43.0"}\n'],
  ['package-lock.json', '{"name":"vendoo","lockfileVersion":3}\n'],
  ['bootstrap.mjs', 'export const bootstrap = true;\n'],
  ['server.mjs', 'export const server = true;\n'],
  ['lib/db.mjs', 'export const db = true;\n'],
  ['db/schema.sql', 'CREATE TABLE test(id INTEGER PRIMARY KEY);\n'],
  ['update-manifest.json', '{"version":"1.43.0","updaterVersion":"3.4.0"}\n'],
  ['UPDATE_VENDOO.bat', '@echo off\r\n'],
  ['scripts/updater/Vendoo.Updater.Preflight.ps1', 'Write-Output preflight\n'],
  ['scripts/updater/Vendoo.Updater.UI.ps1', 'Write-Output ui\n'],
  ['scripts/updater/Vendoo.Updater.Host.ps1', 'Write-Output host\n'],
  ['scripts/updater/Vendoo.Updater.Worker34.ps1', 'Write-Output worker34\n'],
  ['tools/apply-release-manifest.mjs', 'export {};\n'],
  ['tools/updater-lifecycle-3.4.mjs', 'export const lifecycle = true;\n'],
  ['tools/check-release-payload-3.4.mjs', 'export const payloadGuard = true;\n'],
]);

function write(root, rel, content) {
  const target = join(root, rel);
  mkdirSync(join(target, '..'), { recursive: true });
  writeFileSync(target, content, 'utf8');
}

function writePackageManifest(root, removedFiles = []) {
  const sourceManifest = buildReleaseManifest(root, { version: '1.43.0', updaterVersion: '3.4.0', removedFiles });
  const packageManifest = createPackageManifest(sourceManifest, {
    sourceCommit: '1'.repeat(40),
    baseCommit: '2'.repeat(40),
    createdAt: '2026-07-10T12:00:00Z',
  });
  writeFileSync(join(root, MANIFEST_FILE), `${JSON.stringify(packageManifest, null, 2)}\n`, 'utf8');
  return packageManifest;
}

try {
  mkdirSync(join(destination, '.git'), { recursive: true });
  for (const [rel, body] of requiredBodies) write(source, rel, body);
  write(source, 'public/new.js', 'export const current = 3;\n');
  write(source, 'logs/secret.log', 'must-not-enter-manifest');
  write(destination, 'db/schema.sql', 'OLD SCHEMA');
  write(destination, 'baseline-only.txt', 'must survive overlay');
  write(destination, 'obsolete.txt', 'must be removed explicitly');
  const manifest = writePackageManifest(source, ['obsolete.txt']);
  if (manifest.schema !== PACKAGE_MANIFEST_SCHEMA) throw new Error('Paketmanifest verwendet nicht Schema v2.');
  if (manifest.files.some(entry => entry.path === 'logs/secret.log')) throw new Error('Runtime-Log wurde in das Release-Manifest aufgenommen.');
  const result = applyReleaseManifest(source, destination);
  if (result.copied !== manifest.files.length || result.controlCopied !== 1 || result.removed !== 1) throw new Error('Release-Zähler sind falsch.');
  if (!existsSync(join(destination, 'baseline-only.txt'))) throw new Error('Overlay hat eine nicht deklarierte Baseline-Datei gelöscht.');
  if (existsSync(join(destination, 'obsolete.txt'))) throw new Error('Explizite Löschung wurde nicht angewendet.');
  if (readFileSync(join(destination, 'db/schema.sql'), 'utf8') !== requiredBodies.get('db/schema.sql')) throw new Error('db/schema.sql wurde nicht korrekt überlagert.');
  if (!existsSync(join(destination, MANIFEST_FILE))) throw new Error('release-manifest.json wurde nicht als Steuerdatei in die Arbeitskopie übernommen.');
  readAndValidateManifest(destination, { allowExtraFiles: true });

  let controlDeletionBlocked = false;
  try { buildReleaseManifest(source, { version: '1.43.0', updaterVersion: '3.4.0', removedFiles: [MANIFEST_FILE] }); }
  catch (error) { controlDeletionBlocked = /Steuerdatei/.test(error.message); }
  if (!controlDeletionBlocked) throw new Error('Löschung von release-manifest.json wurde nicht blockiert.');

  const tamperedManifest = JSON.parse(readFileSync(join(source, MANIFEST_FILE), 'utf8'));
  tamperedManifest.baseCommit = '3'.repeat(40);
  writeFileSync(join(source, MANIFEST_FILE), `${JSON.stringify(tamperedManifest, null, 2)}\n`, 'utf8');
  let provenanceBlocked = false;
  try { readAndValidateManifest(source); } catch (error) { provenanceBlocked = /Payload-Hash/.test(error.message); }
  if (!provenanceBlocked) throw new Error('Manipulierte Paket-Provenienz wurde nicht blockiert.');
  writeFileSync(join(source, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');

  const corrupt = join(temp, 'corrupt');
  mkdirSync(corrupt, { recursive: true });
  for (const entry of manifest.files) {
    const target = join(corrupt, entry.path);
    mkdirSync(join(target, '..'), { recursive: true });
    copyFileSync(join(source, entry.path), target);
  }
  copyFileSync(join(source, MANIFEST_FILE), join(corrupt, MANIFEST_FILE));
  writeFileSync(join(corrupt, 'db/schema.sql'), 'CORRUPT', 'utf8');
  let corruptionBlocked = false;
  try { applyReleaseManifest(corrupt, destination); } catch (error) { corruptionBlocked = /SHA-256|Dateigröße/.test(error.message); }
  if (!corruptionBlocked) throw new Error('Manipulierte Release-Datei wurde nicht blockiert.');

  const missing = join(temp, 'missing');
  mkdirSync(missing, { recursive: true });
  for (const entry of manifest.files) {
    if (entry.path === 'db/schema.sql') continue;
    const target = join(missing, entry.path);
    mkdirSync(join(target, '..'), { recursive: true });
    copyFileSync(join(source, entry.path), target);
  }
  copyFileSync(join(source, MANIFEST_FILE), join(missing, MANIFEST_FILE));
  let missingBlocked = false;
  try { applyReleaseManifest(missing, destination); } catch (error) { missingBlocked = /fehlende Paketdatei|Pflichtdatei|verweist auf fehlende/.test(error.message); }
  if (!missingBlocked) throw new Error('Fehlende Pflichtdatei db/schema.sql wurde nicht blockiert.');

  console.log(`Release-Manifest-Gate 3.4 erfolgreich: ${manifest.files.length} Dateien hashgeprüft, Paket-Provenienz validiert, Overlay bewahrt Baseline, Löschungen sind explizit, Manipulation und fehlendes db/schema.sql werden blockiert.`);
} finally {
  rmSync(temp, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
}
