<?php

declare(strict_types=1);

namespace WPUltimateSecurity\UltimateSecurity\Modules\Monitoring\VulnerabilityScanner;

use WPUltimateSecurity\UltimateSecurity\Modules\Base\AbstractModule;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\ApiKeyCryptoService;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\Utils;
use WP_Error;

/**
 * Vulnerability Scanner Module
 *
 * Scans installed plugins and themes against vulnerability databases
 * using WPScan API (primary) and Patchstack API (fallback).
 */
class VulnerabilityScanner extends AbstractModule
{
    public const MODULE_ID = 'monitoring.vulnerability_scanner';

    protected const VERSION = '2.0.0';

    /**
     * Get unique module identifier.
     */
    public static function getId(): string
    {
        return self::MODULE_ID;
    }

    /**
     * Get human-readable module name.
     */
    public static function getName(): string
    {
        return __('Vulnerability Scanner', 'ultimate-security');
    }

    /**
     * Get module domain.
     */
    public static function getDomain(): string
    {
        return 'monitoring';
    }
    /**
     * Option key for storing scan results
     */
    private const RESULTS_OPTION = 'ultimate_security_vulnerability_scan_results';

    /**
     * Option key for storing last scan time
     */
    private const LAST_SCAN_OPTION = 'ultimate_security_vulnerability_last_scan';

    /**
     * Transient key for API rate limiting
     */
    private const RATE_LIMIT_TRANSIENT = 'ultimate_security_vuln_api_rate_limit';

    /**
     * Cache duration for scan results (12 hours)
     */
    private const CACHE_DURATION = 43200;

    /**
     * Cron hook name
     */
    private const CRON_HOOK = 'ultimate_security_vulnerability_scan_cron';

    /**
     * Option key for ignored items
     */
    private const IGNORED_OPTION = 'ultimate_security_ignored_vulnerabilities';

    /**
     * Masked API key value returned to UI.
     */
    private const MASKED_API_KEY = '********';

    /**
     * Boot the module after registration.
     */
    public static function boot(): void
    {
        add_action('rest_api_init', [self::class, 'register_routes']);
        add_action(self::CRON_HOOK, [self::class, 'run_scheduled_scan']);
        add_action('admin_init', [self::class, 'setup_cron_schedule']);
    }

    /**
     * Register REST API routes
     */
    public static function register_routes(): void
    {
        $namespace = 'ultimate-security/v1';

        // Trigger a scan
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/scan',
            [
                'methods' => 'GET',
                'callback' => [self::class, 'api_trigger_scan'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
            ]
        );

        // Get cached results
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/results',
            [
                'methods' => 'GET',
                'callback' => [self::class, 'api_get_results'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
            ]
        );

        // Get settings
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/settings',
            [
                'methods' => 'GET',
                'callback' => [self::class, 'api_get_settings'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
            ]
        );

        // Save settings
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/settings',
            [
                'methods' => 'POST',
                'callback' => [self::class, 'api_save_settings'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
            ]
        );

        // Update single plugin/theme
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/update',
            [
                'methods' => 'POST',
                'callback' => [self::class, 'api_update_item'],
                'permission_callback' => function (\WP_REST_Request $request) {
                    $type = $request->get_param('type');
                    if ($type === 'theme') {
                        return current_user_can('update_themes');
                    }
                    if ($type === 'plugin') {
                        return current_user_can('update_plugins');
                    }
                    return current_user_can('update_plugins');
                },
                'args' => [
                    'type' => [
                        'required' => true,
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                    'slug' => [
                        'required' => true,
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                ],
            ]
        );

        // Get dashboard summary
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/summary',
            [
                'methods' => 'GET',
                'callback' => [self::class, 'api_get_summary'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
            ]
        );

        // Get scan history (Pro only)
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/history',
            [
                'methods' => 'GET',
                'callback' => [self::class, 'api_get_history'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
            ]
        );

        // Get specific scan details from history (Pro only)
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/history/(?P<id>\d+)',
            [
                'methods' => 'GET',
                'callback' => [self::class, 'api_get_history_details'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
                'args' => [
                    'id' => [
                        'required' => true,
                        'validate_callback' => function ($param) {
                            return is_numeric($param);
                        },
                    ],
                ],
            ]
        );

        // Compare two scans (Pro only)
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/compare',
            [
                'methods' => 'POST',
                'callback' => [self::class, 'api_compare_scans'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
                'args' => [
                    'scan_id_1' => [
                        'required' => true,
                        'validate_callback' => function ($param) {
                            return is_numeric($param);
                        },
                    ],
                    'scan_id_2' => [
                        'required' => true,
                        'validate_callback' => function ($param) {
                            return is_numeric($param);
                        },
                    ],
                ],
            ]
        );

        // Ignore a vulnerability or item
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/ignore',
            [
                'methods' => 'POST',
                'callback' => [self::class, 'api_ignore_item'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
                'args' => [
                    'type' => [
                        'required' => true,
                        'sanitize_callback' => 'sanitize_text_field',
                        'enum' => ['vulnerability', 'plugin', 'theme'],
                    ],
                    'id' => [
                        'required' => true,
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                ],
            ]
        );

        // Un-ignore an item
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/ignore',
            [
                'methods' => 'DELETE',
                'callback' => [self::class, 'api_unignore_item'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
                'args' => [
                    'type' => [
                        'required' => true,
                        'sanitize_callback' => 'sanitize_text_field',
                        'enum' => ['vulnerability', 'plugin', 'theme'],
                    ],
                    'id' => [
                        'required' => true,
                        'sanitize_callback' => 'sanitize_text_field',
                    ],
                ],
            ]
        );

        // Get ignored items
        register_rest_route(
            $namespace,
            '/vulnerability-scanner/ignored',
            [
                'methods' => 'GET',
                'callback' => [self::class, 'api_get_ignored'],
                'permission_callback' => function () {
                    return current_user_can('manage_options');
                },
            ]
        );
    }

    /**
     * API: Trigger a vulnerability scan
     *
     * @return \WP_REST_Response
     */
    public static function api_trigger_scan(): \WP_REST_Response
    {
        // Check rate limiting
        $rate_limit = get_transient(self::RATE_LIMIT_TRANSIENT);
        if ($rate_limit) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => __('Please wait before triggering another scan. Rate limit in effect.', 'ultimate-security'),
                'next_scan_available' => $rate_limit,
            ], 429);
        }

