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

/**
 * Endpoint handler for fetching plugin and theme vulnerabilities
 */
function wpsec_vulnerabilities_endpoint($request) {
    // Get installed plugins and themes
    $installed_plugins = wpsec_get_installed_plugins();
    $installed_themes = wpsec_get_installed_themes();
    
    // Get WordPress version
    $wp_version = get_bloginfo('version');
    
    // Check for cached data (hourly cache to avoid hammering the API)
    $cache_key = 'wpsec_vulnerability_report_' . md5($wp_version . serialize(array_column($installed_plugins, 'slug')) . serialize(array_column($installed_themes, 'slug')));
    $cached_report = get_transient($cache_key);
    
    if ($cached_report !== false) {
        return rest_ensure_response($cached_report);
    }
    
    // Fetch vulnerabilities
    $plugin_vulnerabilities = wpsec_fetch_plugin_vulnerabilities($installed_plugins);
    $theme_vulnerabilities = wpsec_fetch_theme_vulnerabilities($installed_themes);
    $wp_vulnerabilities = wpsec_fetch_wordpress_vulnerabilities($wp_version);
    
    // Count vulnerable items
    $vulnerable_plugins = array_filter($plugin_vulnerabilities, function($item) {
        return !empty($item['vulnerabilities']);
    });
    
    $vulnerable_themes = array_filter($theme_vulnerabilities, function($item) {
        return !empty($item['vulnerabilities']);
    });
    
    // Build severity counts
    $severity_counts = [
        'critical' => 0,
        'high' => 0,
        'medium' => 0,
        'low' => 0,
        'update_recommended' => 0
    ];
    
    // Count plugin vulnerabilities by severity
    foreach ($vulnerable_plugins as $plugin) {
        $severity_counts['update_recommended']++;
        foreach ($plugin['vulnerabilities'] as $vuln) {
            if (isset($vuln['cvss']['severity'])) {
                $severity = strtolower($vuln['cvss']['severity']);
                if (isset($severity_counts[$severity])) {
                    $severity_counts[$severity]++;
                }
            }
        }
    }
    
    // Count theme vulnerabilities by severity
    foreach ($vulnerable_themes as $theme) {
        $severity_counts['update_recommended']++;
        foreach ($theme['vulnerabilities'] as $vuln) {
            if (isset($vuln['cvss']['severity'])) {
                $severity = strtolower($vuln['cvss']['severity']);
                if (isset($severity_counts[$severity])) {
                    $severity_counts[$severity]++;
                }
            }
        }
    }
    
    // Build the complete report
    $report = [
        'generated_at' => current_time('mysql'),
        'wordpress' => [
            'version' => $wp_version,
            'vulnerabilities' => $wp_vulnerabilities
        ],
        'plugins' => [
            'total' => count($installed_plugins),
            'vulnerable' => count($vulnerable_plugins),
            'items' => $plugin_vulnerabilities
        ],
        'themes' => [
            'total' => count($installed_themes),
            'vulnerable' => count($vulnerable_themes),
            'items' => $theme_vulnerabilities
        ],
        'summary' => $severity_counts
    ];
    
    // Cache the report for 1 hour
    set_transient($cache_key, $report, HOUR_IN_SECONDS);
    
    return rest_ensure_response($report);
}

/**
 * Get installed WordPress plugins
 */
function wpsec_get_installed_plugins() {
    if (!function_exists('get_plugins')) {
        require_once ABSPATH . 'wp-admin/includes/plugin.php';
    }
    
    $plugins = [];
    $installed_plugins = get_plugins();
    
    foreach ($installed_plugins as $path => $plugin_data) {
        $slug = explode('/', $path)[0];
        
        $plugins[] = [
            'slug' => $slug,
            'name' => $plugin_data['Name'],
            'version' => $plugin_data['Version'],
            'active' => is_plugin_active($path),
            'author' => $plugin_data['Author'],
            'path' => $path
        ];
    }
    
    return $plugins;
}

/**
 * Get installed WordPress themes
 */
function wpsec_get_installed_themes() {
    $themes = [];
    $installed_themes = wp_get_themes();
    $active_theme = wp_get_theme();
    
    foreach ($installed_themes as $slug => $theme) {
        $themes[] = [
            'slug' => $slug,
            'name' => $theme->get('Name'),
            'version' => $theme->get('Version'),
            'active' => ($active_theme->get_stylesheet() === $slug),
            'author' => $theme->get('Author')
        ];
    }
    
    return $themes;
}

/**
 * Fetch plugin vulnerabilities from wpvulnerability.net API
 */
