<?php
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Fix permissions for files and directories
 * 
 * @param WP_REST_Request $request Request object
 * @return WP_REST_Response Response object
 */
function wpfortai_fix_permissions_endpoint($request) {
    // Get parameters
    $path = $request->get_param('path');
    $recursive = $request->get_param('recursive');
    $type = $request->get_param('type'); // 'file' or 'directory'
    $fix_all = $request->get_param('fix_all'); // New parameter to fix all WordPress core permissions
    
    // Initialize response
    $response = array(
        'timestamp' => current_time('mysql'),
        'status' => 'error',
        'message' => '',
        'fixed_items' => array(),
        'failed_items' => array()
    );
    
    // Special handling for fix_all parameter
    if ($fix_all === true || $fix_all === 'true' || $fix_all === '1') {
        return fix_all_wordpress_permissions();
    }
    
    // Validate path for normal operation
    if (empty($path)) {
        $response['message'] = 'Path parameter is required unless fix_all=true';
        return new WP_REST_Response($response, 400);
    }
    
    // Security check for paths
    $wp_root = ABSPATH;
    $absolute_path = '';
    $server_path_pattern = '/^\/var\/www\/[\w\.-]+\/htdocs/'; // Common server path pattern
    
    // Handle different path scenarios
    if (preg_match($server_path_pattern, $path)) {
        // This is a server absolute path from the core integrity check
        $absolute_path = $path;
        
        // Special case for /var/www/sub2.test-wpworld.uk/htdocs - exact path from the integrity check
        if ($path === '/var/www/sub2.test-wpworld.uk/htdocs') {
            // Accept this specific path directly
            $is_valid_wp_path = true;
        }
        else {
            // For security, verify it's a common WordPress directory or file
            $wp_patterns = array(
                '/htdocs$/', // WordPress root directory
                '/htdocs\/public_html$/', // Alt WordPress root
                '/public_html$/', // Another common root dir
                '/wp-admin$/',
                '/wp-admin\//',
                '/wp-includes$/',
                '/wp-includes\//',
                '/wp-content$/',
                '/wp-content\//',
                '/index\.php$/',
                '/wp-config\.php$/',
                '\/\.htaccess$/',
            );
            
            $is_valid_wp_path = false;
            foreach ($wp_patterns as $pattern) {
                if (preg_match($pattern, $path)) {
                    $is_valid_wp_path = true;
                    break;
                }
            }
        }
        
        if (!$is_valid_wp_path) {
            $response['message'] = 'Invalid WordPress path. Must be a recognized WordPress directory or file.';
            return new WP_REST_Response($response, 400);
        }
    } 
    // Check if path is already absolute and within WordPress directory
    else if (strpos($path, $wp_root) === 0) {
        $absolute_path = $path;
    } 
    // Handle relative paths
    else {
        // Path may be relative to WordPress root
        $normalized_path = ltrim(str_replace('\\', '/', $path), '/');
        $absolute_path = $wp_root . $normalized_path;
    }
    
    // Final validation
    if (!file_exists($absolute_path)) {
        $response['message'] = 'Path does not exist: ' . $absolute_path;
        return new WP_REST_Response($response, 404);
    }
    
    // Initialize WordPress filesystem
    if (!function_exists('WP_Filesystem')) {
        require_once(ABSPATH . 'wp-admin/includes/file.php');
    }
    
    $wp_filesystem = null;
    $filesystem_method = get_filesystem_method();
    
    // Initialize with credentials
    $creds = request_filesystem_credentials('', $filesystem_method);
    if (false === $creds || !WP_Filesystem($creds)) {
        $response['message'] = 'Unable to access the filesystem. Please check file permissions.';
        return new WP_REST_Response($response, 500);
    }
    
    global $wp_filesystem;
    
    // Recommended permissions
    $recommended_perms = array(
        'directory' => array(
            'wp-admin' => 0755,
            'wp-includes' => 0755,
            'wp-content' => 0755,
            'wp-content/plugins' => 0755,
            'wp-content/themes' => 0755,
            'wp-content/uploads' => 0755,
            'default' => 0755,
        ),
        'file' => array(
            'wp-config.php' => 0440,
            '.htaccess' => 0440,
            'index.php' => 0644,
            'default' => 0644,
        )
    );
    
    // Special case for uploads directory
    if (strpos($path, 'wp-content/uploads') === 0 || $path === 'wp-content/uploads') {
        $recommended_perms['directory']['wp-content/uploads'] = 0755;
    }
    
    // Fix permissions based on path type
    $results = array(
        'success' => array(),
        'failed' => array()
    );
    
    if (is_file($absolute_path)) {
        // Process single file
        $path_type = 'file';
        process_file_permission($absolute_path, $recommended_perms, $results, $type, $wp_filesystem);
    } elseif (is_dir($absolute_path)) {
        // Process directory
        $path_type = 'directory';
        process_dir_permission($absolute_path, $recommended_perms, $results, $type, $recursive, $wp_filesystem);
    } else {
        $response['message'] = 'Path does not exist or is neither a file nor a directory';
        return new WP_REST_Response($response, 404);
    }
    
    // Count actual changes made
    $actual_changes = 0;
    foreach ($results['success'] as $item) {
        if (isset($item['changed']) && $item['changed'] === true) {
            $actual_changes++;
        }
    }
    
    // Prepare response
    if (!empty($results['failed'])) {
        $response['status'] = count($results['success']) > 0 ? 'partial' : 'error';
        $response['message'] = 'Some permissions could not be fixed';
    } else if (!empty($results['success'])) {
        $response['status'] = 'success';
        if ($actual_changes > 0) {
            $response['message'] = 'Permissions fixed successfully (' . $actual_changes . ' changes made)';
        } else {
            $response['message'] = 'All permissions verified - already set correctly';
        }
    } else {
        $response['message'] = 'No permissions were modified';
    }
    
    $response['fixed_items'] = $results['success'];
    $response['failed_items'] = $results['failed'];
    
    return new WP_REST_Response($response, 200);
}

