<?php
// backup.php - Full system backup (SQL + source ZIP)
// v2.0: display_errors off, wrapped in Throwable handler for sanitized output.

ini_set('display_errors', 0);
ini_set('log_errors', 1);
error_reporting(E_ALL);

set_time_limit(600);
ini_set('memory_limit', '512M');

require 'db.php';

if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
    header('Content-Type: text/html; charset=utf-8');
    die("Access Denied. Please <a href='index.php'>login</a> first.");
}

if (!class_exists('ZipArchive')) {
    header('Content-Type: text/html; charset=utf-8');
    die("Error: PHP ZipArchive extension is missing.");
}

$zipFilename = 'FONFIX_FULL_BACKUP_' . date('Y-m-d_H-i-s') . '.zip';
$sqlTempFile = null;
$zipTempFile = null;

try {
    // --- STEP 1: SQL DUMP (streamed to temp file) ---
    $sqlTempFile = tempnam(sys_get_temp_dir(), 'sql_dump');
    if ($sqlTempFile === false) {
        throw new RuntimeException('Cannot write to temporary directory.');
    }
    $handle = fopen($sqlTempFile, 'w');
    if (!$handle) {
        throw new RuntimeException('Cannot open SQL temp file for writing.');
    }

    fwrite($handle, "-- FONFIX SYSTEM BACKUP\n-- Generated: " . date('Y-m-d H:i:s') . "\n\n");
    fwrite($handle, "SET FOREIGN_KEY_CHECKS=0;\n\n");

    $tables = [];
    $result = $pdo->query('SHOW TABLES');
    while ($row = $result->fetch(PDO::FETCH_NUM)) { $tables[] = $row[0]; }

    foreach ($tables as $table) {
        $row2 = $pdo->query('SHOW CREATE TABLE ' . $table)->fetch(PDO::FETCH_NUM);
        fwrite($handle, "\n\n" . $row2[1] . ";\n\n");

        $stmt = $pdo->query('SELECT * FROM ' . $table);
        while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
            $values = [];
            foreach ($row as $val) {
                if (!isset($val)) {
                    $values[] = "NULL";
                } else {
                    // PDO::quote() is charset-aware (utf8mb4) and handles
                    // edge cases addslashes() misses (e.g. multi-byte
                    // characters that addslashes can corrupt). Returns the
                    // value already wrapped in single quotes.
                    $values[] = $pdo->quote((string) $val);
                }
            }
            $line = "INSERT INTO $table VALUES(" . implode(',', $values) . ");\n";
            fwrite($handle, $line);
        }
    }
    fwrite($handle, "\n\nSET FOREIGN_KEY_CHECKS=1;");
    fclose($handle);

    // --- STEP 2: CREATE ZIP ARCHIVE ---
    $zip = new ZipArchive();
    $zipTempFile = tempnam(sys_get_temp_dir(), 'fonfix_zip');
    if ($zipTempFile === false) {
        throw new RuntimeException('Cannot create zip temp file.');
    }
    unlink($zipTempFile);

    if ($zip->open($zipTempFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
        throw new RuntimeException('Cannot create zip file.');
    }

    $zip->addFile($sqlTempFile, 'database_dump.sql');

    $rootPath = realpath(__DIR__);
    $files = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($rootPath, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::LEAVES_ONLY
    );

    foreach ($files as $name => $file) {
        if (!$file->isDir()) {
            $filePath = $file->getRealPath();
            if (!$filePath) continue;

            $relativePath = substr($filePath, strlen($rootPath) + 1);

            if (strpos($filePath, '.zip') !== false) continue;
            if (strpos($filePath, 'error_log') !== false) continue;
            if (strpos($relativePath, '.') === 0) continue;

            $zip->addFile($filePath, $relativePath);
        }
    }

    $zip->close();

    // --- STEP 3: DOWNLOAD ---
    if (!file_exists($zipTempFile)) {
        throw new RuntimeException('Backup file could not be created.');
    }
    if (ob_get_level()) ob_end_clean();

    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="' . $zipFilename . '"');
    header('Content-Length: ' . filesize($zipTempFile));
    header('Pragma: no-cache');

    readfile($zipTempFile);

    @unlink($zipTempFile);
    @unlink($sqlTempFile);
    exit;
} catch (\Throwable $e) {
    error_log('[backup.php] ' . $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine());
    if (is_string($sqlTempFile) && file_exists($sqlTempFile)) { @unlink($sqlTempFile); }
    if (is_string($zipTempFile) && file_exists($zipTempFile)) { @unlink($zipTempFile); }
    if (ob_get_level()) { @ob_end_clean(); }
    http_response_code(500);
    header('Content-Type: text/html; charset=utf-8');
    echo "<h2>Backup Failed</h2>";
    echo "<p>The system could not complete the backup. The administrator has been notified via error log.</p>";
    echo "<p><a href='dashboard.php'>&larr; Back to Dashboard</a></p>";
    exit;
}
