<?php
/**
 * CLI Backup Command
 * 
 * Creates a complete WordPress site backup (.lc-migrator) and uploads it to GCS via pre-signed URL
 *
 * @package Lcmigrator\LcMigrator\Cli
 * @since 1.0.0
 */

namespace Lcmigrator\LcMigrator\Cli;

use Exception;
use WP_CLI;
use WP_CLI_Command;
use Lcmigrator\LcMigrator\Logger\Logger;
use Lcmigrator\LcMigrator\Migrator\BackupInProgress\BackupInProgress;
use Lcmigrator\LcMigrator\FileUpload\GCP;

if (!defined('WP_CLI') || !WP_CLI) {
    return;
}

/**
 * Class BackupCli
 * 
 * Handles CLI backup operations with GCS upload
 */
class BackupCli extends WP_CLI_Command
{
    /**
     * Logger instance
     *
     * @var Logger
     */
    private $logger;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->logger = Logger::getInstance();
    }

    /**
     * Creates a complete site backup (.lc-migrator format) and uploads to GCS
     *
     * ## OPTIONS
     *
     * [--snapshot-id=<id>]
     * : Snapshot ID for constructing GCS URL file path
     *
     * [--location-id=<id>]
     * : Location ID for constructing GCS URL file path
     *
     * [--wp-id=<id>]
     * : WordPress ID for constructing GCS URL file path
     *
     * [--gcs-url=<url>]
     * : Pre-signed GCS upload URL (alternative to snapshot/location/wp IDs)
     *
     * [--gcs-url-file=<path>]
     * : Path to file containing the GCS URL (alternative to snapshot/location/wp IDs)
     *
     * ## EXAMPLES
     *
     *     # Backup using snapshot, location, and WP IDs (recommended)
     *     $ wp lcm backup --snapshot-id=MPsiZ5UL4iqmf2qjpzJC --location-id=boJHIx6YSjcoGsky53rk --wp-id=195924
     *
     *     # Basic backup with direct GCS upload URL
     *     $ wp lcm backup --gcs-url="https://storage.googleapis.com/bucket/backup.lc-migrator?X-Goog-Signature=..."
     *
     *     # Backup using custom URL file
     *     $ wp lcm backup --gcs-url-file=/path/to/gcs-url.txt
     *
     * @param array $args Positional arguments
     * @param array $assoc_args Associative arguments
     */
    public function __invoke($args, $assoc_args)
    {
        $startTime = microtime(true);

        try {
            // 1. Validate and parse arguments
            WP_CLI::log('Initializing backup process...');
            
            // Get IDs for constructing file path
            $snapshotId = $assoc_args['snapshot-id'] ?? $assoc_args['snapshot_id'] ?? null;
            $locationId = $assoc_args['location-id'] ?? $assoc_args['location_id'] ?? null;
            $wpId = $assoc_args['wp-id'] ?? $assoc_args['wp_id'] ?? null;
            
            $gcsUrl = null;
            
            // Priority 1: If all IDs provided, construct file path
            if ($snapshotId && $locationId && $wpId) {
                $urlFilePath = WP_CONTENT_DIR . "/uploads/lc-migrator/upload/{$snapshotId}-{$locationId}-{$wpId}.txt";
                WP_CLI::log("Looking for GCS URL file: {$urlFilePath}");
                
                if (file_exists($urlFilePath)) {
                    $gcsUrl = trim(file_get_contents($urlFilePath));
                    WP_CLI::log("Using GCS URL from file: {$urlFilePath}");
                } else {
                    WP_CLI::error("GCS URL file not found: {$urlFilePath}");
                }
            }
            
            // Priority 2: Direct URL argument
            if (!$gcsUrl) {
            $gcsUrl = $assoc_args['gcs-url'] ?? $assoc_args['gcs_url'] ?? null;
            }
            
            // Priority 3: Positional argument
            if (!$gcsUrl && !empty($args)) {
                $gcsUrl = $args[0];
            }
            
            // Priority 4: Environment variable
            if (!$gcsUrl && getenv('GCS_UPLOAD_URL')) {
                $gcsUrl = getenv('GCS_UPLOAD_URL');
                WP_CLI::log('Using GCS URL from GCS_UPLOAD_URL environment variable');
            }
            
            // Priority 5: Custom file path
            if (!$gcsUrl && isset($assoc_args['gcs-url-file'])) {
                $urlFile = $assoc_args['gcs-url-file'];
                if (file_exists($urlFile)) {
                    $gcsUrl = trim(file_get_contents($urlFile));
                    WP_CLI::log("Using GCS URL from file: {$urlFile}");
                } else {
                    WP_CLI::error("URL file not found: {$urlFile}");
                }
            }
            
            // Priority 6: Default file locations (fallback)
            if (!$gcsUrl) {
                $default_locations = [
                    WP_CONTENT_DIR . '/uploads/lc-migrator/upload/',  // Check upload folder for any .txt file
                    WP_CONTENT_DIR . '/lc-migrator-config/gcs-url.txt',
                    WP_CONTENT_DIR . '/uploads/lc-migrator/gcs-url.txt',
                    sys_get_temp_dir() . '/gcs-url.txt',
                ];
                
                foreach ($default_locations as $file_path) {
                    if (file_exists($file_path)) {
                        $gcsUrl = trim(file_get_contents($file_path));
                        if (!empty($gcsUrl)) {
                            WP_CLI::log("Using GCS URL from file: {$file_path}");
                            break;
                        }
                    }
                }
            }
            
            if (!$gcsUrl) {
                WP_CLI::error('GCS URL not found. Provide --snapshot-id, --location-id, and --wp-id, or use --gcs-url or --gcs-url-file.');
            }
            
            // Decode URL if it was encoded
            $gcsUrl = urldecode($gcsUrl);
            
            // Log the URL (truncated for security)
            WP_CLI::log('GCS URL received: ' . substr($gcsUrl, 0, 100) . '...');

            $this->_validateGcsUrl($gcsUrl);

            // Use sensible defaults - prefer IDs if provided
            if ($snapshotId && $locationId && $wpId) {
                $backupId = "{$snapshotId}-{$locationId}-{$wpId}";
            } else {
            $backupId = 'cli_backup_' . date('Y-m-d_H-i-s');
            }
            $contentType = 'application/octet-stream'; // .lc-migrator files use octet-stream

            WP_CLI::log("Backup ID: {$backupId}");
            if ($snapshotId) WP_CLI::log("Snapshot ID: {$snapshotId}");
            if ($locationId) WP_CLI::log("Location ID: {$locationId}");
            if ($wpId) WP_CLI::log("WP ID: {$wpId}");

            // 2. Setup directories
            WP_CLI::log('Setting up backup directories...');
            $dirs = $this->_setupDirectories();
            if (!$dirs['success']) {
                WP_CLI::error('Failed to setup directories: ' . $dirs['error']);
            }

            // 3. Verify system requirements
            WP_CLI::log('Verifying system requirements...');
            $requirements = $this->_verifySystemRequirements($dirs['backup_dir'], $dirs['temp_dir']);
            if (!$requirements['success']) {
                WP_CLI::error('System requirements check failed: ' . $requirements['error']);
            }

            // 4. Create BackupInProgress instance
            $backupInProgress = new BackupInProgress($backupId);

            // 5. Export site using filter pipeline (.lc-migrator format)
            WP_CLI::log('');
            WP_CLI::log('Starting site export (.lc-migrator format)...');
            WP_CLI::log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');

            $exportResult = $this->_exportSiteWithFilters($backupId, $backupInProgress);

            if (!$exportResult['success']) {
                WP_CLI::error('Export failed: ' . $exportResult['error']);
            }

            $backupPath = $exportResult['backup_path'];
            $backupSize = filesize($backupPath);
            $backupSizeFormatted = $this->_formatFileSize($backupSize);

            WP_CLI::log('');
            WP_CLI::success("✓ Backup created successfully: {$backupPath} ({$backupSizeFormatted})");

            // 6. Upload to GCS
            WP_CLI::log('');
            WP_CLI::log('Uploading to Google Cloud Storage...');
            WP_CLI::log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');

            $uploadResult = $this->_uploadToGcs(
                $gcsUrl,
                $backupPath,
                $contentType,
                $backupId,
                $backupInProgress
            );

            if (!$uploadResult['success']) {
                // Don't delete local backup if upload fails
                WP_CLI::error('Upload failed: ' . ($uploadResult['message'] ?? 'Unknown error'));
            }

            WP_CLI::log('');
            WP_CLI::success('✓ Backup uploaded successfully to GCS!');

            // 7. Cleanup - remove local backup and storage folder after successful upload
            WP_CLI::log('Cleaning up local files...');
            
            // Remove the backup file
            if (file_exists($backupPath)) {
                unlink($backupPath);
                WP_CLI::log('✓ Local backup file removed');
            }
            
            // Clean up storage folder (CSVs, temp files) since we skipped Lcm_Export_Clean
            if (isset($exportResult['storage_path']) && is_dir($exportResult['storage_path'])) {
                try {
                    // Use WordPress function to delete directory recursively
                    if (function_exists('lcm_delete_directory')) {
                        lcm_delete_directory($exportResult['storage_path']);
                    } elseif (class_exists('\Lcm_Directory')) {
                        \Lcm_Directory::delete($exportResult['storage_path']);
                    } else {
                        // Fallback: use PHP's RecursiveDirectoryIterator
                        $this->_deleteDirectory($exportResult['storage_path']);
                    }
                    WP_CLI::log('✓ Storage folder cleaned up');
                } catch (\Exception $e) {
                    WP_CLI::warning('Could not clean up storage folder: ' . $e->getMessage());
                }
            }

            // 8. Summary
            $totalTime = microtime(true) - $startTime;
            WP_CLI::log('');
            WP_CLI::log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
            WP_CLI::success('Backup completed successfully!');
            WP_CLI::log("Total time: " . $this->_formatTime($totalTime));
            WP_CLI::log("Backup size: {$backupSizeFormatted}");
            if (isset($uploadResult['avg_speed'])) {
                WP_CLI::log("Upload speed: " . round($uploadResult['avg_speed'], 2) . " MB/s");
            }

        } catch (Exception $e) {
            WP_CLI::error('Backup failed: ' . $e->getMessage());
        }
    }

    /**
     * Validate GCS pre-signed URL
     *
     * @param string $url The GCS URL to validate
     * @return void
     * @throws Exception If URL is invalid
     */
    private function _validateGcsUrl($url)
    {
        if (empty($url)) {
            throw new Exception('GCS URL cannot be empty');
        }

        // Check if it's a valid URL
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
            throw new Exception('Invalid URL format');
        }

        // Check if it's a GCS URL
        if (strpos($url, 'storage.googleapis.com') === false) {
            WP_CLI::warning('URL does not appear to be a Google Cloud Storage URL');
        }

        // Check for required query parameters (basic check)
        if (strpos($url, 'X-Goog-Signature') === false && strpos($url, 'signature') === false) {
            WP_CLI::warning('URL may not be a valid pre-signed URL (missing signature parameter)');
        }

        $this->logger->info("Validated GCS URL: " . substr($url, 0, 100) . "...");
    }

    /**
     * Setup backup and temp directories
     *
     * @return array Success status and directory paths or error
     */
    private function _setupDirectories()
    {
        try {
            $upload_dir = wp_upload_dir();
            if (isset($upload_dir['error']) && $upload_dir['error']) {
                throw new Exception('WordPress uploads directory is not writable: ' . $upload_dir['error']);
            }

            $backup_dir = $upload_dir['basedir'] . '/wp-site-backups/';
            $temp_dir = $upload_dir['basedir'] . '/wp-site-temp/';

            // Create directories if they don't exist
            if (!file_exists($backup_dir)) {
                wp_mkdir_p($backup_dir);
            }
            if (!file_exists($temp_dir)) {
                wp_mkdir_p($temp_dir);
            }

            // Set proper permissions
            @chmod($backup_dir, 0755);
            @chmod($temp_dir, 0755);

            // Verify directories are writable
            if (!is_writable($backup_dir)) {
                throw new Exception("Backup directory is not writable: {$backup_dir}");
            }
            if (!is_writable($temp_dir)) {
                throw new Exception("Temporary directory is not writable: {$temp_dir}");
            }

            return [
                'success' => true,
                'backup_dir' => $backup_dir,
                'temp_dir' => $temp_dir
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Verify system requirements
     *
     * @param string $backup_dir Backup directory path
     * @param string $temp_dir Temp directory path
     * @return array Success status and message or error
     */
    private function _verifySystemRequirements($backup_dir, $temp_dir)
    {
        try {
            // Check PHP version
            if (version_compare(PHP_VERSION, '7.4.0', '<')) {
                throw new Exception("PHP version 7.4.0 or higher is required. Current: " . PHP_VERSION);
            }

            // Check WordPress version
            global $wp_version;
            if (version_compare($wp_version, '5.0.0', '<')) {
                throw new Exception("WordPress version 5.0.0 or higher is required. Current: {$wp_version}");
            }

            // Check required PHP extensions (zip not required for .lc-migrator format)
            $required_extensions = ['mysqli', 'json', 'curl'];
            foreach ($required_extensions as $ext) {
                if (!extension_loaded($ext)) {
                    throw new Exception("Required PHP extension '{$ext}' is not loaded");
                }
            }

            // Check disk space (at least 1GB free recommended)
            $free_space = disk_free_space($backup_dir);
            if ($free_space < 1073741824) { // 1GB
                WP_CLI::warning('Low disk space detected. Backup may fail for large sites.');
            }

            return [
                'success' => true,
                'message' => 'System requirements verified'
            ];
        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Export site using the filter pipeline to create .lc-migrator file
     *
     * @param string $siteId Site ID for tracking
     * @param BackupInProgress $backupInProgress Backup progress tracker
     * @return array Success status and backup path or error
     */
    private function _exportSiteWithFilters($siteId, $backupInProgress)
    {
        global $lcm_params;

        try {
            // Setup environment for export
            $this->_setupExportEnvironment();

            // Get storage folder ID - lcm_storage_path() will build the full path from this ID
            $storageId = lc_migrator_storage_folder();

            // Initialize export params
            // IMPORTANT: 'storage' should be just the ID, not full path
            // lcm_storage_path($params) will construct: LC_MIGRATOR_STORAGE_PATH / $storageId
            $params = [
                'archive'   => lc_migrator_archive_file(),  // Generates .lc-migrator filename
                'storage'   => $storageId,                  // Just the ID, NOT full path!
                'site_id'   => $siteId,
                'priority'  => 5,
                'cli_mode'  => true,  // Flag to indicate CLI execution
                'options'   => [],    // Export options (empty = export everything)
            ];

            $lcm_params = $params;

            // Get the actual full storage path for logging
            $fullStoragePath = lcm_storage_path($params);

            WP_CLI::log("Archive file: {$params['archive']}");
            WP_CLI::log("Storage ID: {$params['storage']}");
            WP_CLI::log("Storage path: {$fullStoragePath}");

            // Get all export filters
            $filters = $this->_getExportFilters('lcm_export');

            if (empty($filters)) {
                throw new Exception('No export filters registered. Make sure the plugin is properly initialized.');
            }

            // Define filters to skip for CLI (we handle upload and cleanup separately)
            $skipFilters = [
                'Lcm_Export_Generate_Cloud_Url::execute',  // We finalize archive ourselves
                'Lcm_Export_Upload_To_Cloud::execute',     // We upload ourselves
                'Lcm_Export_Clean::execute',               // CRITICAL: This deletes the storage folder including archive!
                'Lcm_Start_Server_Import::execute',        // Not needed for CLI
            ];

            // Execute filters in priority order
            foreach ($filters as $priority => $hooks) {
                foreach ($hooks as $hook) {
                    $functionName = $this->_getFunctionName($hook['function']);
                    
                    // Skip cloud upload and server import filters (handled separately for CLI)
                    if (in_array($functionName, $skipFilters)) {
                        WP_CLI::log("Skipping filter: {$functionName} (handled separately for CLI)");
                        continue;
                    }

                    WP_CLI::log("Executing: {$functionName} (priority: {$priority})");

                    try {
                        // Check if backup was stopped
                        if ($backupInProgress && $backupInProgress->isForceClosed()) {
                return [
                    'success' => false,
                                'error' => 'Backup was stopped by user'
                            ];
                        }

                        // Execute the filter
                        $params = call_user_func_array($hook['function'], [$params]);

                        // Debug: Show enumerate results
                        if (strpos($functionName, 'Enumerate') !== false) {
                            $storagePath = lcm_storage_path($params);
                            // List files use .csv extension (from config.php)
                            $listFiles = glob($storagePath . '/*.csv');
                            foreach ($listFiles as $path) {
                                $name = basename($path);
                                $size = filesize($path);
                                $lines = count(file($path));
                                WP_CLI::log("  → {$name}: {$size} bytes, {$lines} entries");
                            }
                            // Show counts from params
                            if (isset($params['total_content_files_count'])) {
                                WP_CLI::log("  → total_content_files_count: " . $params['total_content_files_count']);
                            }
                            if (isset($params['total_media_files_count'])) {
                                WP_CLI::log("  → total_media_files_count: " . $params['total_media_files_count']);
                            }
                        }

                        // Debug: Show export results (check archive size after each export)
                        if (strpos($functionName, 'Lcm_Export_') !== false && 
                            strpos($functionName, 'Enumerate') === false &&
                            strpos($functionName, 'Clean') === false) {
                            $archivePath = lcm_archive_path($params);
                            if (file_exists($archivePath)) {
                                $archiveSize = filesize($archivePath);
                                WP_CLI::log("  → Archive size after {$functionName}: " . number_format($archiveSize) . " bytes");
                            }
                        }

                        // Handle completion flag for chunked operations
                        $completed = isset($params['completed']) ? (bool) $params['completed'] : true;
                        
                        // If not completed, re-run the same filter (chunked processing)
                        while (!$completed) {
                            WP_CLI::log("  Continuing {$functionName}...");
                            $params = call_user_func_array($hook['function'], [$params]);
                            $completed = isset($params['completed']) ? (bool) $params['completed'] : true;
            }

                    } catch (\Lcm_Database_Exception $e) {
                        throw new Exception("Database error in {$functionName}: " . $e->getMessage());
                    }
                }
            }

            // Finalize the archive (append EOF block)
            WP_CLI::log("Finalizing archive...");
            $archive = new \Lcm_Compressor(lcm_archive_path($params));
            $archive->close(true);

            // Get the final backup path
            $backupPath = lcm_archive_path($params);
            
            if (!file_exists($backupPath)) {
                throw new Exception("Backup file was not created: {$backupPath}");
            }

            WP_CLI::log("Archive created: {$backupPath}");

            return [
                'success' => true,
                'backup_path' => $backupPath,
                'storage_path' => lcm_storage_path($params)  // For cleanup
            ];

        } catch (Exception $e) {
            $this->logger->error("Export failed: " . $e->getMessage());
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Setup environment for export process
     *
     * @return void
     */
    private function _setupExportEnvironment()
    {
        @ignore_user_abort(true);
        @set_time_limit(0);
        @ini_set('max_input_time', '-1');
        @ini_set('pcre.backtrack_limit', PHP_INT_MAX);
        
        if (@function_exists('mb_internal_encoding') && (@ini_get('mbstring.func_overload') & 2)) {
            @mb_internal_encoding('ISO-8859-1');
        }
    }

    /**
     * Get export filters from WordPress
     *
     * @param string $tag Filter tag name
     * @return array Filters sorted by priority
     */
    private function _getExportFilters($tag)
    {
        global $wp_filter;
        
        $filters = [];
        if (isset($wp_filter[$tag])) {
            $filters = $wp_filter[$tag];
            if (isset($filters->callbacks)) {
                $filters = $filters->callbacks;
            }
            ksort($filters);
        }
        
        return $filters;
    }

    /**
     * Extract function name from callback for logging
     *
     * @param mixed $function Callback function
     * @return string Function name
     */
    private function _getFunctionName($function)
    {
        if (is_string($function)) {
            return $function;
        }
        
        if (is_array($function)) {
            $class = is_object($function[0]) ? get_class($function[0]) : $function[0];
            return $class . '::' . $function[1];
        }
        
        if (is_object($function) && $function instanceof \Closure) {
            return 'Closure';
        }
        
        return 'Unknown';
    }

    /**
     * Upload backup file to GCS
     *
     * @param string $signedUrl Pre-signed GCS URL
     * @param string $filePath Local backup file path
     * @param string $contentType Content type
     * @param string $backupId Backup ID
     * @param BackupInProgress $backupInProgress Progress tracker
     * @return array Upload result
     */
    private function _uploadToGcs($signedUrl, $filePath, $contentType, $backupId, $backupInProgress)
    {
        try {
            $fileSize = filesize($filePath);
            $fileSizeFormatted = $this->_formatFileSize($fileSize);

            WP_CLI::log("File size: {$fileSizeFormatted}");
            WP_CLI::log("Initializing upload...");

            // Create GCP upload instance
            $gcp = new GCP();

            // Track last progress for display
            $lastProgress = 0;
            $uploadStartTime = time();

            // Progress callback
            $progressCallback = function($progress) use (&$lastProgress, &$uploadStartTime, $fileSize) {
                $currentProgress = round($progress, 1);
                
                // Only log if progress changed significantly (every 5%)
                if ($currentProgress >= $lastProgress + 5 || $currentProgress >= 100) {
                    $elapsed = time() - $uploadStartTime;
                    $uploadedBytes = ($fileSize * $progress) / 100;
                    $speed = $elapsed > 0 ? ($uploadedBytes / $elapsed) / (1024 * 1024) : 0; // MB/s
                    $remainingBytes = $fileSize - $uploadedBytes;
                    $eta = $speed > 0 ? $remainingBytes / ($speed * 1024 * 1024) : 0;
                    
                    WP_CLI::log(sprintf(
                        "Progress: %s%% | Speed: %.2f MB/s | ETA: %s",
                        $currentProgress,
                        $speed,
                        $this->_formatTime($eta)
                    ));
                    
                    $lastProgress = $currentProgress;
                }
            };

            // Perform upload
            $result = $gcp->uploadToSignedUrl(
                $signedUrl,
                $filePath,
                $contentType,
                $backupId,
                $progressCallback
            );

            if (isset($result['error']) && $result['error']) {
                $errorMessage = $result['message'] ?? 'Upload failed';
                WP_CLI::warning("Upload error: {$errorMessage}");
                WP_CLI::log("Note: The backup file was created successfully and is available locally.");
                WP_CLI::log("To upload, you need a valid pre-signed GCS URL with resumable upload support.");
                return [
                    'success' => false,
                    'message' => $errorMessage
                ];
            }

            if (isset($result['status']) && $result['status'] === 'backup_stopped') {
                return [
                    'success' => false,
                    'message' => 'Upload was stopped'
                ];
            }

            return [
                'success' => true,
                'result' => $result,
                'avg_speed' => $result['avg_speed'] ?? null,
                'total_time' => $result['total_time'] ?? null
            ];

        } catch (Exception $e) {
            $this->logger->error("GCS upload failed: " . $e->getMessage());
            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }

    /**
     * Format file size in human-readable format
     *
     * @param int $bytes File size in bytes
     * @return string Formatted size
     */
    private function _formatFileSize($bytes)
    {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        $bytes /= (1 << (10 * $pow));
        return round($bytes, 2) . ' ' . $units[$pow];
    }

    /**
     * Format time in human-readable format
     *
     * @param float $seconds Time in seconds
     * @return string Formatted time
     */
    private function _formatTime($seconds)
    {
        $seconds = (int)$seconds;
        $hours = floor($seconds / 3600);
        $minutes = floor(($seconds % 3600) / 60);
        $seconds = $seconds % 60;

        if ($hours > 0) {
            return sprintf('%d:%02d:%02d', $hours, $minutes, $seconds);
        } else {
            return sprintf('%d:%02d', $minutes, $seconds);
        }
    }

    /**
     * Recursively delete a directory (fallback method)
     *
     * @param string $dir Directory path to delete
     * @return bool True on success
     */
    private function _deleteDirectory($dir)
    {
        if (!is_dir($dir)) {
            return false;
        }

        $files = array_diff(scandir($dir), ['.', '..']);
        
        foreach ($files as $file) {
            $path = $dir . DIRECTORY_SEPARATOR . $file;
            if (is_dir($path)) {
                $this->_deleteDirectory($path);
            } else {
                @unlink($path);
            }
        }

        return @rmdir($dir);
    }
}

