<?php
// /backup_utils.php - LÓGICA DE COPIA DE SEGURIDAD PARA MySQL (PDO)
// VERSIÓN CORREGIDA: Soluciona permisos en MacOS/XAMPP y falta de shell_exec en restauración.

// Se asume que db.php (que define DB_HOST, DB_USER, etc.) ya ha sido incluido
// y que la variable global $db (objeto PDO) está disponible.

// Ruta al ejecutable 'mysql' (Útil si shell_exec funciona, si no, se ignorará)
define('MYSQL_COMMAND_PATH', 'mysql');

/**
 * Función principal de backup que delega en la implementación de MySQL.
 *
 * @param mixed $dbPath (Obsoleto).
 * @param string $backupDir El directorio donde guardar el archivo SQL.
 * @return array ['status' => 'success'|'error', 'message' => string]
 */
function backupDatabase($dbPath, string $backupDir): array
{
    if (!defined('DB_HOST')) {
        return ['status' => 'error', 'message' => "Error: Las constantes de la base de datos no están definidas. Asegúrate de que 'db.php' está incluido."];
    }

    $db_config = [
        'host' => DB_HOST,
        'user' => DB_USER,
        'pass' => DB_PASS,
        'name' => DB_NAME
    ];

    return backupDatabaseMySQL($db_config, $backupDir);
}

/**
 * Realiza una copia de seguridad nativa de la base de datos MySQL utilizando PDO.
 * Incluye corrección de permisos para evitar errores de escritura.
 *
 * @param array $db_config Array asociativo con 'host', 'user', 'pass', 'name'.
 * @param string $backupDir Directorio donde se guardará el archivo de backup (.sql).
 * @param int $maxBackups El número máximo de copias a mantener.
 * @return array {'status': 'success'|'error', 'message': string}
 */
function backupDatabaseMySQL(array $db_config, string $backupDir, int $maxBackups = 5): array
{
    global $db; // Objeto PDO ya conectado desde db.php

    if (!$db) {
        return ['status' => 'error', 'message' => "Error interno: La conexión PDO no está disponible."];
    }

    // 1. PREPARAR ENTORNO Y PERMISOS (CORRECCIÓN)
    if (!is_dir($backupDir)) {
        // Intentar crear con permisos amplios (0777) para evitar problemas en local/Mac
        if (!mkdir($backupDir, 0777, true)) {
            return ['status' => 'error', 'message' => "No se pudo crear el directorio de backup: $backupDir. Verifica permisos de carpeta padre."];
        }
        // Proteger directorio
        @file_put_contents($backupDir . '/.htaccess', "Deny from all");
    } else {
        // Si ya existe, intentamos asegurar que sea escribible
        @chmod($backupDir, 0777);
    }

    if (!is_writable($backupDir)) {
        return ['status' => 'error', 'message' => "El directorio existe pero NO tiene permisos de escritura: $backupDir"];
    }

    // 2. Crear archivo
    $filename = 'db-backup-' . date('Ymd-His') . '.sql';
    $filepath = rtrim($backupDir, '/') . '/' . $filename;

    $handle = @fopen($filepath, 'w');
    if ($handle === false) {
        $err = error_get_last();
        return ['status' => 'error', 'message' => "No se pudo abrir el archivo para escritura. Error PHP: " . ($err['message'] ?? 'Desconocido')];
    }

    $dbName = $db_config['name'];

    try {
        // Encabezado
        $output = "-- Backup de la base de datos '$dbName'\n";
        $output .= "-- Fecha: " . date("Y-m-d H:i:s") . "\n\n";
        $output .= "SET NAMES utf8mb4;\n";
        $output .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";
        fwrite($handle, $output);

        // Obtener tablas
        $tables = $db->query("SHOW TABLES")->fetchAll(PDO::FETCH_COLUMN);

        foreach ($tables as $table) {
            $table = trim($table);

            // Estructura
            $stmt = $db->query("SHOW CREATE TABLE `$table`");
            $row = $stmt->fetch();
            $createTable = $row['Create Table'];

            fwrite($handle, "\n-- Estructura de `$table`\n");
            fwrite($handle, "DROP TABLE IF EXISTS `$table`;\n");
            fwrite($handle, $createTable . ";\n\n");

            // Datos
            $result = $db->query("SELECT * FROM `$table`");
            if ($result->rowCount() > 0) {
                fwrite($handle, "-- Datos de `$table`\n");

                // Columnas
                $tempResult = $db->query("SELECT * FROM `$table` LIMIT 1");
                $columns = implode("`, `", array_keys($tempResult->fetch(PDO::FETCH_ASSOC)));

                $result = $db->query("SELECT * FROM `$table`");
                $values = [];

                while ($row = $result->fetch(PDO::FETCH_NUM)) {
                    $row = array_map(function($value) use ($db) {
                        if ($value === null) return 'NULL';
                        return $db->quote($value);
                    }, $row);
                    $values[] = "(" . implode(', ', $row) . ")";
                }

                if (!empty($values)) {
                    fwrite($handle, "INSERT INTO `$table` (`$columns`) VALUES\n");
                    fwrite($handle, implode(",\n", $values) . ";\n\n");
                }
            }
        }

        fwrite($handle, "SET FOREIGN_KEY_CHECKS = 1;\n");
        fclose($handle);

        // Rotación
        rotateBackups($backupDir, $maxBackups);

        return ['status' => 'success', 'message' => "Còpia de seguretat creada correctament: $filename"];

    } catch (Exception $e) {
        if ($handle) @fclose($handle);
        if (@file_exists($filepath)) @unlink($filepath);
        error_log("Error backup: " . $e->getMessage());
        return ['status' => 'error', 'message' => 'Error al crear backup: ' . $e->getMessage()];
    }
}