function wpsec_fetch_plugin_vulnerabilities($plugins) {
    $results = [];
    
    foreach ($plugins as $plugin) {
        $slug = $plugin['slug'];
        $version = $plugin['version'];
        
        // Initialize result with plugin info
        $result = [
            'slug' => $slug,
            'name' => $plugin['name'],
            'version' => $version,
            'active' => $plugin['active'],
            'vulnerabilities' => [],
            'update_available' => false,
            'recommendation' => 'No known vulnerabilities'
        ];
        
        // Call the API
        $api_url = "https://www.wpvulnerability.net/plugin/{$slug}/";
        $response = wp_remote_get($api_url, ['timeout' => 15]);
        
        if (is_wp_error($response)) {
            $result['api_error'] = $response->get_error_message();
            $results[] = $result;
            continue;
        }
        
        $status_code = wp_remote_retrieve_response_code($response);
        if ($status_code !== 200) {
            $result['api_error'] = "API returned status code: {$status_code}";
            $results[] = $result;
            continue;
        }
        
        $api_data = json_decode(wp_remote_retrieve_body($response), true);
        
        // Check if the API found data
        if (isset($api_data['error']) && $api_data['error'] !== 0) {
            $result['api_error'] = $api_data['message'] ?? 'Unknown API error';
            $results[] = $result;
            continue;
        }
        
        // Process vulnerability data 
        if (isset($api_data['data']['vulnerability']) && is_array($api_data['data']['vulnerability'])) {
            $has_vulnerabilities = false;
            
            foreach ($api_data['data']['vulnerability'] as $vuln) {
                // Check if current version is affected
                if (isset($vuln['operator']) && 
                    wpsec_is_version_affected($version, $vuln['operator'])) {
                    
                    // Get severity from CVSS if available
                    $severity = 'medium'; // Default
                    if (isset($vuln['impact']['cvss']['severity'])) {
                        $severity = $vuln['impact']['cvss']['severity'];
                    }
                    
                    // Build a cleaned vulnerability entry
                    $vulnerability = [
                        'name' => $vuln['name'] ?? 'Unknown vulnerability',
                        'description' => $vuln['description'] ?? '',
                        'affected_versions' => wpsec_get_affected_version_info($vuln['operator']),
                        'fixed_in' => wpsec_get_fixed_version($vuln['operator']),
                        'references' => [],
                        'severity' => $severity
                    ];
                    
                    // Add sources and references
                    if (isset($vuln['source']) && is_array($vuln['source'])) {
                        foreach ($vuln['source'] as $source) {
                            $vulnerability['references'][] = [
                                'name' => $source['name'] ?? '',
                                'link' => $source['link'] ?? '',
                                'description' => $source['description'] ?? ''
                            ];
                        }
                    }
                    
                    // Add impact data if available
                    if (isset($vuln['impact']['cvss'])) {
                        $vulnerability['cvss'] = $vuln['impact']['cvss'];
                    }
                    
                    // Add CWE info if available
                    if (isset($vuln['impact']['cwe']) && is_array($vuln['impact']['cwe'])) {
                        $vulnerability['cwe'] = $vuln['impact']['cwe'];
                    }
                    
                    $result['vulnerabilities'][] = $vulnerability;
                    $has_vulnerabilities = true;
                }
            }
            
            // Set recommendation based on vulnerabilities
            if ($has_vulnerabilities) {
                $result['recommendation'] = 'Update immediately to fix vulnerabilities';
            }
            
            // Check if an update is available using WordPress's update data
            // This properly checks ALL plugins, not just vulnerable ones
            $update_data = get_site_transient('update_plugins');
            if ($update_data && isset($update_data->response) && !empty($update_data->response)) {
                // Loop through all available plugin updates
                foreach ($update_data->response as $plugin_path => $update_info) {
                    // Check if this is the plugin we're currently processing
                    if (strpos($plugin_path, $slug . '/') === 0 || $plugin_path === $slug . '.php') {
                        $result['update_available'] = true;
                        $result['latest_version'] = $update_info->new_version;
                        
                        // If no vulnerabilities but update available, set appropriate recommendation
                        if (!$has_vulnerabilities) {
                            $result['recommendation'] = 'Update available to version ' . $update_info->new_version;
                        }
                        
                        break;
                    }
                }
            }
        }
        
        $results[] = $result;
    }
    
    return $results;
}

/**
 * Fetch theme vulnerabilities from wpvulnerability.net API
 */
