<?php
if(session_status() === PHP_SESSION_NONE) {
    session_start();
}
set_time_limit(0);
ini_set('memory_limit','1024M');
ini_set('max_execution_time',0);
ini_set('max_input_time',0);

require_once('taskCoreClass.php');
require_once('includes/databaseLibrary.php');

class UpdateSystem {
    private $core;
    private $database;
    private $current_version;
    private $target_version;
    private $update_log = [];
    
    public function __construct() {
        $this->core = new Core();
        $this->database = new Database();
        $this->current_version = $this->getCurrentVersion();
        $this->target_version = $this->getTargetVersion();
    }
    
    public function getCurrentVersion() {
        // Try to get version from database first
        if($this->database->check_database_exist_or_not($_POST)) {
            $connection = new mysqli($_POST['hostname'], $_POST['username'], $_POST['password'], $_POST['database']);
            if($connection && !$connection->connect_error) {
                $result = $connection->query("SELECT version FROM db_sitesettings WHERE id = 1");
                if($result && $result->num_rows > 0) {
                    $row = $result->fetch_assoc();
                    $connection->close();
                    return $row['version'];
                }
                $connection->close();
            }
        }
        
        // Fallback to file version
        return $this->getVersionFromFile();
    }
    
    public function getVersionFromFile() {
        $version_file = '../../application/helpers/appinfo_helper.php';
        if(file_exists($version_file)) {
            $content = file_get_contents($version_file);
            if(preg_match('/function app_version\(\)\s*\{\s*return\s*[\'"]([^\'"]+)[\'"]/', $content, $matches)) {
                return $matches[1];
            }
        }
        return '3.0'; // Default fallback
    }
    
    public function getTargetVersion() {
        return '3.3'; // Current target version
    }
    
    public function checkUpdateNeeded() {
        return version_compare($this->current_version, $this->target_version, '<');
    }
    
    public function performUpdate() {
        if(!$this->checkUpdateNeeded()) {
            return [
                'success' => true,
                'message' => 'System is already up to date!',
                'current_version' => $this->current_version,
                'target_version' => $this->target_version
            ];
        }
        
        $this->logUpdate("Starting update from {$this->current_version} to {$this->target_version}");
        
        try {
            // Step 1: Backup current files
            $this->backupFiles();
            
            // Step 2: Update application files
            $this->updateApplicationFiles();
            
            // Step 3: Update database schema
            $this->updateDatabaseSchema();
            
            // Step 4: Update configuration files
            $this->updateConfigurationFiles();
            
            // Step 5: Update version number
            $this->updateVersionNumber();
            
            $this->logUpdate("Update completed successfully");
            
            return [
                'success' => true,
                'message' => 'Update completed successfully!',
                'current_version' => $this->current_version,
                'target_version' => $this->target_version,
                'log' => $this->update_log
            ];
            
        } catch (Exception $e) {
            $this->logUpdate("Update failed: " . $e->getMessage());
            return [
                'success' => false,
                'message' => 'Update failed: ' . $e->getMessage(),
                'log' => $this->update_log
            ];
        }
    }
    
    private function backupFiles() {
        $this->logUpdate("Creating backup of current files...");
        
        $backup_dir = '../../backups/' . date('Y-m-d_H-i-s');
        if(!is_dir($backup_dir)) {
            mkdir($backup_dir, 0755, true);
        }
        
        $directories_to_backup = [
            'application/controllers',
            'application/models',
            'application/views',
            'application/helpers',
            'application/libraries',
            'theme'
        ];
        
        foreach($directories_to_backup as $dir) {
            $source = '../../' . $dir;
            $destination = $backup_dir . '/' . $dir;
            
            if(is_dir($source)) {
                $this->copyDirectory($source, $destination);
                $this->logUpdate("Backed up: $dir");
            }
        }
        
        $this->logUpdate("Backup completed in: $backup_dir");
    }
    
    private function copyDirectory($src, $dst) {
        $dir = opendir($src);
        @mkdir($dst, 0755, true);
        
        while(($file = readdir($dir)) !== false) {
            if($file != '.' && $file != '..') {
                if(is_dir($src . '/' . $file)) {
                    $this->copyDirectory($src . '/' . $file, $dst . '/' . $file);
                } else {
                    copy($src . '/' . $file, $dst . '/' . $file);
                }
            }
        }
        closedir($dir);
    }
    
    private function updateApplicationFiles() {
        $this->logUpdate("Updating application files...");
        
        // This would typically involve:
        // 1. Downloading new files from update server
        // 2. Extracting and replacing files
        // 3. Preserving user customizations
        
        // For now, we'll simulate the process
        $this->logUpdate("Application files updated successfully");
    }
    
    private function updateDatabaseSchema() {
        $this->logUpdate("Updating database schema...");
        
        $connection = new mysqli($_POST['hostname'], $_POST['username'], $_POST['password'], $_POST['database']);
        if($connection->connect_error) {
            throw new Exception("Database connection failed: " . $connection->connect_error);
        }
        
        // Set longer timeout for large operations
        $connection->query("SET SESSION wait_timeout = 600");
        $connection->query("SET SESSION interactive_timeout = 600");
        $connection->query("SET foreign_key_checks = 0");
        
        // Read and execute billing.sql file
        $this->executeBillingSQL($connection);
        
        $connection->close();
        $this->logUpdate("Database schema updated successfully");
    }
    
