#!/usr/bin/env php
<?php
/**
 * Batch metadata backup — securite/ directory.
 *
 * Creates a ZIP archive of the entire securite/ tree (database, master key,
 * wallets) excluding previous backup archives to avoid nesting.
 *
 * Usage:
 *   php exploitation/backup.php [options]
 *
 * Options:
 *   -o, --output <path>   Output directory (default: securite/backups/)
 *   -n, --name <name>     Archive filename (default: metadata_YYYYMMDD_HHMMSS.zip)
 *   -k, --keep <days>     Delete backups older than N days (default: no purge)
 *   -q, --quiet           Suppress output (exit code only)
 *   -h, --help            Show this help
 *
 * Exit codes:
 *   0  Success
 *   1  Error (missing extension, write failure, etc.)
 *
 * Examples:
 *   php exploitation/backup.php
 *   php exploitation/backup.php --keep 30
 *   php exploitation/backup.php -o /mnt/backup -n daily_backup.zip
 *   php exploitation/backup.php --quiet --keep 7
 *
 * Cron (daily at 02:00):
 *   0 2 * * * cd /opt/app && php exploitation/backup.php --keep 30 --quiet
 *
 * Windows Task Scheduler:
 *   php.exe C:\app\exploitation\backup.php --keep 30 --quiet
 */

require_once dirname(__DIR__) . '/shared/cli_bootstrap.php';

/* ── CLI arguments ─────────────────────────────────────────────────── */
$opts = getopt('o:n:k:qh', ['output:', 'name:', 'keep:', 'quiet', 'help']);

if (isset($opts['h']) || isset($opts['help'])) cli_show_help(__FILE__);

$quiet   = isset($opts['q']) || isset($opts['quiet']);
$outDir  = $opts['o'] ?? $opts['output'] ?? SECURITE_PATH . '/backups';
$name    = $opts['n'] ?? $opts['name']   ?? 'metadata_' . date('Ymd_His') . '.zip';
$keepDays = null;
if (isset($opts['k'])) $keepDays = (int)$opts['k'];
if (isset($opts['keep'])) $keepDays = (int)$opts['keep'];

function out(string $msg): void {
    global $quiet;
    if (!$quiet) echo $msg . PHP_EOL;
}

/* ── Validation ────────────────────────────────────────────────────── */
if (!class_exists('ZipArchive')) {
    out('ERROR: PHP ZipArchive extension is not available.');
    exit(1);
}

if (!is_dir(SECURITE_PATH)) {
    out('ERROR: securite/ directory does not exist: ' . SECURITE_PATH);
    exit(1);
}

if (!is_dir($outDir)) {
    if (!mkdir($outDir, 0750, true)) {
        out('ERROR: Cannot create output directory: ' . $outDir);
        exit(1);
    }
}

/* ── Create backup ─────────────────────────────────────────────────── */
$filepath = rtrim($outDir, '/\\') . '/' . $name;

$zip = new ZipArchive();
if ($zip->open($filepath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
    out('ERROR: Cannot create ZIP archive: ' . $filepath);
    exit(1);
}

$fileCount = 0;
$it = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator(SECURITE_PATH, FilesystemIterator::SKIP_DOTS),
    RecursiveIteratorIterator::SELF_FIRST
);

foreach ($it as $file) {
    $realPath = $file->getRealPath();
    $relPath  = ltrim(substr($realPath, strlen(SECURITE_PATH)), '/\\');

    /* Exclude backups/ to avoid nesting */
    if (str_starts_with($relPath, 'backups')) continue;

    if ($file->isDir()) {
        $zip->addEmptyDir($relPath);
    } else {
        $zip->addFile($realPath, $relPath);
        $fileCount++;
    }
}

$zip->close();

$size = filesize($filepath);
$sizeStr = $size >= 1048576
    ? round($size / 1048576, 1) . ' MB'
    : round($size / 1024, 1) . ' KB';

out("OK: Backup created — {$filepath} ({$fileCount} files, {$sizeStr})");

/* ── Purge old backups ─────────────────────────────────────────────── */
if ($keepDays !== null && $keepDays > 0) {
    $cutoff  = time() - ($keepDays * 86400);
    $purged  = 0;

    foreach (scandir($outDir) as $f) {
        if ($f === '.' || $f === '..') continue;
        if (!str_ends_with($f, '.zip')) continue;
        $full = $outDir . '/' . $f;
        if ($full === $filepath) continue; // never delete the one we just created
        if (filemtime($full) < $cutoff) {
            unlink($full);
            $purged++;
        }
    }

    if ($purged > 0) {
        out("Purged {$purged} backup(s) older than {$keepDays} days.");
    }
}

exit(0);
