<?php

/*
 * ITFlow - GET/POST request handler for DB / master key backup
 */

defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");

require_once "../includes/app_version.php";

if (isset($_GET['download_backup'])) {
    
    validateCSRFToken($_GET['csrf_token']);
    
    $timestamp = date('YmdHis');
    $baseName = "itflow_$timestamp";

    // === 0. Scoped cleanup ===
    $cleanupFiles = [];

    $registerTempFileForCleanup = function ($file) use (&$cleanupFiles) {
        $cleanupFiles[] = $file;
    };

    register_shutdown_function(function () use (&$cleanupFiles) {
        foreach ($cleanupFiles as $file) {
            if (is_file($file)) {
                @unlink($file);
            }
        }
    });

    // === 1. Local helper function: zipFolder
    $zipFolder = function ($folderPath, $zipFilePath) {
        $zip = new ZipArchive();
        if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
            error_log("Failed to open zip file: $zipFilePath");
            http_response_code(500);
            exit("Internal Server Error: Cannot open zip archive.");
        }

        $folderPath = realpath($folderPath);
        if (!$folderPath) {
            error_log("Invalid folder path: $folderPath");
            http_response_code(500);
            exit("Internal Server Error: Invalid folder path.");
        }

        $files = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($folderPath),
            RecursiveIteratorIterator::LEAVES_ONLY
        );

        foreach ($files as $file) {
            if (!$file->isDir()) {
                $filePath = $file->getRealPath();
                $relativePath = substr($filePath, strlen($folderPath) + 1);
                $zip->addFile($filePath, $relativePath);
            }
        }

        $zip->close();
    };

    // === 2. Create all temp files
    $sqlFile = tempnam(sys_get_temp_dir(), $baseName . "_sql_");
    $uploadsZip = tempnam(sys_get_temp_dir(), $baseName . "_uploads_");
    $versionFile = tempnam(sys_get_temp_dir(), $baseName . "_version_");
    $finalZip = tempnam(sys_get_temp_dir(), $baseName . "_backup_");

    foreach ([$sqlFile, $uploadsZip, $versionFile, $finalZip] as $f) {
        $registerTempFileForCleanup($f);
        chmod($f, 0600);
    }

    // === 3. Generate SQL Dump
    $sqlContent = "-- UTF-8 + Foreign Key Safe Dump\n";
    $sqlContent .= "SET NAMES 'utf8mb4';\n";
    $sqlContent .= "SET foreign_key_checks = 0;\n\n";

    $tables = [];
    $res = $mysqli->query("SHOW TABLES");
    if (!$res) {
        error_log("MySQL Error: " . $mysqli->error);
        exit("Error retrieving tables.");
    }

    while ($row = $res->fetch_row()) {
        $tables[] = $row[0];
    }

    foreach ($tables as $table) {
        $createRes = $mysqli->query("SHOW CREATE TABLE `$table`");
        if (!$createRes) {
            error_log("MySQL Error: " . $mysqli->error);
            continue;
        }

        $createRow = $createRes->fetch_assoc();
        $createSQL = array_values($createRow)[1];

        $sqlContent .= "\n-- ----------------------------\n";
        $sqlContent .= "-- Table structure for `$table`\n";
        $sqlContent .= "-- ----------------------------\n";
        $sqlContent .= "DROP TABLE IF EXISTS `$table`;\n";
        $sqlContent .= $createSQL . ";\n\n";

        $dataRes = $mysqli->query("SELECT * FROM `$table`");
        if ($dataRes && $dataRes->num_rows > 0) {
            $sqlContent .= "-- Dumping data for table `$table`\n";
            while ($row = $dataRes->fetch_assoc()) {
                $columns = array_map(fn($col) => '`' . $mysqli->real_escape_string($col) . '`', array_keys($row));
                $values = array_map(function ($val) use ($mysqli) {
                    return is_null($val) ? "NULL" : "'" . $mysqli->real_escape_string($val) . "'";
                }, array_values($row));
                $sqlContent .= "INSERT INTO `$table` (" . implode(", ", $columns) . ") VALUES (" . implode(", ", $values) . ");\n";
            }
            $sqlContent .= "\n";
        }
    }

    $sqlContent .= "SET foreign_key_checks = 1;\n";
    file_put_contents($sqlFile, $sqlContent);

    // === 4. Zip the uploads folder
    $zipFolder("../uploads", $uploadsZip);

    // === 5. Create version.txt
    $commitHash = trim(shell_exec('git log -1 --format=%H')) ?: 'N/A';
    $gitBranch = trim(shell_exec('git rev-parse --abbrev-ref HEAD')) ?: 'N/A';

    $versionContent = "ITFlow Backup Metadata\n";
    $versionContent .= "-----------------------------\n";
    $versionContent .= "Generated: " . date('Y-m-d H:i:s') . "\n";
    $versionContent .= "Backup File: " . basename($finalZip) . "\n";
    $versionContent .= "Generated By: $session_name\n";
    $versionContent .= "Host: " . gethostname() . "\n";
    $versionContent .= "Git Branch: $gitBranch\n";
    $versionContent .= "Git Commit: $commitHash\n";
    $versionContent .= "ITFlow Version: " . (defined('APP_VERSION') ? APP_VERSION : 'Unknown') . "\n";
    $versionContent .= "Database Version: " . (defined('CURRENT_DATABASE_VERSION') ? CURRENT_DATABASE_VERSION : 'Unknown') . "\n";
    $versionContent .= "Checksum (SHA256): \n";

    file_put_contents($versionFile, $versionContent);

    // === 6. Build final ZIP
    $final = new ZipArchive();
    if ($final->open($finalZip, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
        error_log("Failed to create final zip: $finalZip");
        http_response_code(500);
        exit("Internal Server Error: Unable to create backup archive.");
    }

    $final->addFile($sqlFile, "db.sql");
    $final->addFile($uploadsZip, "uploads.zip");
    $final->addFile($versionFile, "version.txt");
    $final->close();

    chmod($finalZip, 0600);

    $checksum = hash_file('sha256', $finalZip);
    file_put_contents($versionFile, $versionContent . "$checksum\n");

    // === 7. Serve final ZIP
    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="' . basename($finalZip) . '"');
    header('Content-Length: ' . filesize($finalZip));
    header('Pragma: public');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Content-Transfer-Encoding: binary');

    flush();
    $fp = fopen($finalZip, 'rb');
    fpassthru($fp);
    fclose($fp);

    logAction("System", "Backup Download", "$session_name downloaded full backup.");
    flash_alert("Full backup downloaded.");
    exit;
}


if (isset($_POST['backup_master_key'])) {

    validateCSRFToken($_POST['csrf_token']);

    $password = $_POST['password'];

    $sql = mysqli_query($mysqli, "SELECT * FROM users WHERE user_id = $session_user_id");
    $row = mysqli_fetch_array($sql);

    if (password_verify($password, $row['user_password'])) {
        $site_encryption_master_key = decryptUserSpecificKey($row['user_specific_encryption_ciphertext'], $password);
        
        logAction("Master Key", "Download", "$session_name retrieved the master encryption key");

        appNotify("Master Key", "$session_name retrieved the master encryption key");

        echo "==============================";
        echo "<br>Master encryption key:<br>";
        echo "<b>$site_encryption_master_key</b>";
        echo "<br>==============================";
    
    } else {
        logAction("Master Key", "Download", "$session_name attempted to retrieve the master encryption key but failed");

        flash_alert("Incorrect password.", 'error');
        
        redirect();
    }
}

