/**
 * Create a production data backup.
 *
 * This uses SQLite's backup API so WAL-mode writes are captured
 * consistently. It also copies non-database data files such as evidence
 * into the same timestamped backup directory.
 */

'use strict';

const fs = require('fs');
const path = require('path');
const Database = require('better-sqlite3');

const root = path.resolve(__dirname, '..');
const sourceDbPath = process.env.DB_PATH || path.join(root, 'data', 'ncii.sqlite');
const sourceDataDir = path.dirname(sourceDbPath);
const backupRoot = process.env.BACKUP_DIR || path.join(root, 'backups');
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
const outDir = path.join(backupRoot, `prod-data-${stamp}`);
const outDataDir = path.join(outDir, 'data-files');
const outDbPath = path.join(outDir, 'ncii.sqlite');

const TABLES = [
  'users',
  'orgs',
  'cases',
  'findings',
  'takedowns',
  'takedown_jobs',
  'case_discovery_scans',
  'link_health_checks',
  'platform_contacts',
  'api_keys',
  'api_tokens',
  'alerts',
  'audit_log',
  'webhooks',
  'webhook_deliveries',
  'email_digest_subscriptions',
  'email_transport',
];

function ensureDir(dir) {
  fs.mkdirSync(dir, { recursive: true });
}

function shouldCopyDataFile(name) {
  return !(
    name.endsWith('.sqlite') ||
    name.endsWith('.sqlite-shm') ||
    name.endsWith('.sqlite-wal') ||
    name.endsWith('.db') ||
    name.endsWith('.db-shm') ||
    name.endsWith('.db-wal') ||
    name.endsWith('.log')
  );
}

function copyDataFiles() {
  if (!fs.existsSync(sourceDataDir)) return [];
  ensureDir(outDataDir);
  const copied = [];
  for (const entry of fs.readdirSync(sourceDataDir, { withFileTypes: true })) {
    if (!shouldCopyDataFile(entry.name)) continue;
    const src = path.join(sourceDataDir, entry.name);
    const dest = path.join(outDataDir, entry.name);
    fs.cpSync(src, dest, { recursive: true, force: true });
    copied.push(entry.name);
  }
  return copied;
}

function inspectBackup() {
  const db = new Database(outDbPath, { readonly: true });
  const counts = {};
  for (const table of TABLES) {
    try {
      counts[table] = db.prepare(`SELECT COUNT(*) AS n FROM ${table}`).get().n;
    } catch (_) {
      counts[table] = null;
    }
  }
  const integrity = db.prepare('PRAGMA integrity_check').get().integrity_check;
  db.close();
  return { counts, integrity };
}

async function main() {
  if (!fs.existsSync(sourceDbPath)) {
    throw new Error(`Database not found: ${sourceDbPath}`);
  }

  ensureDir(outDir);
  const sourceDb = new Database(sourceDbPath, { readonly: true });
  await sourceDb.backup(outDbPath);
  sourceDb.close();

  const copiedDataFiles = copyDataFiles();
  const inspection = inspectBackup();
  const manifest = {
    created_at: new Date().toISOString(),
    source_db: sourceDbPath,
    backup_db: outDbPath,
    copied_data_files: copiedDataFiles,
    integrity_check: inspection.integrity,
    table_counts: inspection.counts,
  };
  fs.writeFileSync(path.join(outDir, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n');

  console.log(`Backup created: ${outDir}`);
  console.log(`Database integrity: ${inspection.integrity}`);
  for (const [table, count] of Object.entries(inspection.counts)) {
    console.log(`${table}: ${count === null ? 'missing' : count}`);
  }
}

main().catch(err => {
  console.error(err.message);
  process.exit(1);
});
