import { existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs';
import { extname, join, relative, sep, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const root = dirname(dirname(fileURLToPath(import.meta.url)));
const failures = [];
const warnings = [];
const normalize = value => value.split(sep).join('/').replace(/^\.\//, '');

const forbiddenPathPatterns = [
  /(^|\/)node_modules(\/|$)/i,
  /(^|\/)runtime(\/|$)/i,
  /(^|\/)docker-data(\/|$)/i,
  /(^|\/)release-output(\/|$)/i,
  /(^|\/)operations\/(staging|updates|rollback|apply-[^/]+)(\/|$)/i,
  /(^|\/)operations\/(pending-operation|last-operation|first-admin-code)\.json$/i,
  /(^|\/)operations\/first-admin-code\.txt$/i,
  /(^|\/)(uploads|backups|logs|tmp|temp)(\/|$)/i,
  /^sessions(\/|$)/i,
  /(^|\/)updates(\/|$)/i,
  /^modules(\/|$)/i,
  /(^|\/)db\/.*\.(db|sqlite|sqlite3)(-wal|-shm)?$/i,
  /(^|\/)local-ai\/(comfyui|downloads|extract-temp)(\/|$)/i,
  /(^|\/)\.vendoo-install-mode$/i,
  /(^|\/)\.vendoo-github-state\.json$/i,
];

const allowedEnv = new Set(['.env.example', '.env.docker.example']);
const binaryExt = new Set(['.png', '.jpg', '.jpeg', '.gif', '.ico', '.webp', '.zip', '.pdf', '.woff', '.woff2']);
const secretPatterns = [
  ['OpenAI-/kompatibler Schlüssel', /\bsk-[A-Za-z0-9_-]{20,}\b/g],
  ['GitHub Classic Token', /\bgh[pousr]_[A-Za-z0-9]{30,}\b/g],
  ['GitHub Fine-grained Token', /\bgithub_pat_[A-Za-z0-9_]{30,}\b/g],
  ['Slack Token', /\bxox[baprs]-[A-Za-z0-9-]{20,}\b/g],
  ['Private Key', /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g],
  ['AWS Access Key', /\bAKIA[0-9A-Z]{16}\b/g],
];

function walk(dir, out = []) {
  for (const name of readdirSync(dir)) {
    if (['.git', 'node_modules', 'release-output', 'archiv', 'archive', 'archives'].includes(name.toLowerCase())) continue;
    const abs = join(dir, name);
    const rel = normalize(relative(root, abs));
    const stat = lstatSync(abs);
    if (stat.isSymbolicLink()) {
      warnings.push(`Symlink wird nicht geprüft oder gepackt: ${rel}`);
      continue;
    }
    if (stat.isDirectory()) walk(abs, out);
    else out.push(rel);
  }
  return out;
}

// Absichtlich immer den kompletten Arbeitsbaum prüfen. Ein Git-Erstimport enthält
// viele noch unversionierte Dateien; "git ls-files" würde diese nicht erfassen.
const files = walk(root);
for (const rel of files) {
  const envLike = /(^|\/)\.env(?:\..+)?$/i.test(rel);
  if (envLike && !allowedEnv.has(rel)) failures.push(`Verbotene Environment-Datei: ${rel}`);
  if (!allowedEnv.has(rel) && forbiddenPathPatterns.some(pattern => pattern.test(rel))) {
    failures.push(`Verbotene Laufzeit-/Secret-Datei: ${rel}`);
  }

  const abs = join(root, rel);
  if (!existsSync(abs)) continue;
  const stat = lstatSync(abs);
  if (stat.size > 50 * 1024 * 1024) failures.push(`Datei größer als 50 MiB: ${rel}`);
  if (binaryExt.has(extname(rel).toLowerCase()) || stat.size > 5 * 1024 * 1024) continue;

  let text;
  try { text = readFileSync(abs, 'utf8'); } catch { continue; }
  for (const [label, pattern] of secretPatterns) {
    pattern.lastIndex = 0;
    if (pattern.test(text)) failures.push(`${label} in ${rel}`);
  }

  if (!allowedEnv.has(rel)) {
    const envAssignment = /^(?:ANTHROPIC_API_KEY|OPENAI_API_KEY|OPENROUTER_API_KEY|ETSY_API_KEY|EBAY_CLIENT_SECRET|SMTP_PASS|VENDOO_SETUP_TOKEN|LOCAL_IMAGE_TOKEN)=(.+)$/gm;
    for (const match of text.matchAll(envAssignment)) {
      const value = String(match[1] || '').trim();
      if (value && !/^\$\{|^<|^CHANGEME$/i.test(value)) failures.push(`Befülltes Secret-Feld in ${rel}: ${match[0].split('=')[0]}`);
    }
  }
}

const gitignorePath = join(root, '.gitignore');
if (!existsSync(gitignorePath)) {
  failures.push('.gitignore fehlt.');
} else {
  const gitignore = readFileSync(gitignorePath, 'utf8');
  for (const marker of ['.env', 'node_modules/', 'runtime/', 'modules/', '/Archiv/', '/archive/', '/archives/', 'operations/pending-operation.json', 'local-ai/comfyui/', '.vendoo-install-mode']) {
    if (!gitignore.includes(marker)) failures.push(`.gitignore enthält nicht: ${marker}`);
  }
}

const uniqueFailures = [...new Set(failures)];
if (uniqueFailures.length) {
  console.error(`Git-Sicherheitsgate fehlgeschlagen (${uniqueFailures.length}):`);
  uniqueFailures.slice(0, 100).forEach(item => console.error(`- ${item}`));
  if (uniqueFailures.length > 100) console.error(`- ... ${uniqueFailures.length - 100} weitere Treffer`);
  process.exit(1);
}
console.log(`Git-Sicherheitsgate erfolgreich: ${files.length} Dateien geprüft; keine Secrets oder Laufzeitdaten gefunden.`);
warnings.forEach(item => console.warn(`[WARN] ${item}`));