        $results = self::run_scan();

        if (is_wp_error($results)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => $results->get_error_message(),
            ], 500);
        }

        return new \WP_REST_Response([
            'success' => true,
            'message' => __('Scan completed successfully.', 'ultimate-security'),
            'data' => $results,
        ], 200);
    }

    /**
     * API: Get cached scan results
     *
     * @return \WP_REST_Response
     */
    public static function api_get_results(): \WP_REST_Response
    {
        $results = get_option(self::RESULTS_OPTION, null);
        $last_scan = get_option(self::LAST_SCAN_OPTION, null);

        if (!$results) {
            return new \WP_REST_Response([
                'success' => true,
                'message' => __('No scan results available. Please run a scan first.', 'ultimate-security'),
                'data' => null,
                'last_scan' => null,
            ], 200);
        }

        return new \WP_REST_Response([
            'success' => true,
            'data' => $results,
            'last_scan' => $last_scan,
        ], 200);
    }

    /**
     * API: Get scanner settings
     *
     * @return \WP_REST_Response
     */
    public static function api_get_settings(): \WP_REST_Response
    {
        $options = Utils::ultimate_security_get_option();
        $settings = $options['vulnerability_scanner'] ?? self::get_default_settings();

        // Convert string settings to boolean for Semi UI Form.Switch components
        $settings['enabled'] = ($settings['enabled'] ?? '0') === '1';
        $settings['email_notifications'] = ($settings['email_notifications'] ?? '0') === '1';

        // Mask API keys in the response to avoid exposing secrets in the UI.
        if (!empty($settings['wpscan_api_key'])) {
            $settings['wpscan_api_key'] = self::MASKED_API_KEY;
        }
        if (!empty($settings['patchstack_api_key'])) {
            $settings['patchstack_api_key'] = self::MASKED_API_KEY;
        }

        return new \WP_REST_Response([
            'success' => true,
            'data' => $settings,
        ], 200);
    }

    /**
     * API: Save scanner settings
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function api_save_settings(\WP_REST_Request $request): \WP_REST_Response
    {
        $data = $request->get_json_params();

        if (empty($data)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => __('No settings data provided.', 'ultimate-security'),
            ], 400);
        }

        // Use raw DB options to avoid re-saving decrypted secrets.
        $options = get_option('ultimate_security_options', []);
        $existing_scanner = isset($options['vulnerability_scanner']) && is_array($options['vulnerability_scanner'])
            ? $options['vulnerability_scanner']
            : [];

        // Sanitize and validate settings
        // Note: Semi UI Form.Switch sends boolean true/false, need to convert to string '1'/'0'
        $incoming_wpscan = array_key_exists('wpscan_api_key', $data) ? sanitize_text_field($data['wpscan_api_key']) : null;
        $incoming_patchstack = array_key_exists('patchstack_api_key', $data) ? sanitize_text_field($data['patchstack_api_key']) : null;

        $sanitized = [
            'enabled' => isset($data['enabled']) ?
                ($data['enabled'] === true || $data['enabled'] === '1' || $data['enabled'] === 1 ? '1' : '0') : '0',
            'wpscan_api_key' => ($incoming_wpscan === self::MASKED_API_KEY || $incoming_wpscan === null)
                ? ($existing_scanner['wpscan_api_key'] ?? '')
                : $incoming_wpscan,
            'patchstack_api_key' => ($incoming_patchstack === self::MASKED_API_KEY || $incoming_patchstack === null)
                ? ($existing_scanner['patchstack_api_key'] ?? '')
                : $incoming_patchstack,
            'scan_schedule' => isset($data['scan_schedule']) ? sanitize_text_field($data['scan_schedule']) : 'daily',
            'email_notifications' => isset($data['email_notifications']) ?
                ($data['email_notifications'] === true || $data['email_notifications'] === '1' || $data['email_notifications'] === 1 ? '1' : '0') : '0',
            'notification_email' => isset($data['notification_email']) ? sanitize_email($data['notification_email']) : get_option('admin_email'),
            'notify_severity' => isset($data['notify_severity']) ? array_map('sanitize_text_field', (array) $data['notify_severity']) : ['critical', 'high'],
            'abandoned_threshold' => isset($data['abandoned_threshold']) ? absint($data['abandoned_threshold']) : 730,
        ];

        $options['vulnerability_scanner'] = $sanitized;

        // Encrypt any plaintext secrets before saving (idempotent).
        $options = ApiKeyCryptoService::encrypt($options);
        Utils::ultimate_security_update_option($options);

        // Reschedule cron if needed
        self::setup_cron_schedule();

        return new \WP_REST_Response([
            'success' => true,
            'message' => __('Settings saved successfully.', 'ultimate-security'),
            'data' => $sanitized,
        ], 200);
    }

    /**
     * API: Update a single plugin or theme
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function api_update_item(\WP_REST_Request $request): \WP_REST_Response
    {
        $type = $request->get_param('type');
        $slug = $request->get_param('slug');

        if (!in_array($type, ['plugin', 'theme'], true)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => __('Invalid type specified.', 'ultimate-security'),
            ], 400);
        }

        require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
        require_once ABSPATH . 'wp-admin/includes/plugin.php';
        require_once ABSPATH . 'wp-admin/includes/theme.php';
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/misc.php';

        $skin = new \Automatic_Upgrader_Skin();

        if ($type === 'plugin') {
            $upgrader = new \Plugin_Upgrader($skin);
            $result = $upgrader->upgrade($slug);
        } else {
            $upgrader = new \Theme_Upgrader($skin);
            $result = $upgrader->upgrade($slug);
        }

        if (is_wp_error($result)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => $result->get_error_message(),
            ], 500);
        }

        if ($result === false) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => __('Update failed. The item may already be up to date.', 'ultimate-security'),
            ], 500);
        }

        return new \WP_REST_Response([
            'success' => true,
            'message' => sprintf(
                // translators: %s is the type (plugin or theme)
                __('%s updated successfully.', 'ultimate-security'),
                ucfirst($type)
            ),
        ], 200);
    }

    /**
     * API: Get dashboard summary
     *
     * @return \WP_REST_Response
     */
    public static function api_get_summary(): \WP_REST_Response
    {
        $results = get_option(self::RESULTS_OPTION, null);
        $last_scan = get_option(self::LAST_SCAN_OPTION, null);

        if (!$results) {
            return new \WP_REST_Response([
                'success' => true,
                'data' => [
                    'total_vulnerabilities' => 0,
                    'critical' => 0,
                    'high' => 0,
                    'medium' => 0,
                    'low' => 0,
                    'abandoned_plugins' => 0,
                    'abandoned_themes' => 0,
                    'last_scan' => null,
                    'needs_scan' => true,
                ],
            ], 200);
        }

        $summary = [
            'total_vulnerabilities' => 0,
            'critical' => 0,
            'high' => 0,
            'medium' => 0,
            'low' => 0,
            'abandoned_plugins' => 0,
            'abandoned_themes' => 0,
            'last_scan' => $last_scan,
            'needs_scan' => false,
        ];

        // Count vulnerabilities
        foreach (['plugins', 'themes', 'wordpress'] as $type) {
            if (!empty($results[$type])) {
                foreach ($results[$type] as $item) {
                    if (!empty($item['vulnerabilities'])) {
                        foreach ($item['vulnerabilities'] as $vuln) {
                            $summary['total_vulnerabilities']++;
                            $severity = strtolower($vuln['severity'] ?? 'medium');
                            if (isset($summary[$severity])) {
                                $summary[$severity]++;
                            }
                        }
                    }
                    if (!empty($item['is_abandoned'])) {
                        if ($type === 'plugins') {
                            $summary['abandoned_plugins']++;
                        } elseif ($type === 'themes') {
                            $summary['abandoned_themes']++;
                        }
                    }
                }
            }
        }

        // Check if scan is stale (older than cache duration)
        if ($last_scan) {
            $last_scan_time = strtotime($last_scan);
            if ((time() - $last_scan_time) > self::CACHE_DURATION) {
                $summary['needs_scan'] = true;
            }
        }

        return new \WP_REST_Response([
            'success' => true,
            'data' => $summary,
        ], 200);
    }

    /**
     * API: Get scan history (Pro only)
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function api_get_history(\WP_REST_Request $request): \WP_REST_Response
    {
        global $wpdb;
        $table_name = $wpdb->prefix . 'ultimate_security_vulnerability_scan_history';

        // Check if table exists
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) !== $table_name) {
            return new \WP_REST_Response([
                'success' => true,
                'data' => [],
                'message' => __('No scan history available yet.', 'ultimate-security'),
            ], 200);
        }

        $limit = $request->get_param('limit') ?? 20;
        $offset = $request->get_param('offset') ?? 0;

        // Get scan history
        $history = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT id, scan_id, scan_type, api_used, total_vulnerabilities, critical_count, high_count,
                        medium_count, low_count, abandoned_plugins, abandoned_themes, outdated_count,
                        plugins_scanned, themes_scanned, wp_version, triggered_by, created_at
                 FROM {$table_name}
                 ORDER BY created_at DESC
                 LIMIT %d OFFSET %d",
                $limit,
                $offset
            ),
            ARRAY_A
        );

        // Get total count
        $total = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name}"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared

        return new \WP_REST_Response([
            'success' => true,
            'data' => $history ?: [],
            'total' => (int) $total,
            'limit' => (int) $limit,
            'offset' => (int) $offset,
        ], 200);
    }

    /**
     * API: Get specific scan details from history
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function api_get_history_details(\WP_REST_Request $request): \WP_REST_Response
    {
        global $wpdb;
        $table_name = $wpdb->prefix . 'ultimate_security_vulnerability_scan_history';
        $id = (int) $request->get_param('id');

        $scan = $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM {$table_name} WHERE id = %d", $id), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            ARRAY_A
        );

        if (!$scan) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => __('Scan not found.', 'ultimate-security'),
            ], 404);
        }

        // Decode the results JSON
        if (!empty($scan['results'])) {
            $scan['results'] = json_decode($scan['results'], true);
        }

        return new \WP_REST_Response([
            'success' => true,
            'data' => $scan,
        ], 200);
    }

    /**
     * API: Compare two scans
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function api_compare_scans(\WP_REST_Request $request): \WP_REST_Response
    {
        global $wpdb;
        $table_name = $wpdb->prefix . 'ultimate_security_vulnerability_scan_history';

        $scan_id_1 = (int) $request->get_param('scan_id_1');
        $scan_id_2 = (int) $request->get_param('scan_id_2');

        $scan1 = $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM {$table_name} WHERE id = %d", $scan_id_1), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            ARRAY_A
        );
        $scan2 = $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM {$table_name} WHERE id = %d", $scan_id_2), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            ARRAY_A
        );

        if (!$scan1 || !$scan2) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => __('One or both scans not found.', 'ultimate-security'),
            ], 404);
        }

        // Decode results
        $results1 = json_decode($scan1['results'] ?? '{}', true);
        $results2 = json_decode($scan2['results'] ?? '{}', true);

        // Compare vulnerabilities
        $comparison = [
            'scan1' => [
                'id' => $scan1['id'],
                'date' => $scan1['created_at'],
                'total' => $scan1['total_vulnerabilities'],
            ],
            'scan2' => [
                'id' => $scan2['id'],
                'date' => $scan2['created_at'],
                'total' => $scan2['total_vulnerabilities'],
            ],
            'new_vulnerabilities' => [],
            'resolved_vulnerabilities' => [],
            'vulnerability_change' => (int) $scan2['total_vulnerabilities'] - (int) $scan1['total_vulnerabilities'],
        ];

        // Extract all vulnerability IDs from each scan
        $vulns1 = self::extract_vulnerability_ids($results1);
        $vulns2 = self::extract_vulnerability_ids($results2);

        $comparison['new_vulnerabilities'] = array_diff($vulns2, $vulns1);
        $comparison['resolved_vulnerabilities'] = array_diff($vulns1, $vulns2);

        return new \WP_REST_Response([
            'success' => true,
            'data' => $comparison,
        ], 200);
    }

    /**
     * Extract vulnerability IDs from scan results
     *
     * @param array $results Scan results
     * @return array Vulnerability IDs
     */
    private static function extract_vulnerability_ids(array $results): array
    {
        $ids = [];
        foreach (['plugins', 'themes', 'wordpress'] as $type) {
            if (!empty($results[$type])) {
                foreach ($results[$type] as $item) {
                    if (!empty($item['vulnerabilities'])) {
                        foreach ($item['vulnerabilities'] as $vuln) {
                            $id = $vuln['id'] ?? $vuln['cve'] ?? md5($vuln['title'] ?? '');
                            $ids[] = $id;
                        }
                    }
                }
            }
        }
        return $ids;
    }

    /**
     * Save scan to history
     *
     * @param array $results Scan results
     * @return int|false Inserted row ID or false on failure
     */
    private static function save_scan_to_history(array $results)
    {
        global $wpdb;
        $table_name = $wpdb->prefix . 'ultimate_security_vulnerability_scan_history';

        // Check if table exists
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) !== $table_name) {
            return false;
        }

        // Calculate stats
        $stats = [
            'total_vulnerabilities' => 0,
            'critical_count' => 0,
            'high_count' => 0,
            'medium_count' => 0,
            'low_count' => 0,
            'abandoned_plugins' => 0,
            'abandoned_themes' => 0,
            'outdated_count' => 0,
            'plugins_scanned' => count($results['plugins'] ?? []),
            'themes_scanned' => count($results['themes'] ?? []),
        ];

        foreach (['plugins', 'themes', 'wordpress'] as $type) {
            if (!empty($results[$type])) {
                foreach ($results[$type] as $item) {
                    if (!empty($item['vulnerabilities'])) {
                        $stats['total_vulnerabilities'] += count($item['vulnerabilities']);
                        foreach ($item['vulnerabilities'] as $vuln) {
                            $severity = strtolower($vuln['severity'] ?? 'medium');
                            if ($severity === 'critical') {
                                $stats['critical_count']++;
                            } elseif ($severity === 'high') {
                                $stats['high_count']++;
                            } elseif ($severity === 'medium') {
                                $stats['medium_count']++;
                            } elseif ($severity === 'low') {
                                $stats['low_count']++;
                            }
                        }
                    }
                    if (!empty($item['is_abandoned'])) {
                        if ($type === 'plugins') {
                            $stats['abandoned_plugins']++;
                        } elseif ($type === 'themes') {
                            $stats['abandoned_themes']++;
                        }
                    }
                    if (!empty($item['update_available'])) {
                        $stats['outdated_count']++;
                    }
                }
            }
        }

        // Insert into history table
        $inserted = $wpdb->insert(
            $table_name,
            [
                'scan_id' => wp_generate_uuid4(),
                'scan_type' => 'manual',
                'api_used' => $results['api_used'] ?? 'unknown',
                'total_vulnerabilities' => $stats['total_vulnerabilities'],
                'critical_count' => $stats['critical_count'],
                'high_count' => $stats['high_count'],
                'medium_count' => $stats['medium_count'],
                'low_count' => $stats['low_count'],
                'abandoned_plugins' => $stats['abandoned_plugins'],
                'abandoned_themes' => $stats['abandoned_themes'],
                'outdated_count' => $stats['outdated_count'],
                'plugins_scanned' => $stats['plugins_scanned'],
                'themes_scanned' => $stats['themes_scanned'],
                'wp_version' => get_bloginfo('version'),
                'results' => wp_json_encode($results),
                'triggered_by' => get_current_user_id(),
                'created_at' => current_time('mysql'),
                'updated_at' => current_time('mysql'),
            ],
            ['%s', '%s', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%d', '%s', '%s']
        );

        return $inserted ? $wpdb->insert_id : false;
    }

    /**
     * Run the vulnerability scan
     *
     * @return array|WP_Error
     */
    public static function run_scan()
    {
        $options = Utils::ultimate_security_get_option();
        $settings = $options['vulnerability_scanner'] ?? self::get_default_settings();

        $results = [
            'plugins' => [],
            'themes' => [],
            'wordpress' => [],
            'scan_time' => current_time('mysql'),
            'api_used' => null,
            'api_errors' => [],
        ];

        // Get installed plugins (use cached version for performance)
        $installed_plugins = Utils::get_plugins_cached();

        // Get installed themes (use cached version for performance)
        $installed_themes = Utils::get_themes_cached();

        // Get WordPress version
        $wp_version = get_bloginfo('version');

        // Try WPScan API first if key is available
        $wpscan_key = $settings['wpscan_api_key'] ?? '';
        $patchstack_key = $settings['patchstack_api_key'] ?? '';

        $provider = null;

        if (!empty($wpscan_key)) {
            $provider = new WPScanProvider($wpscan_key);
            $results['api_used'] = 'wpscan';
        } elseif (!empty($patchstack_key)) {
            $provider = new PatchstackProvider($patchstack_key);
            $results['api_used'] = 'patchstack';
        }

        // Scan WordPress core
        if ($provider) {
            $wp_vulns = $provider->check_wordpress($wp_version);
            if (is_wp_error($wp_vulns)) {
                $results['api_errors'][] = [
                    'type' => 'wordpress',
                    'slug' => 'wordpress-core',
                    'error' => $wp_vulns->get_error_code(),
                    'message' => $wp_vulns->get_error_message(),
                ];
            } elseif (!empty($wp_vulns)) {
                $results['wordpress'][] = [
                    'name' => 'WordPress Core',
                    'version' => $wp_version,
                    'vulnerabilities' => $wp_vulns,
                ];
            }
        }

        // Scan plugins
        foreach ($installed_plugins as $plugin_file => $plugin_data) {
            $slug = self::get_plugin_slug($plugin_file);
            $version = $plugin_data['Version'] ?? '';
            $last_updated = self::get_plugin_last_updated($slug);

            $plugin_result = [
                'slug' => $slug,
                'file' => $plugin_file,
                'name' => $plugin_data['Name'] ?? $slug,
                'version' => $version,
                'author' => $plugin_data['Author'] ?? '',
                'description' => $plugin_data['Description'] ?? '',
                'last_updated' => $last_updated,
                'is_abandoned' => self::is_abandoned($last_updated, $settings['abandoned_threshold'] ?? 730),
                'is_active' => is_plugin_active($plugin_file),
                'update_available' => self::has_update_available('plugin', $plugin_file),
                'vulnerabilities' => [],
            ];

            if ($provider) {
                $vulns = $provider->check_plugin($slug, $version);
                if (is_wp_error($vulns)) {
                    $plugin_result['api_error'] = [
                        'error' => $vulns->get_error_code(),
                        'message' => $vulns->get_error_message(),
                    ];
                    // Track rate limit errors separately for summary
                    if ($vulns->get_error_code() === 'wpscan_rate_limited') {
                        $results['api_errors'][] = [
                            'type' => 'plugin',
                            'slug' => $slug,
                            'error' => $vulns->get_error_code(),
                            'message' => $vulns->get_error_message(),
                        ];
                    }
                } elseif (!empty($vulns)) {
                    $plugin_result['vulnerabilities'] = $vulns;
                }
            }

            $results['plugins'][] = $plugin_result;
        }

        // Scan themes
        foreach ($installed_themes as $theme_slug => $theme) {
            $version = $theme->get('Version');
            $last_updated = self::get_theme_last_updated($theme_slug);

            $theme_result = [
                'slug' => $theme_slug,
                'name' => $theme->get('Name'),
                'version' => $version,
                'author' => $theme->get('Author'),
                'description' => $theme->get('Description'),
                'last_updated' => $last_updated,
                'is_abandoned' => self::is_abandoned($last_updated, $settings['abandoned_threshold'] ?? 730),
                'is_active' => (get_stylesheet() === $theme_slug),
                'update_available' => self::has_update_available('theme', $theme_slug),
                'vulnerabilities' => [],
            ];

            if ($provider) {
                $vulns = $provider->check_theme($theme_slug, $version);
                if (is_wp_error($vulns)) {
                    $theme_result['api_error'] = [
                        'error' => $vulns->get_error_code(),
                        'message' => $vulns->get_error_message(),
                    ];
                    // Track rate limit errors separately for summary
                    if ($vulns->get_error_code() === 'wpscan_rate_limited') {
                        $results['api_errors'][] = [
                            'type' => 'theme',
                            'slug' => $theme_slug,
                            'error' => $vulns->get_error_code(),
                            'message' => $vulns->get_error_message(),
                        ];
                    }
                } elseif (!empty($vulns)) {
                    $theme_result['vulnerabilities'] = $vulns;
                }
            }

            $results['themes'][] = $theme_result;
        }

        // Store results
        update_option(self::RESULTS_OPTION, $results);
        update_option(self::LAST_SCAN_OPTION, current_time('mysql'));

        // Save to history table (Pro only)
        self::save_scan_to_history($results);

        // Log scan completion to Activity Logs (Pro only)
        self::log_scan_event($results);

        // Set rate limit (5 minutes between scans)
        set_transient(self::RATE_LIMIT_TRANSIENT, time() + 300, 300);

        // Send email notification if enabled and vulnerabilities found
        // Handle both string '1' and boolean true
        $email_enabled = $settings['email_notifications'] ?? '0';
        if ($email_enabled === '1' || $email_enabled === true || $email_enabled === 1) {
            self::maybe_send_notification($results, $settings);
        }

        return $results;
    }

    /**
     * Run scheduled scan via cron
     */
    public static function run_scheduled_scan(): void
    {
        $options = Utils::ultimate_security_get_option();
        $settings = $options['vulnerability_scanner'] ?? [];

        // Handle both string '1' and boolean true
        $enabled = $settings['enabled'] ?? '0';
        if ($enabled !== '1' && $enabled !== true && $enabled !== 1) {
            return;
        }

        self::run_scan();
    }

    /**
     * Setup cron schedule
     */
    public static function setup_cron_schedule(): void
    {
        $options = Utils::ultimate_security_get_option();
        $settings = $options['vulnerability_scanner'] ?? [];

        // Clear existing schedule
        $timestamp = wp_next_scheduled(self::CRON_HOOK);
        if ($timestamp) {
            wp_unschedule_event($timestamp, self::CRON_HOOK);
        }

        // Don't schedule if disabled - handle both string '1' and boolean true
        $enabled = $settings['enabled'] ?? '0';
        if ($enabled !== '1' && $enabled !== true && $enabled !== 1) {
            return;
        }

        $schedule = $settings['scan_schedule'] ?? 'daily';
        $valid_schedules = ['hourly', 'twicedaily', 'daily', 'weekly'];

        if (!in_array($schedule, $valid_schedules, true)) {
            $schedule = 'daily';
        }

        // Schedule new event
        wp_schedule_event(time(), $schedule, self::CRON_HOOK);
    }

    /**
     * Get plugin slug from plugin file path
     *
     * @param string $plugin_file Plugin file path
     * @return string Plugin slug
     */
    private static function get_plugin_slug(string $plugin_file): string
    {
        if (strpos($plugin_file, '/') !== false) {
            return explode('/', $plugin_file)[0];
        }
        return str_replace('.php', '', $plugin_file);
    }

    /**
     * Get plugin last updated date from WordPress.org API
     *
     * @param string $slug Plugin slug
     * @return string|null Last updated date or null
     */
    private static function get_plugin_last_updated(string $slug): ?string
    {
        $cache_key = 'ultimate_security_plugin_info_' . $slug;
        $cached = get_transient($cache_key);

        if ($cached !== false) {
            return $cached['last_updated'] ?? null;
        }

        $response = wp_remote_get(
            "https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&slug={$slug}",
            ['timeout' => 10]
        );

        if (is_wp_error($response)) {
            return null;
        }

        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (empty($data) || isset($data['error'])) {
            // Cache negative result for 1 hour
            set_transient($cache_key, ['last_updated' => null], HOUR_IN_SECONDS);
            return null;
        }

        $last_updated = $data['last_updated'] ?? null;

        // Convert to ISO 8601 format for JavaScript compatibility
        // WordPress.org returns dates like "2026-01-28 11:55am GMT" which JS can't parse
        if ($last_updated) {
            $timestamp = strtotime($last_updated);
            if ($timestamp) {
                $last_updated = gmdate('c', $timestamp);
            }
        }

        // Cache for 24 hours
        set_transient($cache_key, ['last_updated' => $last_updated], DAY_IN_SECONDS);

        return $last_updated;
    }

    /**
     * Get theme last updated date from WordPress.org API
     *
     * @param string $slug Theme slug
     * @return string|null Last updated date or null
     */
    private static function get_theme_last_updated(string $slug): ?string
    {
        $cache_key = 'ultimate_security_theme_info_' . $slug;
        $cached = get_transient($cache_key);

        if ($cached !== false) {
            return $cached['last_updated'] ?? null;
        }

        $response = wp_remote_get(
            "https://api.wordpress.org/themes/info/1.2/?action=theme_information&slug={$slug}",
            ['timeout' => 10]
        );

        if (is_wp_error($response)) {
            return null;
        }

        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (empty($data) || isset($data['error'])) {
            set_transient($cache_key, ['last_updated' => null], HOUR_IN_SECONDS);
            return null;
        }

        $last_updated = $data['last_updated'] ?? null;

        // Convert to ISO 8601 format for JavaScript compatibility
        // WordPress.org returns dates like "2026-01-28" which JS can parse, but ensure consistency
        if ($last_updated) {
            $timestamp = strtotime($last_updated);
            if ($timestamp) {
                $last_updated = gmdate('c', $timestamp);
            }
        }

        set_transient($cache_key, ['last_updated' => $last_updated], DAY_IN_SECONDS);

        return $last_updated;
    }

    /**
     * Check if a plugin/theme is abandoned
     *
     * @param string|null $last_updated Last updated date
     * @param int $threshold Days threshold for abandoned status
     * @return bool
     */
    private static function is_abandoned(?string $last_updated, int $threshold = 730): bool
    {
        if (!$last_updated) {
            return false; // Can't determine, might be premium/custom
        }

        $last_updated_time = strtotime($last_updated);
        if (!$last_updated_time) {
            return false;
        }

        $days_since_update = (time() - $last_updated_time) / DAY_IN_SECONDS;
        return $days_since_update > $threshold;
    }

    /**
     * Check if plugin/theme has update available
     *
     * @param string $type 'plugin' or 'theme'
     * @param string $identifier Plugin file or theme slug
     * @return bool
     */
    private static function has_update_available(string $type, string $identifier): bool
    {
        if ($type === 'plugin') {
            $update_plugins = get_site_transient('update_plugins');
            return isset($update_plugins->response[$identifier]);
        }

        $update_themes = get_site_transient('update_themes');
        return isset($update_themes->response[$identifier]);
    }

    /**
     * Send email notification for vulnerabilities
     *
     * @param array $results Scan results
     * @param array $settings Scanner settings
     */
    private static function maybe_send_notification(array $results, array $settings): void
    {
        $notify_severity = $settings['notify_severity'] ?? ['critical', 'high'];
        $vulnerabilities_to_notify = [];

        foreach (['plugins', 'themes', 'wordpress'] as $type) {
            if (!empty($results[$type])) {
                foreach ($results[$type] as $item) {
                    if (!empty($item['vulnerabilities'])) {
                        foreach ($item['vulnerabilities'] as $vuln) {
                            $severity = strtolower($vuln['severity'] ?? 'medium');
                            if (in_array($severity, $notify_severity, true)) {
                                $vulnerabilities_to_notify[] = [
                                    'type' => $type,
                                    'name' => $item['name'],
                                    'version' => $item['version'],
                                    'vulnerability' => $vuln,
                                ];
                            }
                        }
                    }
                }
            }
        }

        if (empty($vulnerabilities_to_notify)) {
            return;
        }

        $to = $settings['notification_email'] ?? get_option('admin_email');
        $site_name = get_bloginfo('name');
        $site_url = get_site_url();

        $subject = sprintf(
            // translators: %1$d is count, %2$s is site name
            __('[%2$s] %1$d Security Vulnerabilities Detected', 'ultimate-security'),
            count($vulnerabilities_to_notify),
            $site_name
        );

        $message = sprintf(
            // translators: %s is site URL
            __("The Ultimate Security vulnerability scanner has detected security issues on your site:\n\nSite: %s\n\n", 'ultimate-security'),
            $site_url
        );

        foreach ($vulnerabilities_to_notify as $item) {
            $vuln = $item['vulnerability'];
            $message .= sprintf(
                "---\n%s: %s (v%s)\n%s: %s\n%s: %s\n",
                ucfirst($item['type']),
                $item['name'],
                $item['version'],
                __('Severity', 'ultimate-security'),
                ucfirst($vuln['severity'] ?? 'Unknown'),
                __('Title', 'ultimate-security'),
                $vuln['title'] ?? __('Unknown vulnerability', 'ultimate-security')
            );

            if (!empty($vuln['fixed_in'])) {
                $message .= sprintf(
                    "%s: %s\n",
                    __('Fixed in version', 'ultimate-security'),
                    $vuln['fixed_in']
                );
            }

            if (!empty($vuln['references']['url'])) {
                $message .= sprintf(
                    "%s: %s\n",
                    __('More info', 'ultimate-security'),
                    is_array($vuln['references']['url']) ? $vuln['references']['url'][0] : $vuln['references']['url']
                );
            }

            $message .= "\n";
        }

        $message .= sprintf(
            // translators: %s is admin URL
            __("Please update the affected components as soon as possible.\n\nManage updates: %s", 'ultimate-security'),
            admin_url('update-core.php')
        );

        wp_mail($to, $subject, $message);

        // Trigger external alerting system (Pro feature)
        self::trigger_vulnerability_alert($vulnerabilities_to_notify, $results);
    }

    /**
     * Trigger external alerting for vulnerabilities (Pro only)
     *
     * @param array $vulnerabilities Vulnerabilities to alert about
     * @param array $results Full scan results
     */
    private static function trigger_vulnerability_alert(array $vulnerabilities, array $results): void
    {
        // Only trigger if Pro is active and external alerting is available
        if (!defined('ULTIMATE_SECURITY_PRO') || !ULTIMATE_SECURITY_PRO) {
            return;
        }

        // Count by severity
        $critical_count = 0;
        $high_count = 0;
        foreach ($vulnerabilities as $item) {
            $severity = strtolower($item['vulnerability']['severity'] ?? 'medium');
            if ($severity === 'critical') {
                $critical_count++;
            } elseif ($severity === 'high') {
                $high_count++;
            }
        }

        // Determine priority based on highest severity found
        $priority = 'medium';
        if ($critical_count > 0) {
            $priority = 'critical';
        } elseif ($high_count > 0) {
            $priority = 'high';
        }

        // Build alert message
        $title = sprintf(
            // translators: %d is count of vulnerabilities
            __('%d Security Vulnerabilities Found', 'ultimate-security'),
            count($vulnerabilities)
        );

        $message = sprintf(
            // translators: %1$d is total count, %2$d is critical count, %3$d is high count
            __("Vulnerability scan detected %1\$d security issues.\n\nCritical: %2\$d\nHigh: %3\$d\n\n", 'ultimate-security'),
            count($vulnerabilities),
            $critical_count,
            $high_count
        );

        // Add summary of affected items
        $affected_plugins = [];
        $affected_themes = [];
        foreach ($vulnerabilities as $item) {
            if ($item['type'] === 'plugins') {
                $affected_plugins[] = $item['name'];
            } elseif ($item['type'] === 'themes') {
                $affected_themes[] = $item['name'];
            }
        }

        if (!empty($affected_plugins)) {
            $message .= __('Affected Plugins: ', 'ultimate-security') . implode(', ', array_unique($affected_plugins)) . "\n";
        }
        if (!empty($affected_themes)) {
            $message .= __('Affected Themes: ', 'ultimate-security') . implode(', ', array_unique($affected_themes)) . "\n";
        }

        $message .= sprintf(
            // translators: %s is admin URL
            __("\nView full report: %s", 'ultimate-security'),
            admin_url('admin.php?page=ultimate-security#/vulnerability-scanner')
        );

        // Trigger the alert action
        $alert_data = [
            'type' => 'vulnerability_scan',
            'title' => $title,
            'message' => $message,
            'priority' => $priority,
            'vulnerability_count' => count($vulnerabilities),
            'critical_count' => $critical_count,
            'high_count' => $high_count,
            'affected_plugins' => $affected_plugins,
            'affected_themes' => $affected_themes,
            'scan_time' => $results['scan_time'] ?? current_time('mysql'),
        ];

        /**
         * Fire the external alerting action.
         * This hook is provided by the Pro plugin's ExternalAlerting module.
         *
         * @param array $alert_data Alert data including type, title, message, priority
         */
        do_action('ultimate_security_send_alert', $alert_data);

        // For critical vulnerabilities, send immediately
        if ($priority === 'critical') {
            do_action('ultimate_security_send_critical_alert', $alert_data);
        }
    }

    /**
     * Get default settings
     *
     * @return array
     */
    public static function get_default_settings(): array
    {
        return [
            'enabled' => '0',
            'wpscan_api_key' => '',
            'patchstack_api_key' => '',
            'scan_schedule' => 'daily',
            'email_notifications' => '0',
            'notification_email' => get_option('admin_email'),
            'notify_severity' => ['critical', 'high'],
            'abandoned_threshold' => 730, // 2 years in days
        ];
    }

    /**
     * Log vulnerability scan event to Activity Logs (Pro only)
     *
     * @param array $results Scan results
     */
    private static function log_scan_event(array $results): void
    {
        // Only log if Pro is active and EventLogger is available
        if (!defined('ULTIMATE_SECURITY_PRO') || !ULTIMATE_SECURITY_PRO) {
            return;
        }

        // Calculate stats
        $total_vulnerabilities = 0;
        $critical_count = 0;
        $high_count = 0;
        $plugins_scanned = count($results['plugins'] ?? []);
        $themes_scanned = count($results['themes'] ?? []);

        foreach (['plugins', 'themes', 'wordpress'] as $type) {
            if (!empty($results[$type])) {
                foreach ($results[$type] as $item) {
                    if (!empty($item['vulnerabilities'])) {
                        $total_vulnerabilities += count($item['vulnerabilities']);
                        foreach ($item['vulnerabilities'] as $vuln) {
                            $severity = strtolower($vuln['severity'] ?? 'medium');
                            if ($severity === 'critical') {
                                $critical_count++;
                            } elseif ($severity === 'high') {
                                $high_count++;
                            }
                        }
                    }
                }
            }
        }

        // Determine severity based on findings
        $log_severity = 'low';
        if ($critical_count > 0) {
            $log_severity = 'critical';
        } elseif ($high_count > 0) {
            $log_severity = 'high';
        } elseif ($total_vulnerabilities > 0) {
            $log_severity = 'medium';
        }

        /**
         * Log the scan completion event using the EventLogger hook.
         * This hook is provided by the Pro plugin's EventLogger module.
         *
         * @param string $event_type The type of event
         * @param array $event_data Event data and metadata
         */
        do_action('ultimate_security_log_event', 'vulnerability_scan_completed', [
            'description' => sprintf(
                // translators: %1$d is total vulnerabilities, %2$d is plugins scanned, %3$d is themes scanned
                __('Vulnerability scan completed. Found %1$d vulnerabilities across %2$d plugins and %3$d themes.', 'ultimate-security'),
                $total_vulnerabilities,
                $plugins_scanned,
                $themes_scanned
            ),
            'total_vulnerabilities' => $total_vulnerabilities,
            'critical_count' => $critical_count,
            'high_count' => $high_count,
            'plugins_scanned' => $plugins_scanned,
            'themes_scanned' => $themes_scanned,
            'api_used' => $results['api_used'] ?? 'unknown',
            'scan_time' => $results['scan_time'] ?? current_time('mysql'),
            'severity' => $log_severity,
        ]);

        // Also log individual critical vulnerabilities found
        if ($critical_count > 0) {
            foreach (['plugins', 'themes', 'wordpress'] as $type) {
                if (!empty($results[$type])) {
                    foreach ($results[$type] as $item) {
                        if (!empty($item['vulnerabilities'])) {
                            foreach ($item['vulnerabilities'] as $vuln) {
                                if (strtolower($vuln['severity'] ?? '') === 'critical') {
                                    do_action('ultimate_security_log_event', 'vulnerability_found', [
                                        'description' => sprintf(
                                            // translators: %1$s is item name, %2$s is vulnerability title
                                            __('Critical vulnerability found in %1$s: %2$s', 'ultimate-security'),
                                            $item['name'],
                                            $vuln['title'] ?? __('Unknown vulnerability', 'ultimate-security')
                                        ),
                                        'item_type' => $type,
                                        'item_name' => $item['name'],
                                        'item_version' => $item['version'],
                                        'vulnerability_title' => $vuln['title'] ?? '',
                                        'vulnerability_severity' => $vuln['severity'] ?? '',
                                        'vulnerability_cve' => $vuln['cve'] ?? '',
                                        'fixed_in' => $vuln['fixed_in'] ?? '',
                                        'severity' => 'critical',
                                    ]);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * API: Ignore a vulnerability or item
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function api_ignore_item(\WP_REST_Request $request): \WP_REST_Response
    {
        $type = $request->get_param('type');
        $id = $request->get_param('id');

        if (!in_array($type, ['vulnerability', 'plugin', 'theme'], true)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => __('Invalid type. Must be vulnerability, plugin, or theme.', 'ultimate-security'),
            ], 400);
        }

        $ignored = get_option(self::IGNORED_OPTION, [
            'vulnerabilities' => [],
            'plugins' => [],
            'themes' => [],
        ]);

        // Ensure arrays exist
        $ignored['vulnerabilities'] = $ignored['vulnerabilities'] ?? [];
        $ignored['plugins'] = $ignored['plugins'] ?? [];
        $ignored['themes'] = $ignored['themes'] ?? [];

        // Map type to key
        $key = $type === 'vulnerability' ? 'vulnerabilities' : ($type . 's');

        // Add to ignored list if not already there
        if (!in_array($id, $ignored[$key], true)) {
            $ignored[$key][] = $id;
            update_option(self::IGNORED_OPTION, $ignored);
        }

        return new \WP_REST_Response([
            'success' => true,
            'message' => sprintf(
                // translators: %s is the item type
                __('%s has been ignored.', 'ultimate-security'),
                ucfirst($type)
            ),
            'ignored' => $ignored,
        ], 200);
    }

    /**
     * API: Un-ignore an item
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public static function api_unignore_item(\WP_REST_Request $request): \WP_REST_Response
    {
        $type = $request->get_param('type');
        $id = $request->get_param('id');

        if (!in_array($type, ['vulnerability', 'plugin', 'theme'], true)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => __('Invalid type. Must be vulnerability, plugin, or theme.', 'ultimate-security'),
            ], 400);
        }

        $ignored = get_option(self::IGNORED_OPTION, [
            'vulnerabilities' => [],
            'plugins' => [],
            'themes' => [],
        ]);

        // Map type to key
        $key = $type === 'vulnerability' ? 'vulnerabilities' : ($type . 's');

        // Remove from ignored list
        if (isset($ignored[$key])) {
            $ignored[$key] = array_values(array_filter($ignored[$key], function ($item) use ($id) {
                return $item !== $id;
            }));
            update_option(self::IGNORED_OPTION, $ignored);
        }

        return new \WP_REST_Response([
            'success' => true,
            'message' => sprintf(
                // translators: %s is the item type
                __('%s has been removed from ignored list.', 'ultimate-security'),
                ucfirst($type)
            ),
            'ignored' => $ignored,
        ], 200);
    }

    /**
     * API: Get all ignored items
     *
     * @return \WP_REST_Response
     */
    public static function api_get_ignored(): \WP_REST_Response
    {
        $ignored = get_option(self::IGNORED_OPTION, [
            'vulnerabilities' => [],
            'plugins' => [],
            'themes' => [],
        ]);

        // Count totals
        $total = count($ignored['vulnerabilities'] ?? [])
            + count($ignored['plugins'] ?? [])
            + count($ignored['themes'] ?? []);

        return new \WP_REST_Response([
            'success' => true,
            'data' => [
                'ignored' => $ignored,
                'total' => $total,
            ],
        ], 200);
    }
}