/**
 * Gestiona la rotación de archivos de backup (.sql)
 */
function rotateBackups(string $backupDir, int $maxBackups = 5): void
{
    try {
        $backupFiles = glob(rtrim($backupDir, '/') . '/db-backup-*.sql');
        $backupFiles = array_filter($backupFiles, 'is_file');

        if (count($backupFiles) > $maxBackups) {
            usort($backupFiles, function ($a, $b) {
                return filemtime($a) - filemtime($b); // Más antiguo primero
            });
            $filesToDeleteCount = count($backupFiles) - $maxBackups;
            for ($i = 0; $i < $filesToDeleteCount; $i++) {
                if (file_exists($backupFiles[$i])) {
                    @unlink($backupFiles[$i]);
                }
            }
        }
    } catch (Exception $e) {
         error_log("Error rotación backups: " . $e->getMessage());
    }
}

/**
 * Lista los archivos de copia de seguridad existentes.
 */
function listBackups(string $backupDir): array
{
    $backupFilesInfo = [];
    if (!is_dir($backupDir)) return [];

    $backupFiles = glob(rtrim($backupDir, '/') . '/db-backup-*.sql');
    $backupFiles = array_filter($backupFiles, 'is_file');

    foreach ($backupFiles as $filePath) {
        $timestamp = filemtime($filePath);
        if ($timestamp !== false) {
            $backupFilesInfo[] = [
                'filename' => basename($filePath),
                'timestamp' => $timestamp,
            ];
        }
    }

    usort($backupFilesInfo, function ($a, $b) {
        return $b['timestamp'] - $a['timestamp'];
    });

    return $backupFilesInfo;
}

/**
 * Restaura una base de datos desde un archivo .sql.
 * CORREGIDO: Intenta usar shell_exec, y si falla, usa PHP nativo.
 */
function restoreDatabaseFromSQL(array $db_config, string $sqlFilePath): array
{
    global $db;

    if (!file_exists($sqlFilePath)) {
        return ['status' => 'error', 'message' => 'Error: L\'arxiu SQL no existeix.'];
    }

    // --- INTENTO 1: shell_exec (Rápido) ---
    if (function_exists('shell_exec') && defined('MYSQL_COMMAND_PATH') && file_exists(MYSQL_COMMAND_PATH)) {
        $mysql_exec = escapeshellarg(MYSQL_COMMAND_PATH);
        $host = escapeshellarg($db_config['host']);
        $user = escapeshellarg($db_config['user']);
        $pass = escapeshellarg($db_config['pass']);
        $name = escapeshellarg($db_config['name']);
        $path = escapeshellarg($sqlFilePath);

        // Comando seguro que suprime errores estándar en la salida si tiene éxito
        $command = sprintf('%s -h %s -u %s --password=%s %s < %s 2>&1', $mysql_exec, $host, $user, $pass, $name, $path);

        $output = shell_exec($command);

        // Si no hay salida, shell_exec funcionó bien
        if (empty($output)) {
            return ['status' => 'success', 'message' => 'Restauració completada (Mode Ràpid).'];
        }
        // Si falló, logueamos y seguimos al plan B
        error_log("Shell exec falló: $output");
    }

    // --- INTENTO 2: PHP Nativo (Compatible sin shell_exec) ---
    if (!$db) {
        return ['status' => 'error', 'message' => 'Error intern: No hi ha connexió a la BBDD.'];
    }

    try {
        $db->exec("SET FOREIGN_KEY_CHECKS = 0"); // Desactivar checks temporalmente

        $handle = fopen($sqlFilePath, "r");
        if (!$handle) {
            return ['status' => 'error', 'message' => 'Error: No es pot llegir l\'arxiu SQL.'];
        }

        $query = '';
        while (($line = fgets($handle)) !== false) {
            $trimLine = trim($line);
            // Ignorar comentarios y líneas vacías
            if ($trimLine === '' || strpos($trimLine, '--') === 0 || strpos($trimLine, '/*') === 0) {
                continue;
            }

            $query .= $line;
            // Si termina en punto y coma, ejecutar
            if (substr(trim($line), -1, 1) == ';') {
                try {
                    $db->exec($query);
                } catch (PDOException $e) {
                    // Continuar aunque falle una línea específica (ej: tabla ya existe)
                    error_log("Error SQL puntual en restore: " . $e->getMessage());
                }
                $query = '';
            }
        }
        fclose($handle);
        $db->exec("SET FOREIGN_KEY_CHECKS = 1");

        return ['status' => 'success', 'message' => 'Restauració completada (Mode Compatibilitat).'];

    } catch (Exception $e) {
        return ['status' => 'error', 'message' => 'Error greu durant la restauració: ' . $e->getMessage()];
    }
}
?>
