<?php
defined('ABSPATH') || exit;

/**
 * WordPress Core Backup Restore Functions
 *
 * Provides functionality to restore WordPress core files from backups
 * created during core reinstall operations.
 */

/**
 * Get a list of available WordPress core backups
 *
 * @return array List of available backups with metadata
 */
function wpsec_get_core_backups() {
    // Use the standardized backup directory path
    $backups_dir = WP_CONTENT_DIR . '/wpsec-backups/core/';
    $backups = array();
    
    // Check if the backup directory exists
    if (!file_exists($backups_dir)) {
        return $backups;
    }
    
    // Find all zip files in the backup directory
    $files = glob($backups_dir . '*.zip');
    
    // Return empty if no files found
    if (empty($files)) {
        return $backups;
    }
    
    // Sort by file modification time (newest first)
    usort($files, function($a, $b) {
        return filemtime($b) - filemtime($a);
    });
    
    foreach ($files as $file) {
        $filename = basename($file);
        $created_time = filemtime($file);
        
        // Extract date from filename (if available)
        $date_part = '';
        if (preg_match('/backup-(\d{8}-\d{6})\.zip$/', $filename, $matches)) {
            $date_part = $matches[1];
        }
        
        $backup_info = array(
            'id' => md5($filename),
            'filename' => $filename,
            'path' => $file,
            'size' => filesize($file),
            'size_formatted' => size_format(filesize($file), 2),
            'created' => gmdate('Y-m-d H:i:s', $created_time),
            'date_part' => $date_part
        );
        
        $backups[] = $backup_info;
    }
    
    return $backups;
}

/**
 * Restore WordPress core files from a backup
 *
 * @param string $backup_id The ID of the backup to restore from
 * @param array $options Array of restore options:
 *                     - skip_content: Whether to skip restoring wp-content (default: true)
 *                     - skip_config: Whether to skip restoring wp-config.php (default: true)
 * @return array|WP_Error Result of the restore operation or error
 */
function wpsec_restore_core_from_backup($backup_id, $options = array()) {
    // Default options
    $default_options = array(
        'skip_content' => true,
        'skip_config' => true,
    );
    
    $options = array_merge($default_options, $options);
    
    // Generate a unique operation ID for tracking
    $operation_id = uniqid('wpsec_core_restore_', true);
    
    // Initialize status data
    $status = array(
        'id' => $operation_id,
        'started_at' => current_time('mysql'),
        'status' => 'preparing',
        'progress' => 0,
        'log' => array('Core restore operation started'),
        'backup_id' => $backup_id,
        'options' => $options
    );
    
    update_option('wpsec_core_restore_' . $operation_id, $status);
    
    // Find the backup file
    $backups = wpsec_get_core_backups();
    $backup_file = null;
    
    foreach ($backups as $backup) {
        if ($backup['id'] === $backup_id) {
            $backup_file = $backup['path'];
            break;
        }
    }
    
    if (!$backup_file) {
        wpsec_core_restore_log($operation_id, 'Backup not found: ' . $backup_id, 'error');
        wpsec_core_restore_update_status($operation_id, 'failed', 'Backup not found');
        return new WP_Error('backup_not_found', 'Backup not found');
    }
    
    wpsec_core_restore_log($operation_id, 'Found backup file: ' . basename($backup_file));
    
    // Use WordPress file system API
    if (!function_exists('WP_Filesystem')) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
    }
    
    WP_Filesystem();
    global $wp_filesystem;
    
    // Create temporary directory
    $temp_dir = get_temp_dir() . 'wpsec_core_restore_' . $operation_id;
    if (!file_exists($temp_dir)) {
        wp_mkdir_p($temp_dir);
    }
    
    wpsec_core_restore_update_status($operation_id, 'extracting', 'Extracting backup archive', 10);
    
    // Extract backup archive
    if (!class_exists('PclZip')) {
        require_once ABSPATH . 'wp-admin/includes/class-pclzip.php';
    }
    
    $zip = new PclZip($backup_file);
    $result = $zip->extract(PCLZIP_OPT_PATH, $temp_dir);
    
    if ($result === 0) {
        wpsec_core_restore_log($operation_id, 'Failed to extract backup: ' . $zip->errorInfo(true), 'error');
        wpsec_core_restore_update_status($operation_id, 'failed', 'Failed to extract backup');
        return new WP_Error('extract_failed', 'Failed to extract backup: ' . $zip->errorInfo(true));
    }
    
    wpsec_core_restore_log($operation_id, 'Backup extracted successfully');
    wpsec_core_restore_update_status($operation_id, 'restoring', 'Restoring core files', 30);
    
    // Determine files to restore
    $exclude_dirs = $options['skip_content'] ? array('wp-content') : array();
    $exclude_files = $options['skip_config'] ? array('wp-config.php') : array();
    
    $copied_count = 0;
    $failed_count = 0;
    
    wpsec_core_restore_log($operation_id, 'Starting file restore operation');
    
    // Recursive function to copy files
    $copy_files = function($source_dir, $dest_dir, $exclude_dirs, $exclude_files) use (&$copy_files, &$copied_count, &$failed_count, $operation_id, &$wp_filesystem) {
        $items = $wp_filesystem->dirlist($source_dir);
        
        if (!$items) {
            return;
        }
        
        foreach ($items as $item) {
            $source_path = trailingslashit($source_dir) . $item['name'];
            $dest_path = trailingslashit($dest_dir) . $item['name'];
            
            // Skip excluded directories
            if ($item['type'] === 'd' && in_array($item['name'], $exclude_dirs)) {
                wpsec_core_restore_log($operation_id, 'Skipping excluded directory: ' . $item['name']);
                continue;
            }
            
            // Skip excluded files
            if ($item['type'] === 'f' && in_array($item['name'], $exclude_files)) {
                wpsec_core_restore_log($operation_id, 'Skipping excluded file: ' . $item['name']);
                continue;
            }
            
            if ($item['type'] === 'd') {
                // Create directory if it doesn't exist
                if (!$wp_filesystem->exists($dest_path)) {
                    $wp_filesystem->mkdir($dest_path);
                }
                
                // Recursively copy subdirectories
                $copy_files($source_path, $dest_path, $exclude_dirs, $exclude_files);
            } else {
                // Copy file
                if ($wp_filesystem->copy($source_path, $dest_path, true)) {
                    $copied_count++;
                    
                    // Log progress every 100 files
                    if ($copied_count % 100 === 0) {
                        wpsec_core_restore_log($operation_id, 'Restored ' . $copied_count . ' files so far');
                        wpsec_core_restore_update_status(
                            $operation_id, 
                            'restoring', 
                            'Restoring core files', 
                            30 + min(60, intval($copied_count / 30))
                        );
                    }
                } else {
                    $failed_count++;
                    wpsec_core_restore_log($operation_id, 'Failed to restore: ' . $dest_path, 'error');
                }
            }
        }
    };
    
    // Start copying files
    $copy_files($temp_dir, ABSPATH, $exclude_dirs, $exclude_files);
    
    wpsec_core_restore_log($operation_id, 'Restored ' . $copied_count . ' files. Failed: ' . $failed_count);
    
    // Clean up
    wpsec_core_restore_update_status($operation_id, 'cleaning_up', 'Cleaning up temporary files', 90);
    
    // Delete the temporary directory
    if (file_exists($temp_dir)) {
        $wp_filesystem->delete($temp_dir, true);
    }
    
    // Complete the operation
    if ($failed_count === 0) {
        wpsec_core_restore_log($operation_id, 'Core restore operation completed successfully');
        wpsec_core_restore_update_status($operation_id, 'completed', 'WordPress core files have been restored successfully', 100);
        
        return array(
            'operation_id' => $operation_id,
            'status' => 'completed',
            'message' => 'WordPress core files have been restored successfully',
            'restored_files' => $copied_count,
            'failed_files' => $failed_count
        );
    } else {
        wpsec_core_restore_log($operation_id, 'Core restore completed with errors', 'warning');
        wpsec_core_restore_update_status($operation_id, 'completed_with_errors', 'WordPress core files restored with ' . $failed_count . ' errors', 100);
        
        return array(
            'operation_id' => $operation_id,
            'status' => 'completed_with_errors',
            'message' => 'WordPress core files restored with ' . $failed_count . ' errors',
            'restored_files' => $copied_count,
            'failed_files' => $failed_count
        );
    }
}

