<?php
class SystemUpdateManager {
    private $conn;
    private $current_version;
    private $update_server;
    private $temp_dir;
    private $backup_dir;
    private $update_log_file;
    
    public function __construct($conn) {
        $this->conn = $conn;
        $this->loadSettings();
        
        // Create required directories
        $this->temp_dir = dirname(__FILE__) . '/temp/updates/';
        $this->backup_dir = dirname(__FILE__) . '/backups/';
        $this->update_log_file = dirname(__FILE__) . '/logs/update.log';
        
        $this->createRequiredDirs();
    }

    public function getUpdateServer() {
        return $this->update_server;
    }

    public function getCurrentVersion() {
        return $this->current_version;
    }
    
    private function loadSettings() {
        $query = "SELECT setting_key, setting_value FROM settings WHERE setting_key IN ('system_version', 'update_server_url')";
        $result = mysqli_query($this->conn, $query);
        
        while ($row = mysqli_fetch_assoc($result)) {
            if ($row['setting_key'] == 'system_version') {
                $this->current_version = $row['setting_value'];
            } else if ($row['setting_key'] == 'update_server_url') {
                $this->update_server = $row['setting_value'];
            }
        }

        // Set defaults if not found in database
        if (!$this->current_version) {
            $this->current_version = '1.0.0';
        }
        if (!$this->update_server) {
            $this->update_server = 'https://updates.myne.co.ke';
        }
    }
    
    private function createRequiredDirs() {
        foreach ([$this->temp_dir, $this->backup_dir, dirname($this->update_log_file)] as $dir) {
            if (!file_exists($dir)) {
                mkdir($dir, 0755, true);
            }
        }
    }
    
    public function checkForUpdates() {
        try {
            // Initialize checksum manager
            require_once 'ChecksumManager.php';
            $checksumManager = new ChecksumManager($this->temp_dir);
            
            // Check update manifest from server
            $ch = curl_init($this->update_server . '/manifest.json');
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            $response = curl_exec($ch);
            
            if (curl_errno($ch)) {
                throw new Exception('Failed to connect to update server: ' . curl_error($ch));
            }
            
            curl_close($ch);
            
            $manifest = json_decode($response, true);
            if (!$manifest) {
                throw new Exception('Invalid manifest file');
            }
            
            // Compare versions
            return version_compare($manifest['latest_version'], $this->current_version, '>') 
                ? $manifest 
                : false;
                
        } catch (Exception $e) {
            $this->logError('Check for updates failed: ' . $e->getMessage());
            return false;
        }
    }
    
    public function downloadUpdate($version) {
        try {
            $update_file = $this->temp_dir . "update_$version.zip";
            
            // Download update package
            $fp = fopen($update_file, 'w+');
            $ch = curl_init($this->update_server . "/packages/update_$version.zip");
            curl_setopt($ch, CURLOPT_TIMEOUT, 600);
            curl_setopt($ch, CURLOPT_FILE, $fp);
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
            
            $success = curl_exec($ch);
            if (curl_errno($ch)) {
                throw new Exception('Failed to download update: ' . curl_error($ch));
            }
            
            curl_close($ch);
            fclose($fp);
            
            // Verify checksum
            $checksumManager = new ChecksumManager($this->temp_dir);
            if (!$checksumManager->verifyPackage($version, $update_file)) {
                unlink($update_file);
                throw new Exception('Update package checksum verification failed');
            }
            
            return $update_file;
            
        } catch (Exception $e) {
            $this->logError('Download update failed: ' . $e->getMessage());
            return false;
        }
    }
    
    public function applyUpdate($update_file, $version) {
        try {
            // Start transaction for database changes
            mysqli_begin_transaction($this->conn);
            
            // Create backup
            $this->createBackup();
            
            // Extract update package
            $zip = new ZipArchive();
            if ($zip->open($update_file) !== true) {
                throw new Exception('Failed to open update package');
            }
            
            $zip->extractTo($this->temp_dir . 'extracted/');
            $zip->close();
            
            // Apply database migrations
            $this->applyDatabaseMigrations($version);
            
            // Copy new/updated files
            $this->copyUpdateFiles($version);
            
            // Update version in settings
            $query = "UPDATE settings SET setting_value = ? WHERE setting_key = 'system_version'";
            $stmt = mysqli_prepare($this->conn, $query);
            mysqli_stmt_bind_param($stmt, 's', $version);
            mysqli_stmt_execute($stmt);
            
            // Log update
            $this->logUpdate("Successfully updated to version $version");
            
            // Commit database changes
            mysqli_commit($this->conn);
            
            // Cleanup
            $this->cleanup($update_file);
            
            return true;
            
        } catch (Exception $e) {
            // Rollback database changes
            mysqli_rollback($this->conn);
            
            // Restore from backup if needed
            $this->restoreFromBackup();
            
            $this->logError('Update failed: ' . $e->getMessage());
            return false;
        }
    }
    