/**
 * Process directory permissions
 *
 * @param string $dir_path Directory path
 * @param array $recommended_perms Recommended permissions
 * @param array &$results Results array to populate
 * @param string $type Type passed from request
 * @param bool $recursive Process recursively
 * @param WP_Filesystem_Base $wp_filesystem WordPress filesystem object
 */
function process_dir_permission($dir_path, $recommended_perms, &$results, $type, $recursive, $wp_filesystem) {
    // Skip if we requested to process only files
    if ($type === 'file') {
        return;
    }
    
    // Get relative path for permission lookup
    $relative_path = str_replace(ABSPATH, '', $dir_path);
    $relative_path = str_replace('\\', '/', $relative_path);
    $relative_path = rtrim($relative_path, '/');
    
    // Determine recommended permissions
    $perms = isset($recommended_perms['directory'][$relative_path]) ? 
        $recommended_perms['directory'][$relative_path] : 
        $recommended_perms['directory']['default'];
    
    // Get current permissions before changing
    $old_perms = substr(sprintf('%o', fileperms($dir_path)), -4);
    $target_perms = decoct($perms);
    
    // Normalize permissions by removing leading zeros for comparison
    $normalized_old = ltrim($old_perms, '0');
    $normalized_target = ltrim($target_perms, '0');
    
    // Only change if current permissions are actually different
    if ($normalized_old !== $normalized_target) {
        // Fix directory permissions
        $chmod_result = $wp_filesystem->chmod($dir_path, $perms);
        
        if ($chmod_result) {
            $results['success'][] = array(
                'path' => $relative_path,
                'type' => 'directory',
                'old_perms' => $old_perms,
                'new_perms' => $target_perms,
                'changed' => true
            );
        } else {
            $results['failed'][] = array(
                'path' => $relative_path,
                'type' => 'directory',
                'current_perms' => $old_perms,
                'target_perms' => $target_perms,
                'error' => 'Failed to change permissions'
            );
        }
    } else {
        // Permissions already correct
        $results['success'][] = array(
            'path' => $relative_path,
            'type' => 'directory',
            'old_perms' => $old_perms,
            'new_perms' => $target_perms,
            'changed' => false,
            'message' => 'Permissions already correct'
        );
    }
    
    // Process recursively if requested
    if ($recursive) {
        $items = new DirectoryIterator($dir_path);
        foreach ($items as $item) {
            if ($item->isDot()) {
                continue;
            }
            
            $item_path = $item->getPathname();
            
            if ($item->isDir()) {
                process_dir_permission($item_path, $recommended_perms, $results, $type, $recursive, $wp_filesystem);
            } elseif ($item->isFile() && $type !== 'directory') {
                process_file_permission($item_path, $recommended_perms, $results, $type, $wp_filesystem);
            }
        }
    }
}