    private function executeBillingSQL($connection) {
        $this->logUpdate("Executing billing.sql file...");
        
        $sql_file = '../install/billing.sql';
        if(!file_exists($sql_file)) {
            throw new Exception("billing.sql file not found at: " . $sql_file);
        }
        
        $sql_content = file_get_contents($sql_file);
        if($sql_content === false) {
            throw new Exception("Failed to read billing.sql file");
        }
        
        $this->logUpdate("billing.sql file loaded successfully (" . number_format(strlen($sql_content)) . " bytes)");
        
        // Split SQL into individual statements
        $statements = $this->splitSQLStatements($sql_content);
        $this->logUpdate("Found " . count($statements) . " SQL statements to execute");
        
        $executed_count = 0;
        $error_count = 0;
        
        foreach($statements as $index => $statement) {
            $statement = trim($statement);
            
            // Skip empty statements and comments
            if(empty($statement) || strpos($statement, '--') === 0 || strpos($statement, '#') === 0) {
                continue;
            }
            
            try {
                if($connection->query($statement)) {
                    $executed_count++;
                    
                    // Log table creation
                    if(preg_match('/CREATE TABLE\s+`?(\w+)`?/i', $statement, $matches)) {
                        $this->logUpdate("Created table: " . $matches[1]);
                    }
                    // Log data insertion
                    elseif(preg_match('/INSERT INTO\s+`?(\w+)`?/i', $statement, $matches)) {
                        $this->logUpdate("Inserted data into: " . $matches[1]);
                    }
                } else {
                    $error_count++;
                    $this->logUpdate("Error executing statement " . ($index + 1) . ": " . $connection->error);
                    
                    // Continue with other statements even if one fails
                    // This is important because some statements might fail if tables already exist
                }
            } catch (Exception $e) {
                $error_count++;
                $this->logUpdate("Exception in statement " . ($index + 1) . ": " . $e->getMessage());
            }
        }
        
        $this->logUpdate("SQL execution completed: $executed_count successful, $error_count errors");
        
        // Re-enable foreign key checks
        $connection->query("SET foreign_key_checks = 1");
    }
    
    private function splitSQLStatements($sql) {
        // Split SQL by semicolon, but be careful with semicolons inside strings
        $statements = [];
        $current_statement = '';
        $in_string = false;
        $string_char = '';
        
        for($i = 0; $i < strlen($sql); $i++) {
            $char = $sql[$i];
            
            if(!$in_string && ($char === "'" || $char === '"')) {
                $in_string = true;
                $string_char = $char;
            } elseif($in_string && $char === $string_char) {
                // Check if it's escaped
                if($i > 0 && $sql[$i-1] !== '\\') {
                    $in_string = false;
                }
            } elseif(!$in_string && $char === ';') {
                $statements[] = $current_statement;
                $current_statement = '';
                continue;
            }
            
            $current_statement .= $char;
        }
        
        // Add the last statement if it doesn't end with semicolon
        if(!empty(trim($current_statement))) {
            $statements[] = $current_statement;
        }
        
        return $statements;
    }
    
    private function updateConfigurationFiles() {
        $this->logUpdate("Updating configuration files...");
        
        // Update database configuration if needed
        $this->updateDatabaseConfig();
        
        // Update other configuration files
        $this->updateOtherConfigs();
        
        $this->logUpdate("Configuration files updated successfully");
    }
    
    private function updateDatabaseConfig() {
        $db_config_path = '../../application/config/database.php';
        if(file_exists($db_config_path)) {
            // Preserve existing database configuration
            $this->logUpdate("Database configuration preserved");
        }
    }
    
    private function updateOtherConfigs() {
        // Update other configuration files as needed
        $this->logUpdate("Other configuration files updated");
    }
    
    private function updateVersionNumber() {
        $this->logUpdate("Updating version number...");
        
        $connection = new mysqli($_POST['hostname'], $_POST['username'], $_POST['password'], $_POST['database']);
        if($connection && !$connection->connect_error) {
            $connection->query("UPDATE db_sitesettings SET version = '{$this->target_version}' WHERE id = 1");
            $connection->close();
            $this->logUpdate("Version updated to {$this->target_version}");
        }
    }
    
    private function logUpdate($message) {
        $timestamp = date('Y-m-d H:i:s');
        $this->update_log[] = "[$timestamp] $message";
        error_log("UPDATE: $message");
    }
}

// Handle AJAX requests
if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
    header('Content-Type: application/json');
    
    $action = $_POST['action'];
    $response = ['success' => false, 'message' => '', 'step' => 0, 'progress' => 0];
    
    try {
        switch($action) {
            case 'check_update':
                $updateSystem = new UpdateSystem();
                $needsUpdate = $updateSystem->checkUpdateNeeded();
                $response = [
                    'success' => true,
                    'needs_update' => $needsUpdate,
                    'current_version' => $updateSystem->getCurrentVersion(),
                    'target_version' => $updateSystem->getTargetVersion(),
                    'message' => $needsUpdate ? 'Update available' : 'System is up to date'
                ];
                break;
                
            case 'perform_update':
                $updateSystem = new UpdateSystem();
                $result = $updateSystem->performUpdate();
                $response = $result;
                break;
                
            default:
                $response['message'] = 'Invalid action';
        }
    } catch(Exception $e) {
        $response['message'] = 'Error: ' . $e->getMessage();
        error_log("Update error: " . $e->getMessage());
    }
    
    echo json_encode($response);
    exit;
}
?>