function wpsec_fetch_theme_vulnerabilities($themes) {
    $results = [];
    
    foreach ($themes as $theme) {
        $slug = $theme['slug'];
        $version = $theme['version'];
        
        $result = [
            'slug' => $slug,
            'name' => $theme['name'],
            'version' => $version,
            'active' => $theme['active'],
            'vulnerabilities' => [],
            'update_available' => false,
            'recommendation' => 'No known vulnerabilities'
        ];
        
        // Call the API
        $api_url = "https://www.wpvulnerability.net/theme/{$slug}/";
        $response = wp_remote_get($api_url, ['timeout' => 15]);
        
        if (is_wp_error($response)) {
            $result['api_error'] = $response->get_error_message();
            $results[] = $result;
            continue;
        }
        
        $status_code = wp_remote_retrieve_response_code($response);
        if ($status_code !== 200) {
            $result['api_error'] = "API returned status code: {$status_code}";
            $results[] = $result;
            continue;
        }
        
        $api_data = json_decode(wp_remote_retrieve_body($response), true);
        
        // Check if the API found data
        if (isset($api_data['error']) && $api_data['error'] !== 0) {
            $result['api_error'] = $api_data['message'] ?? 'Unknown API error';
            $results[] = $result;
            continue;
        }
        
        // Process vulnerability data (similar to plugin processing)
        if (isset($api_data['data']['vulnerability']) && is_array($api_data['data']['vulnerability'])) {
            $has_vulnerabilities = false;
            
            foreach ($api_data['data']['vulnerability'] as $vuln) {
                if (isset($vuln['operator']) && 
                    wpsec_is_version_affected($version, $vuln['operator'])) {
                    
                    $severity = 'medium'; // Default
                    if (isset($vuln['impact']['cvss']['severity'])) {
                        $severity = $vuln['impact']['cvss']['severity'];
                    }
                    
                    $vulnerability = [
                        'name' => $vuln['name'] ?? 'Unknown vulnerability',
                        'description' => $vuln['description'] ?? '',
                        'affected_versions' => wpsec_get_affected_version_info($vuln['operator']),
                        'fixed_in' => wpsec_get_fixed_version($vuln['operator']),
                        'references' => [],
                        'severity' => $severity
                    ];
                    
                    if (isset($vuln['source']) && is_array($vuln['source'])) {
                        foreach ($vuln['source'] as $source) {
                            $vulnerability['references'][] = [
                                'name' => $source['name'] ?? '',
                                'link' => $source['link'] ?? '',
                                'description' => $source['description'] ?? ''
                            ];
                        }
                    }
                    
                    if (isset($vuln['impact']['cvss'])) {
                        $vulnerability['cvss'] = $vuln['impact']['cvss'];
                    }
                    
                    if (isset($vuln['impact']['cwe']) && is_array($vuln['impact']['cwe'])) {
                        $vulnerability['cwe'] = $vuln['impact']['cwe'];
                    }
                    
                    $result['vulnerabilities'][] = $vulnerability;
                    $has_vulnerabilities = true;
                }
            }
            
            // Set recommendation based on vulnerabilities
            if ($has_vulnerabilities) {
                $result['recommendation'] = 'Update immediately to fix vulnerabilities';
            }
            
            // Check if an update is available using WordPress's update data
            // This properly checks ALL themes, not just vulnerable ones
            $update_data = get_site_transient('update_themes');
            if ($update_data && isset($update_data->response) && !empty($update_data->response)) {
                if (isset($update_data->response[$slug])) {
                    $update_info = $update_data->response[$slug];
                    $result['update_available'] = true;
                    $result['latest_version'] = $update_info['new_version'];
                    
                    // If no vulnerabilities but update available, set appropriate recommendation
                    if (!$has_vulnerabilities) {
                        $result['recommendation'] = 'Update available to version ' . $update_info['new_version'];
                    }
                }
            }
        }
        
        $results[] = $result;
    }
    
    return $results;
}

/**
 * Fetch WordPress core vulnerabilities
 */