/**
 * Process file permissions
 *
 * @param string $file_path File path
 * @param array $recommended_perms Recommended permissions
 * @param array &$results Results array to populate
 * @param string $type Type passed from request
 * @param WP_Filesystem_Base $wp_filesystem WordPress filesystem object
 */
function process_file_permission($file_path, $recommended_perms, &$results, $type, $wp_filesystem) {
    // Skip if we requested to process only directories
    if ($type === 'directory') {
        return;
    }
    
    // Get file name and path info for permission lookup
    $relative_path = str_replace(ABSPATH, '', $file_path);
    $relative_path = str_replace('\\', '/', $relative_path);
    $file_name = basename($file_path);
    
    // Special handling for wp-config.php in parent directory
    if ($file_name === 'wp-config.php' && dirname($file_path) === dirname(ABSPATH)) {
        $perms = $recommended_perms['file']['wp-config.php'];
    } else {
        // Determine recommended permissions
        $perms = isset($recommended_perms['file'][$file_name]) ? 
            $recommended_perms['file'][$file_name] : 
            $recommended_perms['file']['default'];
    }
    
    // Get current permissions before changing
    $old_perms = substr(sprintf('%o', fileperms($file_path)), -4);
    $target_perms = decoct($perms);
    
    // Normalize permissions by removing leading zeros for comparison
    $normalized_old = ltrim($old_perms, '0');
    $normalized_target = ltrim($target_perms, '0');
    
    // Only change if current permissions are actually different
    if ($normalized_old !== $normalized_target) {
        // Fix file permissions
        $chmod_result = $wp_filesystem->chmod($file_path, $perms);
        
        if ($chmod_result) {
            $results['success'][] = array(
                'path' => $relative_path,
                'type' => 'file',
                'old_perms' => $old_perms,
                'new_perms' => $target_perms,
                'changed' => true
            );
        } else {
            $results['failed'][] = array(
                'path' => $relative_path,
                'type' => 'file',
                'current_perms' => $old_perms,
                'target_perms' => $target_perms,
                'error' => 'Failed to change permissions'
            );
        }
    } else {
        // Permissions already correct
        $results['success'][] = array(
            'path' => $relative_path,
            'type' => 'file',
            'old_perms' => $old_perms,
            'new_perms' => $target_perms,
            'changed' => false,
            'message' => 'Permissions already correct'
        );
    }
}

/**
 * Fix all WordPress core permissions in a single call
 *
 * @return WP_REST_Response Response with results of all permission fixes
 */
