const fs = require('fs');
const path = require('path');
const os = require('os');
const { execSync } = require('child_process');
const { getDb } = require('../db');

const APP_VERSION = '2.0.0';
const MAX_STORED_BACKUPS = 20;

function getPaths() {
  const root = path.join(__dirname, '..');
  const dataDir = process.env.DB_PATH
    ? path.dirname(path.resolve(process.env.DB_PATH))
    : path.join(root, 'data');
  return {
    root,
    dataDir,
    dbPath: process.env.DB_PATH || path.join(dataDir, 'consulting.db'),
    sessionsPath: path.join(dataDir, 'sessions.db'),
    uploadsDir: path.join(dataDir, 'uploads'),
    backupsDir: path.join(dataDir, 'backups')
  };
}

function ensureBackupsDir() {
  const { backupsDir } = getPaths();
  if (!fs.existsSync(backupsDir)) fs.mkdirSync(backupsDir, { recursive: true });
  return backupsDir;
}

function checkpointDatabases() {
  try {
    const db = getDb();
    db.pragma('wal_checkpoint(TRUNCATE)');
  } catch (e) { /* ignore */ }
  const { sessionsPath } = getPaths();
  if (fs.existsSync(sessionsPath)) {
    try {
      const Database = require('better-sqlite3');
      const sdb = new Database(sessionsPath);
      sdb.pragma('wal_checkpoint(TRUNCATE)');
      sdb.close();
    } catch (e) { /* ignore */ }
  }
}

function collectStats() {
  const db = getDb();
  const tables = ['clients', 'engagements', 'racks', 'rack_devices', 'data_diagrams', 'projects', 'invoices', 'assessments', 'users'];
  const counts = {};
  for (const t of tables) {
    try {
      counts[t] = db.prepare(`SELECT COUNT(*) as c FROM ${t}`).get().c;
    } catch (e) {
      counts[t] = 0;
    }
  }
  const { dbPath, sessionsPath, uploadsDir, dataDir } = getPaths();
  let uploadsBytes = 0;
  let uploadFiles = 0;
  if (fs.existsSync(uploadsDir)) {
    const walk = (dir) => {
      for (const name of fs.readdirSync(dir)) {
        const p = path.join(dir, name);
        const st = fs.statSync(p);
        if (st.isDirectory()) walk(p);
        else { uploadsBytes += st.size; uploadFiles++; }
      }
    };
    walk(uploadsDir);
  }
  return {
    counts,
    dbSizeBytes: fs.existsSync(dbPath) ? fs.statSync(dbPath).size : 0,
    sessionsSizeBytes: fs.existsSync(sessionsPath) ? fs.statSync(sessionsPath).size : 0,
    uploadsBytes,
    uploadFiles,
    dataDir
  };
}

function formatBytes(n) {
  if (n < 1024) return n + ' B';
  if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';
  if (n < 1024 * 1024 * 1024) return (n / 1024 / 1024).toFixed(1) + ' MB';
  return (n / 1024 / 1024 / 1024).toFixed(2) + ' GB';
}

function timestampLabel() {
  const d = new Date();
  const pad = (n) => String(n).padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
}

function safeBackupName(name) {
  return /^mek-tech-backup-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}\.tar\.gz$/.test(name);
}

function pruneOldBackups() {
  const list = listBackups();
  if (list.length <= MAX_STORED_BACKUPS) return;
  const toRemove = list.slice(MAX_STORED_BACKUPS);
  for (const b of toRemove) {
    try { fs.unlinkSync(b.path); } catch (e) { /* ignore */ }
  }
}

function createBackup() {
  ensureBackupsDir();
  checkpointDatabases();

  const paths = getPaths();
  const stats = collectStats();
  const label = timestampLabel();
  const bundleName = `mek-tech-backup-${label}`;
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mek-backup-'));
  const bundleDir = path.join(tmpDir, bundleName);

  try {
    fs.mkdirSync(bundleDir, { recursive: true });

    const manifest = {
      app: 'mek-tech-consulting-framework',
      version: APP_VERSION,
      created_at: new Date().toISOString(),
      stats: stats.counts,
      includes: []
    };

    if (fs.existsSync(paths.dbPath)) {
      fs.copyFileSync(paths.dbPath, path.join(bundleDir, 'consulting.db'));
      manifest.includes.push('consulting.db');
    }
    if (fs.existsSync(paths.sessionsPath)) {
      fs.copyFileSync(paths.sessionsPath, path.join(bundleDir, 'sessions.db'));
      manifest.includes.push('sessions.db');
    }
    if (fs.existsSync(paths.uploadsDir)) {
      copyDirSync(paths.uploadsDir, path.join(bundleDir, 'uploads'));
      manifest.includes.push('uploads/');
    }

    fs.writeFileSync(path.join(bundleDir, 'manifest.json'), JSON.stringify(manifest, null, 2));

    const archiveName = `${bundleName}.tar.gz`;
    const archivePath = path.join(paths.backupsDir, archiveName);
    execSync(`tar -czf "${archivePath}" -C "${tmpDir}" "${bundleName}"`, { stdio: 'pipe' });

    const size = fs.statSync(archivePath).size;
    pruneOldBackups();

    return {
      filename: archiveName,
      path: archivePath,
      size,
      sizeLabel: formatBytes(size),
      created_at: manifest.created_at,
      stats: stats.counts
    };
  } finally {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  }
}