/**
 * Get the status of a core restore operation
 * 
 * @param string $operation_id The operation ID
 * @return array|WP_Error Operation status or WP_Error if operation not found
 */
function wpsec_get_core_restore_status($operation_id) {
    $metadata = get_option('wpsec_core_restore_' . $operation_id);
    
    if (!$metadata) {
        return new WP_Error(
            'operation_not_found',
            'Core restore operation not found'
        );
    }
    
    return $metadata;
}

/**
 * Log a message for the core restore operation
 * 
 * @param string $operation_id The operation ID
 * @param string $message The message to log
 * @param string $type The type of log message (info, warning, error)
 */
function wpsec_core_restore_log($operation_id, $message, $type = 'info') {
    $metadata = get_option('wpsec_core_restore_' . $operation_id);
    
    if (!$metadata) {
        return;
    }
    
    if (!isset($metadata['log'])) {
        $metadata['log'] = array();
    }
    
    $log_entry = $message;
    if ($type !== 'info') {
        $log_entry = '[' . strtoupper($type) . '] ' . $message;
    }
    
    $metadata['log'][] = $log_entry;
    
    // Prevent log from growing too large
    if (count($metadata['log']) > 100) {
        array_shift($metadata['log']);
    }
    
    update_option('wpsec_core_restore_' . $operation_id, $metadata);
    
    // Also log to WordPress log if it's an error
    if ($type === 'error') {
        wpsec_log('Core restore error: ' . $message, 'error');
    }
}

/**
 * Update the status of the core restore operation
 * 
 * @param string $operation_id The operation ID
 * @param string $status The new status
 * @param string $message A message describing the status
 * @param int $progress The progress percentage (0-100)
 */
function wpsec_core_restore_update_status($operation_id, $status, $message = '', $progress = null) {
    $metadata = get_option('wpsec_core_restore_' . $operation_id);
    
    if (!$metadata) {
        return;
    }
    
    $metadata['status'] = $status;
    $metadata['status_message'] = $message;
    
    if ($status === 'completed' || $status === 'failed' || $status === 'completed_with_errors') {
        $metadata['completed_at'] = current_time('mysql');
    }
    
    if ($progress !== null) {
        $metadata['progress'] = $progress;
    }
    
    update_option('wpsec_core_restore_' . $operation_id, $metadata);
    
    // Log the status change
    wpsec_core_restore_log($operation_id, 'Status changed to: ' . $status . ($message ? ' - ' . $message : ''));
}