function fix_all_wordpress_permissions() {
    // Initialize WordPress filesystem
    if (!function_exists('WP_Filesystem')) {
        require_once(ABSPATH . 'wp-admin/includes/file.php');
    }
    
    $wp_filesystem = null;
    $filesystem_method = get_filesystem_method();
    
    // Initialize with credentials
    $creds = request_filesystem_credentials('', $filesystem_method);
    if (false === $creds || !WP_Filesystem($creds)) {
        return new WP_REST_Response([
            'timestamp' => current_time('mysql'),
            'status' => 'error',
            'message' => 'Unable to access the filesystem. Please check file permissions.',
            'fixed_items' => [],
            'failed_items' => []
        ], 500);
    }
    
    global $wp_filesystem;
    
    // Results container
    $results = [
        'success' => [],
        'failed' => []
    ];
    
    // Define critical WordPress directories and their recommended permissions
    $critical_dirs = [
        '' => 0755,                    // WordPress root
        'wp-admin' => 0755,            // Admin directory
        'wp-includes' => 0755,         // Core includes
        'wp-content' => 0755,          // Content directory
        'wp-content/plugins' => 0755,  // Plugins directory
        'wp-content/themes' => 0755,   // Themes directory
        'wp-content/uploads' => 0755   // Uploads directory (needs to be writable)
    ];
    
    // Define critical WordPress files and their recommended permissions
    $critical_files = [
        'index.php' => 0644,           // Main index file
        '.htaccess' => 0644,           // Apache config file
        'wp-config.php' => 0440        // WordPress config file (most sensitive)
    ];
    
    // Process all critical directories
    foreach ($critical_dirs as $dir => $perms) {
        $dir_path = ABSPATH . $dir;
        if (is_dir($dir_path)) {
            // Fix the directory itself
            process_dir_permission($dir_path, ['directory' => $critical_dirs], $results, 'both', false, $wp_filesystem);
            
            // Recursively process wp-content subdirectories
            if ($dir === 'wp-content/plugins' || $dir === 'wp-content/themes' || $dir === 'wp-content/uploads') {
                // Create a proper permissions array for subdirectories
                $subdirectory_perms = [
                    'directory' => [
                        'default' => 0755 // Default directory permission
                    ],
                    'file' => [
                        'default' => 0644 // Default file permission
                    ]
                ];
                
                $items = new DirectoryIterator($dir_path);
                foreach ($items as $item) {
                    if ($item->isDot()) {
                        continue;
                    }
                    
                    $item_path = $item->getPathname();
                    
                    if ($item->isDir()) {
                        process_dir_permission($item_path, $subdirectory_perms, $results, 'both', true, $wp_filesystem);
                    }
                }
            }
        }
    }
    
    // Process all critical files
    foreach ($critical_files as $file => $perms) {
        $file_path = ABSPATH . $file;
        
        // Special handling for wp-config.php which might be in parent directory
        if ($file === 'wp-config.php' && !file_exists($file_path)) {
            $parent_config = dirname(ABSPATH) . '/wp-config.php';
            if (file_exists($parent_config)) {
                $file_path = $parent_config;
            }
        }
        
        if (file_exists($file_path)) {
            process_file_permission($file_path, ['file' => $critical_files], $results, 'both', $wp_filesystem);
        }
    }
    
    // Count actual changes made
    $actual_changes = 0;
    foreach ($results['success'] as $item) {
        if (isset($item['changed']) && $item['changed'] === true) {
            $actual_changes++;
        }
    }
    
    // Prepare response
    $response = [
        'timestamp' => current_time('mysql'),
        'fixed_items' => $results['success'],
        'failed_items' => $results['failed']
    ];
    
    if (!empty($results['failed'])) {
        $response['status'] = count($results['success']) > 0 ? 'partial' : 'error';
        $response['message'] = 'Some permissions could not be fixed';
    } else if (!empty($results['success'])) {
        $response['status'] = 'success';
        if ($actual_changes > 0) {
            $response['message'] = 'All WordPress permissions fixed successfully (' . $actual_changes . ' changes made)';
        } else {
            $response['message'] = 'All WordPress permissions verified - already set correctly';
        }
    } else {
        $response['message'] = 'No permissions were modified';
    }
    
    return new WP_REST_Response($response, 200);
}

// Register the endpoint
add_action('rest_api_init', function () {
    register_rest_route('wpsec/v1', '/fix-permissions', array(
        'methods' => 'POST',
        'callback' => 'wpfortai_fix_permissions_endpoint',
        'permission_callback' => 'wpsec_authenticate_request',
        'args' => array(
            'path' => array(
                'required' => false, // Changed to false to allow fix_all parameter
                'type' => 'string',
                'description' => 'Path to the file or directory to fix permissions for'
            ),
            'recursive' => array(
                'required' => false,
                'type' => 'boolean',
                'default' => false,
                'description' => 'Whether to fix permissions recursively for directories'
            ),
            'type' => array(
                'required' => false,
                'type' => 'string',
                'enum' => array('file', 'directory', 'both'),
                'default' => 'both',
                'description' => 'Type of items to fix permissions for'
            ),
            'fix_all' => array(
                'required' => false,
                'type' => 'boolean',
                'default' => false,
                'description' => 'Set to true to fix all WordPress core permissions in a single call'
            )
        )
    ));
});
