<?php
/**
 * Database Backup & Restore API
 * Handles full SQL export, ZIP compression, and Restoration.
 */
header('Content-Type: application/json');
session_start();

require_once('../libs/classes/DbPathsArray.php');
require_once('../libs/classes/App_DB.php');

if (!isset($_SESSION['userid'])) {
    echo json_encode(['success' => false, 'message' => 'Unauthorized access.']);
    exit;
}

$db = new App_DB(DBU::$dba_user);
$action = $_GET['action'] ?? '';

// Use absolute path for backup directory
$backup_dir = dirname(__DIR__) . '/dbBackup/';
if (!is_dir($backup_dir)) {
    mkdir($backup_dir, 0755, true);
}
// Ensure it ends with a slash
$backup_dir = rtrim($backup_dir, '/') . '/';

switch ($action) {
    case 'backup':
        try {
            $log_file = $backup_dir . 'backup_debug.log';
            file_put_contents($log_file, "Backup started at " . date('Y-m-d H:i:s') . "\n", FILE_APPEND);

            $tables = array();
            $res = $db->getRecordSet("SHOW TABLES");

            if (empty($res)) {
                file_put_contents($log_file, "Error: No tables found.\n", FILE_APPEND);
                throw new Exception("No tables found in database.");
            }

            foreach ($res as $row) {
                // Get the first value of the row, which is always the table name for SHOW TABLES
                $tables[] = current($row);
            }

            file_put_contents($log_file, "Found " . count($tables) . " tables: " . implode(', ', $tables) . "\n", FILE_APPEND);

            $sql_content = "-- Madrasa Management System Backup\n";
            $sql_content .= "-- Generated: " . date('Y-m-d H:i:s') . "\n";
            $sql_content .= "SET NAMES utf8mb4;\n";
            $sql_content .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";

            foreach ($tables as $table) {
                // Table structure
                $res_create_set = $db->getRecordSet("SHOW CREATE TABLE `$table`");
                if (empty($res_create_set)) {
                    file_put_contents($log_file, "Skipping table $table: Show Create Table failed.\n", FILE_APPEND);
                    continue;
                }
                $res_create = $res_create_set[0];
                $sql_content .= "DROP TABLE IF EXISTS `$table`;\n";
                $sql_content .= $res_create['Create Table'] . ";\n\n";

                // Table data
                $res_data = $db->getRecordSet("SELECT * FROM `$table`");
                if (!empty($res_data)) {
                    foreach ($res_data as $row) {
                        $keys = array_keys($row);
                        $values = array_values($row);
                        $escaped_values = array_map(function ($v) use ($db) {
                            if ($v === null)
                                return 'NULL';
                            if (is_numeric($v))
                                return $v;
                            return "'" . str_replace(["'", "\n", "\r"], ["''", "\\n", "\\r"], $v) . "'";
                        }, $values);

                        $sql_content .= "INSERT INTO `$table` (`" . implode("`, `", $keys) . "`) VALUES (" . implode(", ", $escaped_values) . ");\n";
                    }
                }
                $sql_content .= "\n";
                file_put_contents($log_file, "Added table $table with " . count($res_data) . " rows.\n", FILE_APPEND);
            }

            $sql_content .= "SET FOREIGN_KEY_CHECKS = 1;\n";

            $filename = date('Ymd_His') . "_db_backup";
            $sql_file = $backup_dir . $filename . ".sql";
            $zip_file = $backup_dir . $filename . ".zip";

            if (file_put_contents($sql_file, $sql_content) === false) {
                file_put_contents($log_file, "Error: Failed to write SQL file: $sql_file\n", FILE_APPEND);
                throw new Exception("Failed to write SQL file: " . $sql_file);
            }

            file_put_contents($log_file, "SQL file created: $sql_file (" . strlen($sql_content) . " bytes)\n", FILE_APPEND);

            // Compress to ZIP
            if (class_exists('ZipArchive')) {
                $zip = new ZipArchive();
                if ($zip->open($zip_file, ZipArchive::CREATE) === TRUE) {
                    $zip->addFile($sql_file, basename($sql_file));
                    $zip->close();

                    if (file_exists($zip_file)) {
                        file_put_contents($log_file, "ZIP file created: $zip_file (" . filesize($zip_file) . " bytes)\n", FILE_APPEND);
                    } else {
                        file_put_contents($log_file, "Error: ZIP file not found after closing: $zip_file\n", FILE_APPEND);
                        throw new Exception("ZIP archive creation failed.");
                    }

                    // Cleanup SQL file
                    unlink($sql_file);

                    // Output ZIP for download
                    header('Content-Type: application/zip');
                    header('Content-Disposition: attachment; filename="' . basename($zip_file) . '"');
                    header('Content-Length: ' . filesize($zip_file));
                    header('Pragma: no-cache');
                    header('Expires: 0');
                    readfile($zip_file);

                    file_put_contents($log_file, "Backup completed successfully.\n\n", FILE_APPEND);
                    exit;
                } else {
                    file_put_contents($log_file, "Error: Could not open ZIP archive for creation: $zip_file\n", FILE_APPEND);
                    throw new Exception("Could not create ZIP archive.");
                }
            } else {
                file_put_contents($log_file, "Warning: ZipArchive not found. Sending SQL file instead.\n", FILE_APPEND);
                header('Content-Type: application/sql');
                header('Content-Disposition: attachment; filename="' . basename($sql_file) . '"');
                readfile($sql_file);
                unlink($sql_file);
                exit;
            }

        } catch (Exception $e) {
            if (isset($log_file))
                file_put_contents($log_file, "Exception: " . $e->getMessage() . "\n\n", FILE_APPEND);
            header('Content-Type: application/json');
            echo json_encode(['success' => false, 'message' => 'Backup failed: ' . $e->getMessage()]);
        }
        break;

    case 'restore':
        try {
            if (!isset($_FILES['backup_zip'])) {
                throw new Exception("No file uploaded.");
            }

            $file = $_FILES['backup_zip'];
            $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
            if (strtolower($ext) !== 'zip') {
                throw new Exception("Only ZIP files are allowed.");
            }

            $zip = new ZipArchive();
            if ($zip->open($file['tmp_name']) === TRUE) {
                // Extract to temp location
                $extract_path = $backup_dir . 'restore_temp/';
                if (!is_dir($extract_path))
                    mkdir($extract_path);

                $zip->extractTo($extract_path);
                $zip->close();

                // Find the .sql file
                $files = scandir($extract_path);
                $sql_file = '';
                foreach ($files as $f) {
                    if (pathinfo($f, PATHINFO_EXTENSION) === 'sql') {
                        $sql_file = $extract_path . $f;
                        break;
                    }
                }

                if (empty($sql_file)) {
                    throw new Exception("No .sql file found inside the ZIP.");
                }

                $queries = file_get_contents($sql_file);

                // Execute queries
                // Note: Simple split might fail with complex data, but for this app it should work.
                // Better approach: PDO::exec() for the whole block or multi_query if using mysqli.
                // Since our CRUD uses PDO, we can try to execute the whole string or split by semicolon.

                try {
                    $con = $db->getCon();
                    // MySQL DDL statements (DROP, CREATE) cause implicit commits.
                    // Transactions are not effective for full restores containing DDL.
                    $con->exec($queries);

                    // Cleanup
                    if (file_exists($sql_file))
                        unlink($sql_file);
                    if (is_dir($extract_path)) {
                        // Better cleanup: delete all files in temp dir then dir itself
                        $temp_files = array_diff(scandir($extract_path), array('.', '..'));
                        foreach ($temp_files as $tf)
                            unlink($extract_path . $tf);
                        rmdir($extract_path);
                    }

                    echo json_encode(['success' => true, 'message' => 'ڈیٹا بیس کامیابی سے بحال کر دیا گیا ہے۔']);
                    exit;
                } catch (Exception $ex) {
                    throw new Exception("Execution error: " . $ex->getMessage());
                }
            } else {
                throw new Exception("Could not open ZIP file.");
            }
        } catch (Exception $e) {
            echo json_encode(['success' => false, 'message' => 'Restoration failed: ' . $e->getMessage()]);
        }
        break;

    default:
        echo json_encode(['success' => false, 'message' => 'Invalid action.']);
        break;
}
?>