import {
  chmodSync,
  copyFileSync,
  existsSync,
  lstatSync,
  mkdirSync,
  readdirSync,
  rmSync,
} from 'node:fs';
import { basename, dirname, extname, join, relative, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';

const normalize = value => value.split(sep).join('/').replace(/^\.\//, '');
const allowedEnvTemplates = new Set(['.env.example', '.env.docker.example']);
const skippedTopLevelDirs = new Set([
  'node_modules', 'runtime', 'docker-data', 'uploads', 'backups', 'logs',
  'sessions', 'tmp', 'temp', '__pycache__', 'release-output', 'operations', 'updates', 'modules',
  'archiv', 'archive', 'archives',
]);
const skippedLocalAiDirs = new Set(['local-ai/comfyui', 'local-ai/downloads', 'local-ai/extract-temp']);
const skippedExactFiles = new Set([
  '.vendoo-install-mode', '.vendoo-github-state.json',
  'pending-operation.json', 'last-operation.json', 'first-admin-code.txt',
]);
const skippedExtensions = new Set([
  '.db', '.sqlite', '.sqlite3', '.log', '.key', '.pem', '.pfx', '.p12',
]);

export function shouldSkipStagingEntry(relativePath, stat) {
  const rel = normalize(relativePath);
  const parts = rel.split('/');
  const name = basename(rel);
  const lower = rel.toLowerCase();

  if (!rel || rel === '.git' || lower.startsWith('.git/')) return true;
  if (skippedTopLevelDirs.has(parts[0].toLowerCase())) return true;
  if (parts.some(part => part.toLowerCase() === '__pycache__') || /\.pyc$/i.test(rel)) return true;
  if ([...skippedLocalAiDirs].some(dir => lower === dir || lower.startsWith(`${dir}/`))) return true;
  if (/^\.env(?:\..+)?$/i.test(name) && !allowedEnvTemplates.has(rel)) return true;
  if (skippedExactFiles.has(name.toLowerCase())) return true;
  if (/\.(?:db|sqlite|sqlite3)-(?:wal|shm)$/i.test(name)) return true;
  if (skippedExtensions.has(extname(name).toLowerCase())) return true;
  if (stat.isSymbolicLink()) return true;
  return false;
}

function cleanDestination(destination) {
  if (!existsSync(join(destination, '.git'))) {
    throw new Error(`Ziel ist kein geklontes Git-Repository: ${destination}`);
  }
  for (const name of readdirSync(destination)) {
    if (name === '.git') continue;
    rmSync(join(destination, name), { recursive: true, force: true });
  }
}

export function prepareGitStaging(sourceRoot, destinationRoot) {
  const source = resolve(sourceRoot);
  const destination = resolve(destinationRoot);
  if (source === destination) throw new Error('Quell- und Zielordner dürfen nicht identisch sein.');
  if (!existsSync(source)) throw new Error(`Quellordner fehlt: ${source}`);
  if (!existsSync(destination)) throw new Error(`Zielordner fehlt: ${destination}`);

  cleanDestination(destination);
  let copied = 0;
  let skipped = 0;

  function walk(current) {
    for (const name of readdirSync(current)) {
      const absolute = join(current, name);
      const rel = normalize(relative(source, absolute));
      const stat = lstatSync(absolute);
      if (shouldSkipStagingEntry(rel, stat)) {
        skipped += 1;
        continue;
      }
      const target = join(destination, rel);
      if (stat.isDirectory()) {
        mkdirSync(target, { recursive: true });
        walk(absolute);
      } else if (stat.isFile()) {
        mkdirSync(dirname(target), { recursive: true });
        copyFileSync(absolute, target);
        try { chmodSync(target, stat.mode & 0o777); } catch {}
        copied += 1;
      }
    }
  }

  walk(source);
  return { source, destination, copied, skipped };
}

const invokedPath = process.argv[1] ? resolve(process.argv[1]) : '';
if (invokedPath === fileURLToPath(import.meta.url)) {
  const [, , source, destination] = process.argv;
  if (!source || !destination) {
    console.error('Aufruf: node tools/prepare-git-staging.mjs <Quellordner> <geklontes Zielrepository>');
    process.exit(64);
  }
  try {
    const result = prepareGitStaging(source, destination);
    console.log(`Git-Staging vorbereitet: ${result.copied} Quelldateien kopiert; ${result.skipped} lokale Einträge ausgeschlossen.`);
  } catch (error) {
    console.error(`[FEHLER] Git-Staging konnte nicht vorbereitet werden: ${error.message}`);
    process.exit(1);
  }
}