    private function createBackup() {
        $backup_dir = $this->backup_dir . date('Y-m-d_H-i-s') . '/';
        mkdir($backup_dir);
        
        // Backup files
        $this->recursiveCopy(dirname(__FILE__), $backup_dir);
        
        // Backup database
        $this->backupDatabase($backup_dir . 'database.sql');
    }
    
    private function backupDatabase($output_file) {
        $db_config = parse_ini_file('config/database.ini');
        
        $command = sprintf(
            'mysqldump --host=%s --user=%s --password=%s %s > %s',
            escapeshellarg($db_config['host']),
            escapeshellarg($db_config['username']),
            escapeshellarg($db_config['password']),
            escapeshellarg($db_config['database']),
            escapeshellarg($output_file)
        );
        
        system($command);
    }
    
    private function applyDatabaseMigrations($version) {
        $migrations_dir = $this->temp_dir . "extracted/migrations/";
        
        if (!file_exists($migrations_dir)) {
            return; // No migrations to apply
        }
        
        $migrations = glob($migrations_dir . "*.sql");
        sort($migrations); // Apply in order
        
        foreach ($migrations as $migration) {
            $sql = file_get_contents($migration);
            if (!mysqli_multi_query($this->conn, $sql)) {
                throw new Exception('Migration failed: ' . mysqli_error($this->conn));
            }
            
            // Clear results
            while (mysqli_more_results($this->conn)) {
                mysqli_next_result($this->conn);
            }
        }
    }
    
    private function copyUpdateFiles($version) {
        $source = $this->temp_dir . "extracted/files/";
        $destination = dirname(__FILE__);
        
        if (!file_exists($source)) {
            return; // No files to update
        }
        
        $this->recursiveCopy($source, $destination);
    }
    
    private function recursiveCopy($src, $dst) {
        $dir = opendir($src);
        @mkdir($dst);
        
        while (($file = readdir($dir)) !== false) {
            if ($file != '.' && $file != '..') {
                if (is_dir($src . '/' . $file)) {
                    $this->recursiveCopy($src . '/' . $file, $dst . '/' . $file);
                } else {
                    copy($src . '/' . $file, $dst . '/' . $file);
                }
            }
        }
        
        closedir($dir);
    }
    
    private function restoreFromBackup() {
        // Get latest backup
        $backups = glob($this->backup_dir . '*', GLOB_ONLYDIR);
        if (empty($backups)) {
            return false;
        }
        
        $latest_backup = end($backups);
        
        // Restore files
        $this->recursiveCopy($latest_backup, dirname(__FILE__));
        
        // Restore database
        $db_backup = $latest_backup . '/database.sql';
        if (file_exists($db_backup)) {
            $db_config = parse_ini_file('config/database.ini');
            
            $command = sprintf(
                'mysql --host=%s --user=%s --password=%s %s < %s',
                escapeshellarg($db_config['host']),
                escapeshellarg($db_config['username']),
                escapeshellarg($db_config['password']),
                escapeshellarg($db_config['database']),
                escapeshellarg($db_backup)
            );
            
            system($command);
        }
        
        return true;
    }
    
    private function cleanup($update_file) {
        // Remove temporary files
        unlink($update_file);
        $this->recursiveDelete($this->temp_dir . 'extracted/');
        
        // Remove old backups (keep last 5)
        $backups = glob($this->backup_dir . '*', GLOB_ONLYDIR);
        if (count($backups) > 5) {
            sort($backups);
            $old_backups = array_slice($backups, 0, -5);
            foreach ($old_backups as $backup) {
                $this->recursiveDelete($backup);
            }
        }
    }
    
    private function recursiveDelete($dir) {
        if (is_dir($dir)) {
            $objects = scandir($dir);
            foreach ($objects as $object) {
                if ($object != "." && $object != "..") {
                    if (is_dir($dir . "/" . $object)) {
                        $this->recursiveDelete($dir . "/" . $object);
                    } else {
                        unlink($dir . "/" . $object);
                    }
                }
            }
            rmdir($dir);
        }
    }
    
    private function logUpdate($message) {
        $this->log('UPDATE', $message);
    }
    
    private function logError($message) {
        $this->log('ERROR', $message);
    }
    
    private function log($type, $message) {
        $log_entry = date('Y-m-d H:i:s') . " [$type] $message\n";
        file_put_contents($this->update_log_file, $log_entry, FILE_APPEND);
    }
}
?>