function wpsec_fetch_wordpress_vulnerabilities($version) {
    $vulnerabilities = [];
    
    // Call the API
    $api_url = "https://www.wpvulnerability.net/wordpress";
    $response = wp_remote_get($api_url, ['timeout' => 15]);
    
    if (is_wp_error($response)) {
        return ['api_error' => $response->get_error_message()];
    }
    
    $status_code = wp_remote_retrieve_response_code($response);
    if ($status_code !== 200) {
        return ['api_error' => "API returned status code: {$status_code}"];
    }
    
    $api_data = json_decode(wp_remote_retrieve_body($response), true);
    
    // Check if the API found data
    if (isset($api_data['error']) && $api_data['error'] !== 0) {
        return ['api_error' => $api_data['message'] ?? 'Unknown API error'];
    }
    
    // Format similar to plugin/theme responses
    $result = [
        'current_version' => $version,
        'vulnerabilities' => [],
        'update_recommended' => false,
        'latest_version' => $version
    ];
    
    // Process vulnerability data
    if (isset($api_data['data']['vulnerability']) && is_array($api_data['data']['vulnerability'])) {
        foreach ($api_data['data']['vulnerability'] as $vuln) {
            if (isset($vuln['operator']) && 
                wpsec_is_version_affected($version, $vuln['operator'])) {
                
                $severity = 'medium'; // Default
                if (isset($vuln['impact']['cvss']['severity'])) {
                    $severity = $vuln['impact']['cvss']['severity'];
                }
                
                $vulnerability = [
                    'name' => $vuln['name'] ?? 'Unknown vulnerability',
                    'description' => $vuln['description'] ?? '',
                    'affected_versions' => wpsec_get_affected_version_info($vuln['operator']),
                    'fixed_in' => wpsec_get_fixed_version($vuln['operator']),
                    'references' => [],
                    'severity' => $severity
                ];
                
                if (isset($vuln['source']) && is_array($vuln['source'])) {
                    foreach ($vuln['source'] as $source) {
                        $vulnerability['references'][] = [
                            'name' => $source['name'] ?? '',
                            'link' => $source['link'] ?? '',
                            'description' => $source['description'] ?? ''
                        ];
                    }
                }
                
                if (isset($vuln['impact']['cvss'])) {
                    $vulnerability['cvss'] = $vuln['impact']['cvss'];
                }
                
                if (isset($vuln['impact']['cwe']) && is_array($vuln['impact']['cwe'])) {
                    $vulnerability['cwe'] = $vuln['impact']['cwe'];
                }
                
                $result['vulnerabilities'][] = $vulnerability;
                $result['update_recommended'] = true;
            }
        }
    }
    
    // Check if updates are available
    if (isset($api_data['data']['latest']) && 
        version_compare($api_data['data']['latest'], $version, '>')) {
        $result['latest_version'] = $api_data['data']['latest'];
        $result['update_recommended'] = true;
    }
    
    return $result;
}

/**
 * Check if version is affected by vulnerability based on operator
 */
function wpsec_is_version_affected($current_version, $operator) {
    // Check minimum version constraint if set
    if (!empty($operator['min_version']) && !empty($operator['min_operator'])) {
        $min_version = $operator['min_version'];
        $min_operator = $operator['min_operator'];
        
        if ($min_operator === 'ge' && version_compare($current_version, $min_version, '<')) {
            return false; // Current version is less than minimum required
        } elseif ($min_operator === 'gt' && version_compare($current_version, $min_version, '<=')) {
            return false; // Current version is less than or equal to minimum required
        }
    }
    
    // Check maximum version constraint if set
    if (!empty($operator['max_version']) && !empty($operator['max_operator'])) {
        $max_version = $operator['max_version'];
        $max_operator = $operator['max_operator'];
        
        if ($max_operator === 'lt' && version_compare($current_version, $max_version, '>=')) {
            return false; // Current version is greater than or equal to maximum allowed
        } elseif ($max_operator === 'le' && version_compare($current_version, $max_version, '>')) {
            return false; // Current version is greater than maximum allowed
        }
    }
    
    // If we got here, the version is affected
    return true;
}

/**
 * Get human-readable affected version information
 */
function wpsec_get_affected_version_info($operator) {
    $version_info = '';
    
    if (!empty($operator['min_version']) && !empty($operator['min_operator'])) {
        $version_info .= $operator['min_operator'] === 'ge' ? '>= ' : '> ';
        $version_info .= $operator['min_version'];
    }
    
    if (!empty($operator['min_version']) && !empty($operator['max_version'])) {
        $version_info .= ' and ';
    }
    
    if (!empty($operator['max_version']) && !empty($operator['max_operator'])) {
        $version_info .= $operator['max_operator'] === 'lt' ? '< ' : '<= ';
        $version_info .= $operator['max_version'];
    }
    
    return $version_info;
}

/**
 * Get the version where the vulnerability was fixed
 */
function wpsec_get_fixed_version($operator) {
    if ($operator['max_operator'] === 'lt' && !empty($operator['max_version'])) {
        return $operator['max_version'];
    }
    
    return null;
}