function copyDirSync(src, dest) {
  fs.mkdirSync(dest, { recursive: true });
  for (const name of fs.readdirSync(src)) {
    const s = path.join(src, name);
    const d = path.join(dest, name);
    if (fs.statSync(s).isDirectory()) copyDirSync(s, d);
    else fs.copyFileSync(s, d);
  }
}

function listBackups() {
  ensureBackupsDir();
  const { backupsDir } = getPaths();
  if (!fs.existsSync(backupsDir)) return [];
  return fs.readdirSync(backupsDir)
    .filter(f => f.endsWith('.tar.gz') && safeBackupName(f))
    .map(f => {
      const p = path.join(backupsDir, f);
      const st = fs.statSync(p);
      return {
        filename: f,
        path: p,
        size: st.size,
        sizeLabel: formatBytes(st.size),
        created_at: st.mtime.toISOString()
      };
    })
    .sort((a, b) => b.created_at.localeCompare(a.created_at));
}

function getBackupPath(filename) {
  if (!safeBackupName(filename)) return null;
  const p = path.join(getPaths().backupsDir, filename);
  return fs.existsSync(p) ? p : null;
}

function deleteBackup(filename) {
  const p = getBackupPath(filename);
  if (!p) return false;
  fs.unlinkSync(p);
  return true;
}

function validateArchive(archivePath) {
  if (!fs.existsSync(archivePath)) throw new Error('Backupbestand niet gevonden');
  const listing = execSync(`tar -tzf "${archivePath}"`, { encoding: 'utf8' });
  if (!listing.includes('manifest.json')) throw new Error('Ongeldig backup: manifest.json ontbreekt');
  if (!listing.includes('consulting.db')) throw new Error('Ongeldig backup: consulting.db ontbreekt');
  return listing;
}

function restoreBackup(archivePath) {
  validateArchive(archivePath);

  const auto = createBackup();
  const paths = getPaths();
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mek-restore-'));

  try {
    execSync(`tar -xzf "${archivePath}" -C "${tmpDir}"`, { stdio: 'pipe' });
    const entries = fs.readdirSync(tmpDir);
    const bundleDir = entries.length === 1 ? path.join(tmpDir, entries[0]) : tmpDir;

    const manifestPath = path.join(bundleDir, 'manifest.json');
    if (!fs.existsSync(manifestPath)) throw new Error('manifest.json niet gevonden in backup');

    checkpointDatabases();

    const dbDest = paths.dbPath;
    const dbSrc = path.join(bundleDir, 'consulting.db');
    if (fs.existsSync(dbSrc)) {
      for (const ext of ['-wal', '-shm']) {
        const sidecar = dbDest + ext;
        if (fs.existsSync(sidecar)) fs.unlinkSync(sidecar);
      }
      fs.copyFileSync(dbSrc, dbDest);
    }

    const sessSrc = path.join(bundleDir, 'sessions.db');
    if (fs.existsSync(sessSrc)) {
      for (const ext of ['-wal', '-shm']) {
        const sidecar = paths.sessionsPath + ext;
        if (fs.existsSync(sidecar)) fs.unlinkSync(sidecar);
      }
      fs.copyFileSync(sessSrc, paths.sessionsPath);
    }

    const uploadsSrc = path.join(bundleDir, 'uploads');
    if (fs.existsSync(uploadsSrc)) {
      if (fs.existsSync(paths.uploadsDir)) fs.rmSync(paths.uploadsDir, { recursive: true, force: true });
      copyDirSync(uploadsSrc, paths.uploadsDir);
    }

    return {
      success: true,
      preRestoreBackup: auto.filename,
      restored_at: new Date().toISOString()
    };
  } finally {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  }
}

module.exports = {
  getPaths,
  collectStats,
  formatBytes,
  createBackup,
  listBackups,
  getBackupPath,
  deleteBackup,
  restoreBackup,
  safeBackupName,
  APP_VERSION
};
