<?php
/**
 * Sistema de Migración de Base de Datos
 * Sistema de Inventario Forestal de Guatemala V1.0
 * 
 * Este script maneja las migraciones de la base de datos para actualizaciones futuras
 */

require_once __DIR__ . '/../api/config/database.php';

class DatabaseMigration {
    private $pdo;
    private $currentVersion;
    private $targetVersion;
    private $migrations = [];
    
    public function __construct() {
        try {
            $database = new Database();
            $this->pdo = $database->getConnection();
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            
            $this->initializeMigrationTable();
            $this->loadMigrations();
            $this->currentVersion = $this->getCurrentVersion();
            
        } catch (Exception $e) {
            die("Error de conexión a la base de datos: " . $e->getMessage());
        }
    }
    
    /**
     * Crear tabla de migraciones si no existe
     */
    private function initializeMigrationTable() {
        $sql = "
            CREATE TABLE IF NOT EXISTS `migraciones` (
                `id` INT(11) NOT NULL AUTO_INCREMENT,
                `version` VARCHAR(20) NOT NULL,
                `descripcion` TEXT NOT NULL,
                `fecha_aplicacion` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                `estado` ENUM('aplicada', 'revertida') DEFAULT 'aplicada',
                PRIMARY KEY (`id`),
                UNIQUE KEY `version_unique` (`version`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
        ";
        
        $this->pdo->exec($sql);
        
        // Insertar migración inicial si no existe
        $stmt = $this->pdo->prepare("
            INSERT IGNORE INTO migraciones (version, descripcion) 
            VALUES ('1.0.0', 'Esquema inicial del sistema')
        ");
        $stmt->execute();
    }
    
    /**
     * Cargar definiciones de migraciones
     */
    private function loadMigrations() {
        $this->migrations = [
            '1.0.1' => [
                'description' => 'Agregar campos de auditoría a tablas principales',
                'up' => [
                    "ALTER TABLE usuarios ADD COLUMN fecha_ultima_modificacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
                    "ALTER TABLE proyectos ADD COLUMN fecha_ultima_modificacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
                    "ALTER TABLE rodales ADD COLUMN fecha_ultima_modificacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
                ],
                'down' => [
                    "ALTER TABLE usuarios DROP COLUMN fecha_ultima_modificacion",
                    "ALTER TABLE proyectos DROP COLUMN fecha_ultima_modificacion",
                    "ALTER TABLE rodales DROP COLUMN fecha_ultima_modificacion"
                ]
            ],
            
            '1.0.2' => [
                'description' => 'Agregar tabla de logs de auditoría',
                'up' => [
                    "CREATE TABLE `logs_auditoria` (
                        `id` INT(11) NOT NULL AUTO_INCREMENT,
                        `tabla` VARCHAR(50) NOT NULL,
                        `registro_id` INT(11) NOT NULL,
                        `accion` ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
                        `datos_anteriores` JSON NULL,
                        `datos_nuevos` JSON NULL,
                        `usuario_id` INT(11) NOT NULL,
                        `fecha_accion` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                        `ip_address` VARCHAR(45) NULL,
                        `user_agent` TEXT NULL,
                        PRIMARY KEY (`id`),
                        KEY `idx_tabla_registro` (`tabla`, `registro_id`),
                        KEY `idx_usuario_fecha` (`usuario_id`, `fecha_accion`),
                        KEY `idx_fecha` (`fecha_accion`),
                        CONSTRAINT `fk_logs_usuario` FOREIGN KEY (`usuario_id`) REFERENCES `usuarios` (`id`) ON DELETE RESTRICT
                    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
                ],
                'down' => [
                    "DROP TABLE IF EXISTS `logs_auditoria`"
                ]
            ],
            
            '1.0.3' => [
                'description' => 'Agregar índices de rendimiento adicionales',
                'up' => [
                    "CREATE INDEX `idx_datos_campo_fecha_usuario` ON `datos_campo` (`fecha_captura`, `usuario_id`)",
                    "CREATE INDEX `idx_especies_nombre_activa` ON `especies` (`nombre_comun`, `activa`)",
                    "CREATE INDEX `idx_proyectos_departamento_municipio` ON `proyectos` (`departamento`, `municipio`)"
                ],
                'down' => [
                    "DROP INDEX `idx_datos_campo_fecha_usuario` ON `datos_campo`",
                    "DROP INDEX `idx_especies_nombre_activa` ON `especies`",
                    "DROP INDEX `idx_proyectos_departamento_municipio` ON `proyectos`"
                ]
            ],
            
            '1.1.0' => [
                'description' => 'Agregar soporte para coordenadas de rodales y parcelas',
                'up' => [
                    "ALTER TABLE rodales ADD COLUMN latitud_centro DECIMAL(10, 8) NULL AFTER area_ha",
                    "ALTER TABLE rodales ADD COLUMN longitud_centro DECIMAL(11, 8) NULL AFTER latitud_centro",
                    "ALTER TABLE rodales ADD COLUMN elevacion_msnm INT(11) NULL AFTER longitud_centro",
                    "CREATE TABLE `parcelas` (
                        `id` INT(11) NOT NULL AUTO_INCREMENT,
                        `rodal_id` INT(11) NOT NULL,
                        `numero_parcela` INT(11) NOT NULL,
                        `latitud` DECIMAL(10, 8) NOT NULL,
                        `longitud` DECIMAL(11, 8) NOT NULL,
                        `area_m2` DECIMAL(8,2) DEFAULT 400.00,
                        `forma` ENUM('circular', 'cuadrada', 'rectangular') DEFAULT 'circular',
                        `fecha_establecimiento` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                        `activa` BOOLEAN DEFAULT TRUE,
                        PRIMARY KEY (`id`),
                        UNIQUE KEY `rodal_parcela_unique` (`rodal_id`, `numero_parcela`),
                        KEY `idx_rodal_activa` (`rodal_id`, `activa`),
                        CONSTRAINT `fk_parcelas_rodal` FOREIGN KEY (`rodal_id`) REFERENCES `rodales` (`id`) ON DELETE CASCADE
                    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
                ],
                'down' => [
                    "DROP TABLE IF EXISTS `parcelas`",
                    "ALTER TABLE rodales DROP COLUMN elevacion_msnm",
                    "ALTER TABLE rodales DROP COLUMN longitud_centro",
                    "ALTER TABLE rodales DROP COLUMN latitud_centro"
                ]
            ],
            
            '1.2.0' => [
                'description' => 'Agregar soporte para múltiples mediciones por árbol',
                'up' => [
                    "CREATE TABLE `mediciones_arbol` (
                        `id` INT(11) NOT NULL AUTO_INCREMENT,
                        `datos_campo_id` INT(11) NOT NULL,
                        `fecha_medicion` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                        `dap_cm` DECIMAL(6,2) NOT NULL,
                        `altura_m` DECIMAL(6,2) NOT NULL,
                        `condicion_arbol` ENUM('Sano', 'Bifurcado', 'Plagado', 'Muerto en Pie') NOT NULL,
                        `observaciones` TEXT NULL,
                        `usuario_id` INT(11) NOT NULL,
                        PRIMARY KEY (`id`),
                        KEY `idx_datos_campo_fecha` (`datos_campo_id`, `fecha_medicion`),
                        CONSTRAINT `fk_mediciones_datos_campo` FOREIGN KEY (`datos_campo_id`) REFERENCES `datos_campo` (`id`) ON DELETE CASCADE,
                        CONSTRAINT `fk_mediciones_usuario` FOREIGN KEY (`usuario_id`) REFERENCES `usuarios` (`id`) ON DELETE RESTRICT
                    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
                    "ALTER TABLE datos_campo ADD COLUMN numero_arbol INT(11) NULL AFTER numero_parcela",
                    "ALTER TABLE datos_campo ADD COLUMN es_medicion_inicial BOOLEAN DEFAULT TRUE AFTER estado_sincronizacion"
                ],
                'down' => [
                    "ALTER TABLE datos_campo DROP COLUMN es_medicion_inicial",
                    "ALTER TABLE datos_campo DROP COLUMN numero_arbol",
                    "DROP TABLE IF EXISTS `mediciones_arbol`"
                ]
            ]
        ];
    }
    
    /**
     * Obtener versión actual de la base de datos
     */
    private function getCurrentVersion() {
        try {
            $stmt = $this->pdo->query("
                SELECT version FROM migraciones 
                WHERE estado = 'aplicada' 
                ORDER BY fecha_aplicacion DESC 
                LIMIT 1
            ");
            $result = $stmt->fetchColumn();
            return $result ?: '1.0.0';
        } catch (Exception $e) {
            return '1.0.0';
        }
    }
    
    /**
     * Ejecutar migraciones hasta una versión específica
     */
    public function migrate($targetVersion = null) {
        if ($targetVersion === null) {
            $targetVersion = $this->getLatestVersion();
        }
        
        $this->targetVersion = $targetVersion;
        
        echo "=== Iniciando Migración ===\n";
        echo "Versión actual: {$this->currentVersion}\n";
        echo "Versión objetivo: {$this->targetVersion}\n\n";
        
        if (version_compare($this->currentVersion, $this->targetVersion, '>=')) {
            echo "La base de datos ya está en la versión {$this->currentVersion} o superior.\n";
            return true;
        }
        
        $migrationsToRun = $this->getMigrationsToRun();
        
        if (empty($migrationsToRun)) {
            echo "No hay migraciones para ejecutar.\n";
            return true;
        }
        
        echo "Migraciones a ejecutar: " . count($migrationsToRun) . "\n\n";
        
        $this->pdo->beginTransaction();
        
        try {
            foreach ($migrationsToRun as $version => $migration) {
                $this->runMigration($version, $migration);
            }
            
            $this->pdo->commit();
            echo "\n✅ Todas las migraciones se ejecutaron exitosamente!\n";
            return true;
            
        } catch (Exception $e) {
            $this->pdo->rollBack();
            echo "\n❌ Error durante la migración: " . $e->getMessage() . "\n";
            echo "Se revirtieron todos los cambios.\n";
            return false;
        }
    }
    
    /**
     * Ejecutar una migración específica
     */
    private function runMigration($version, $migration) {
        echo "Ejecutando migración {$version}: {$migration['description']}\n";
        
        foreach ($migration['up'] as $sql) {
            try {
                $this->pdo->exec($sql);
                echo "  ✓ SQL ejecutado exitosamente\n";
            } catch (Exception $e) {
                throw new Exception("Error en migración {$version}: " . $e->getMessage());
            }
        }
        
        // Registrar migración como aplicada
        $stmt = $this->pdo->prepare("
            INSERT INTO migraciones (version, descripcion, estado) 
            VALUES (?, ?, 'aplicada')
            ON DUPLICATE KEY UPDATE 
            estado = 'aplicada', 
            fecha_aplicacion = CURRENT_TIMESTAMP
        ");
        $stmt->execute([$version, $migration['description']]);
        
        echo "  ✅ Migración {$version} completada\n\n";
    }
    
    /**
     * Revertir una migración específica
     */
    public function rollback($version) {
        if (!isset($this->migrations[$version])) {
            echo "❌ Migración {$version} no encontrada.\n";
            return false;
        }
        
        echo "=== Revirtiendo Migración {$version} ===\n";
        
        $migration = $this->migrations[$version];
        
        $this->pdo->beginTransaction();
        
        try {
            foreach ($migration['down'] as $sql) {
                $this->pdo->exec($sql);
                echo "  ✓ SQL de reversión ejecutado\n";
            }
            
            // Marcar migración como revertida
            $stmt = $this->pdo->prepare("
                UPDATE migraciones 
                SET estado = 'revertida', fecha_aplicacion = CURRENT_TIMESTAMP 
                WHERE version = ?
            ");
            $stmt->execute([$version]);
            
            $this->pdo->commit();
            echo "✅ Migración {$version} revertida exitosamente!\n";
            return true;
            
        } catch (Exception $e) {
            $this->pdo->rollBack();
            echo "❌ Error revirtiendo migración: " . $e->getMessage() . "\n";
            return false;
        }
    }
    
    /**
     * Obtener migraciones que necesitan ejecutarse
     */
    private function getMigrationsToRun() {
        $toRun = [];
        
        foreach ($this->migrations as $version => $migration) {
            if (version_compare($version, $this->currentVersion, '>') && 
                version_compare($version, $this->targetVersion, '<=')) {
                $toRun[$version] = $migration;
            }
        }
        
        // Ordenar por versión
        uksort($toRun, 'version_compare');
        
        return $toRun;
    }
    
    /**
     * Obtener la última versión disponible
     */
    private function getLatestVersion() {
        $versions = array_keys($this->migrations);
        usort($versions, 'version_compare');
        return end($versions);
    }
    
    /**
     * Mostrar estado de migraciones
     */
    public function status() {
        echo "=== Estado de Migraciones ===\n";
        echo "Versión actual: {$this->currentVersion}\n";
        echo "Última versión disponible: " . $this->getLatestVersion() . "\n\n";
        
        echo "Migraciones disponibles:\n";
        foreach ($this->migrations as $version => $migration) {
            $status = $this->isMigrationApplied($version) ? '✅ Aplicada' : '⏳ Pendiente';
            echo "  {$version}: {$migration['description']} - {$status}\n";
        }
        
        echo "\nMigraciones aplicadas:\n";
        $stmt = $this->pdo->query("
            SELECT version, descripcion, fecha_aplicacion, estado 
            FROM migraciones 
            ORDER BY fecha_aplicacion DESC
        ");
        
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
            $statusIcon = $row['estado'] === 'aplicada' ? '✅' : '❌';
            echo "  {$statusIcon} {$row['version']}: {$row['descripcion']} ({$row['fecha_aplicacion']})\n";
        }
    }
    
    /**
     * Verificar si una migración está aplicada
     */
    private function isMigrationApplied($version) {
        $stmt = $this->pdo->prepare("
            SELECT COUNT(*) FROM migraciones 
            WHERE version = ? AND estado = 'aplicada'
        ");
        $stmt->execute([$version]);
        return $stmt->fetchColumn() > 0;
    }
    
    /**
     * Crear backup de la base de datos antes de migrar
     */
    public function createBackup() {
        $backupDir = __DIR__ . '/backups';
        if (!is_dir($backupDir)) {
            mkdir($backupDir, 0755, true);
        }
        
        $timestamp = date('Y-m-d_H-i-s');
        $backupFile = "{$backupDir}/backup_v{$this->currentVersion}_{$timestamp}.sql";
        
        // Aquí se implementaría la lógica de backup usando mysqldump
        // Por simplicidad, solo creamos un archivo de marcador
        file_put_contents($backupFile, "-- Backup creado el " . date('Y-m-d H:i:s') . "\n");
        
        echo "Backup creado: {$backupFile}\n";
        return $backupFile;
    }
}

// Ejecutar si se llama directamente
if (basename(__FILE__) == basename($_SERVER['SCRIPT_NAME'])) {
    $migration = new DatabaseMigration();
    
    $action = $argv[1] ?? 'status';
    
    switch ($action) {
        case 'migrate':
            $version = $argv[2] ?? null;
            $migration->migrate($version);
            break;
            
        case 'rollback':
            $version = $argv[2] ?? null;
            if ($version) {
                $migration->rollback($version);
            } else {
                echo "❌ Debe especificar una versión para revertir.\n";
                echo "Uso: php migrations.php rollback 1.0.1\n";
            }
            break;
            
        case 'status':
        default:
            $migration->status();
            break;
    }
}
?>