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

class Vulnity_Core {
    private static $instance = null;
    private $alert_manager = null;
    private $siem_connector = null;
    private $inventory_sync = null;
    private $security_monitor = null;
    private $mitigation_manager = null;
    private $static_security = null;
    private $default_tab = 'dashboard';

    private function sanitize_ajax_message($message) {
        if (is_array($message) || is_object($message)) {
            $message = wp_json_encode($message);
        }

        if (!is_scalar($message)) {
            return '';
        }

        return sanitize_text_field((string) $message);
    }

    public static function get_instance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    private function __construct() {
        $this->load_dependencies();

        if (is_admin()) {
            add_action('admin_menu', array($this, 'admin_menu'));
            add_action('admin_enqueue_scripts', array($this, 'admin_scripts'));
            add_action('admin_notices', array($this, 'show_remote_disconnect_notice'));
            $this->register_ajax_handlers();
        }

        add_action('rest_api_init', array($this, 'register_rest_routes'));
    }
    
    private function load_dependencies() {
        require_once vulnity_plugin_path('includes/class-siem-connector.php');
        require_once vulnity_plugin_path('includes/class-alert-manager.php');
        require_once vulnity_plugin_path('includes/class-inventory-sync.php');
        require_once vulnity_plugin_path('includes/class-security-monitor.php');
        require_once vulnity_plugin_path('includes/class-firewall-manager.php');
        require_once vulnity_plugin_path('includes/class-mitigation-manager.php');
        require_once vulnity_plugin_path('includes/class-static-security.php');

        $this->siem_connector = Vulnity_SIEM_Connector::get_instance();
        $this->alert_manager = Vulnity_Alert_Manager::get_instance();
        $this->inventory_sync = Vulnity_Inventory_Sync::get_instance();
        $this->security_monitor = Vulnity_Security_Monitor::get_instance();
        Vulnity_Firewall_Manager::get_instance();
        $this->mitigation_manager = Vulnity_Mitigation_Manager::get_instance();
        $this->static_security = Vulnity_Static_Security::get_instance();
    }

    public function admin_menu() {
        $menu_title = 'Vulnity';
        $capability = 'manage_options';
        $menu_icon = 'dashicons-shield';

        add_menu_page(
            'Vulnity Security',
            $menu_title,
            $capability,
            'vulnity',
            array($this, 'admin_page'),
            $menu_icon
        );

        add_submenu_page('vulnity', 'Vulnity Dashboard', 'Dashboard', $capability, 'vulnity', array($this, 'admin_page'));
        add_submenu_page('vulnity', 'Vulnity Alerts', 'Alerts', $capability, 'vulnity-alerts', array($this, 'admin_page_alerts'));
        add_submenu_page('vulnity', 'Vulnity Mitigation', 'Mitigation', $capability, 'vulnity-mitigation', array($this, 'admin_page_mitigation'));
        add_submenu_page('vulnity', 'Vulnity Hardening', 'Hardening', $capability, 'vulnity-hardening', array($this, 'admin_page_hardening'));
        add_submenu_page('vulnity', 'Vulnity Synchronization', 'Synchronization', $capability, 'vulnity-sync', array($this, 'admin_page_sync'));
        add_submenu_page('vulnity', 'Vulnity Settings', 'Settings', $capability, 'vulnity-settings', array($this, 'admin_page_settings'));
    }

    private function register_admin_assets() {
        $plugin_file = vulnity_plugin_path('vulnity.php');
        $plugin_url  = plugin_dir_url($plugin_file);

        if (!wp_script_is('vulnity-admin', 'registered')) {
            wp_register_script('vulnity-admin', $plugin_url . 'assets/admin.js', array('jquery'), VULNITY_VERSION, true);
        }

        if (!wp_style_is('vulnity-admin', 'registered')) {
            wp_register_style('vulnity-admin', $plugin_url . 'assets/admin.css', array(), VULNITY_VERSION);
        }
    }

    public function admin_scripts($hook) {
        if (!is_admin()) {
            return;
        }

        $valid_hooks = array(
            'toplevel_page_vulnity',
            'vulnity_page_vulnity',
            'vulnity_page_vulnity-alerts',
            'vulnity_page_vulnity-sync',
            'vulnity_page_vulnity-settings',
            'vulnity_page_vulnity-mitigation',
            'vulnity_page_vulnity-hardening',
        );

        if (!in_array($hook, $valid_hooks, true)) return;

        $this->register_admin_assets();

        $localize_data = array(
            'ajaxurl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('vulnity_nonce'),
            'version' => VULNITY_VERSION,
        );

        wp_enqueue_script('vulnity-admin');
        wp_localize_script('vulnity-admin', 'vulnity_ajax', $localize_data);

        wp_enqueue_style('vulnity-admin');
    }
    
    private function register_ajax_handlers() {
        add_action('wp_ajax_vulnity_pair', array($this, 'ajax_pair'));
        add_action('wp_ajax_vulnity_unpair', array($this, 'ajax_unpair'));
        add_action('wp_ajax_vulnity_mark_viewed', array($this, 'ajax_mark_viewed'));
        add_action('wp_ajax_vulnity_clear_alerts', array($this, 'ajax_clear_alerts'));
        add_action('wp_ajax_vulnity_test_connection', array($this, 'ajax_test_connection'));
        add_action('wp_ajax_vulnity_get_alerts', array($this, 'ajax_get_alerts'));
        add_action('wp_ajax_vulnity_sync_inventory', array($this, 'ajax_sync_inventory'));
        add_action('wp_ajax_vulnity_retry_alert', array($this, 'ajax_retry_alert'));
        add_action('wp_ajax_vulnity_retry_all_failed', array($this, 'ajax_retry_all_failed'));
        add_action('wp_ajax_vulnity_unblock_ip', array($this->mitigation_manager, 'ajax_unblock_ip'));
        add_action('wp_ajax_vulnity_block_ip', array($this->mitigation_manager, 'ajax_block_ip'));
        add_action('wp_ajax_vulnity_sync_mitigation', array($this->mitigation_manager, 'ajax_sync_mitigation'));
    }
    
    public function admin_page() {
        $config = get_option('vulnity_config');
        $status = $this->get_status();
        $this->static_security->maybe_handle_form_submission();
        $active_tab = $this->get_active_tab();

        if ($status === 'not_configured') {
            include vulnity_plugin_path('views/admin-setup.php');
        } else {
            include vulnity_plugin_path('views/admin-dashboard.php');
        }
    }

    public function admin_page_alerts() {
        $this->set_default_tab('alerts');
        $this->admin_page();
    }

    public function admin_page_sync() {
        $this->set_default_tab('sync');
        $this->admin_page();
    }

    public function admin_page_settings() {
        $this->set_default_tab('settings');
        $this->admin_page();
    }

    public function admin_page_mitigation() {
        $this->set_default_tab('mitigation');
        $this->admin_page();
    }

    public function admin_page_hardening() {
        $this->set_default_tab('hardening');
        $this->admin_page();
    }

    private function set_default_tab($tab) {
        $allowed_tabs = $this->get_allowed_tabs();

        if (in_array($tab, $allowed_tabs, true)) {
            $this->default_tab = $tab;
        }
    }

    private function get_active_tab() {
        $allowed_tabs = $this->get_allowed_tabs();
        $active_tab   = $this->default_tab;

        // Leer "tab" desde INPUT_GET en vez de usar $_GET directamente
        $tab_raw = filter_input(INPUT_GET, 'tab', FILTER_SANITIZE_FULL_SPECIAL_CHARS);

        if (null !== $tab_raw) {
            // Aunque filter_input no aplica magic quotes, wp_unslash aquí es inocuo
            $requested_tab = sanitize_key(wp_unslash($tab_raw));

            if (in_array($requested_tab, $allowed_tabs, true)) {
                $active_tab = $requested_tab;
            }
        } else {
            // Igual para "page"
            $page_raw = filter_input(INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS);

            if (null !== $page_raw) {
                $page_slug = sanitize_key(wp_unslash($page_raw));
                $tab_from_page = $this->map_page_to_tab($page_slug);

                if ($tab_from_page && in_array($tab_from_page, $allowed_tabs, true)) {
                    $active_tab = $tab_from_page;
                }
            }
        }

        return $active_tab;
    }

    private function map_page_to_tab($page_slug) {
        $map = array(
            'vulnity' => 'dashboard',
            'vulnity-alerts' => 'alerts',
            'vulnity-mitigation' => 'mitigation',
            'vulnity-hardening' => 'hardening',
            'vulnity-sync' => 'sync',
            'vulnity-settings' => 'settings',
        );

        return $map[$page_slug] ?? '';
    }

    private function get_allowed_tabs() {
        return array('dashboard', 'alerts', 'mitigation', 'hardening', 'sync', 'settings');
    }

    private function verify_admin_ajax_request($capability = 'manage_options') {
        // Use `false` to prevent `check_ajax_referer` from exiting; we send JSON errors and return early ourselves.
        $verified = check_ajax_referer('vulnity_nonce', 'nonce', false);

        if (!$verified) {
            wp_send_json_error('Security check failed');
            return false;
        }

        if (!current_user_can($capability)) {
            wp_send_json_error('Security check failed');
            return false;
        }

        return true;
    }
    
    public function ajax_pair() {
        if (!$this->verify_admin_ajax_request()) {
            return;
        }

        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce is verified earlier in this handler (check_ajax_referer/verify_admin_ajax_request).
        $site_id = isset($_POST['site_id']) ? sanitize_text_field(wp_unslash($_POST['site_id'])) : '';
        $pair_code = isset($_POST['pair_code']) ? sanitize_text_field(wp_unslash($_POST['pair_code'])) : '';
        $public_ip_whitelist = isset($_POST['public_ip_whitelist']) ? sanitize_text_field(wp_unslash($_POST['public_ip_whitelist'])) : '';
        // phpcs:enable WordPress.Security.NonceVerification.Missing
        $public_ip_whitelist = trim($public_ip_whitelist);
        
        if (empty($site_id) || empty($pair_code)) {
            wp_send_json_error('Site ID and Pair Code required');
            return;
        }

        if ($public_ip_whitelist !== '' && !filter_var($public_ip_whitelist, FILTER_VALIDATE_IP)) {
            wp_send_json_error('Invalid public IP format');
            return;
        }
        
        $result = $this->pair_plugin($site_id, $pair_code, $public_ip_whitelist);
        if ($result['success']) {
            $this->inventory_sync->perform_sync('initial');
            $this->siem_connector->send_heartbeat('online');
            $message = isset($result['message']) ? $this->sanitize_ajax_message($result['message']) : '';
            wp_send_json_success($message);
        } else {
            $message = isset($result['message']) ? $this->sanitize_ajax_message($result['message']) : 'Pairing failed.';
            wp_send_json_error($message);
        }
    }
    
    public function ajax_unpair() {
        if ( ! $this->verify_admin_ajax_request() ) {
            return;
        }
        
        // Send unpair request to SIEM first
        $unpair_result = $this->unpair_plugin('manual');
        
        if (!$unpair_result['success']) {
            vulnity_log('[Vulnity] Warning: Could not notify SIEM of manual unpair: ' . ($unpair_result['error'] ?? 'Unknown error'));
        }
        
        // Notify SIEM of disconnection before wiping local config
        $this->siem_connector->send_heartbeat('offline');

        // Clean up local data regardless of SIEM notification result
        delete_option('vulnity_config');

        $this->inventory_sync->cleanup();
        $this->cancel_siem_crons();

        wp_send_json_success('Plugin unpaired successfully');
    }
    
    public function ajax_mark_viewed() {
        if ( ! $this->verify_admin_ajax_request() ) {
            return;
        }

        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce is verified earlier in this handler via verify_admin_ajax_request().
        $alert_id = isset($_POST['alert_id']) ? sanitize_text_field(wp_unslash($_POST['alert_id'])) : '';
        $mark_all = isset($_POST['mark_all']) ? rest_sanitize_boolean(wp_unslash($_POST['mark_all'])) : false;
        // phpcs:enable WordPress.Security.NonceVerification.Missing

        if ( ! $mark_all && empty($alert_id) ) {
            wp_send_json_error('Alert ID required');
            return;
        }

        if ($mark_all) {
            $this->alert_manager->mark_all_as_viewed();
        } else {
            $this->alert_manager->mark_alert_as_viewed($alert_id);
        }
        
        wp_send_json_success('Alert marked as viewed');
    }
    
    public function ajax_clear_alerts() {
        if ( ! $this->verify_admin_ajax_request() ) {
            return;
        }
        
        $this->alert_manager->clear_all_alerts();
        wp_send_json_success('Alerts cleared');
    }
    
    public function ajax_test_connection() {
        if ( ! $this->verify_admin_ajax_request() ) {
            return;
        }
        
        $result = $this->siem_connector->test_connection();

        if ($result['success']) {
            $message = isset($result['message']) ? $this->sanitize_ajax_message($result['message']) : 'Connection successful!';
            wp_send_json_success($message);
        } else {
            $error = isset($result['error']) ? $this->sanitize_ajax_message($result['error']) : 'Unknown error';
            $error_message = sprintf('Connection failed: %s', $error);
            wp_send_json_error($error_message);
        }
    }
    
    public function ajax_get_alerts() {
        if ( ! $this->verify_admin_ajax_request() ) {
            return;
        }

        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce is verified earlier in this handler via verify_admin_ajax_request().
        $limit = isset($_POST['limit']) ? absint(wp_unslash($_POST['limit'])) : 50;
        $filter = isset($_POST['filter']) ? sanitize_text_field(wp_unslash($_POST['filter'])) : 'all';
        // phpcs:enable WordPress.Security.NonceVerification.Missing
        
        $alerts = $this->alert_manager->get_alerts($limit, $filter);
        wp_send_json_success($alerts);
    }

    public function ajax_sync_inventory() {
        if ( ! $this->verify_admin_ajax_request() ) {
            return;
        }
        
        $result = $this->inventory_sync->perform_sync('manual');

        if ($result['success']) {
            wp_send_json_success('Inventory synchronized successfully');
        } else {
            $error = isset($result['error']) ? $this->sanitize_ajax_message($result['error']) : 'Unknown error';
            wp_send_json_error(sprintf('Synchronization failed: %s', $error));
        }
    }

    public function ajax_retry_alert() {
        if ( ! $this->verify_admin_ajax_request() ) {
            return;
        }

        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce is verified earlier in this handler via verify_admin_ajax_request().
        $alert_id = isset($_POST['alert_id']) ? sanitize_text_field(wp_unslash($_POST['alert_id'])) : '';
        // phpcs:enable WordPress.Security.NonceVerification.Missing
        
        if (empty($alert_id)) {
            wp_send_json_error('Alert ID required');
            return;
        }
        
        // Load alert base class if needed
        if (!class_exists('Vulnity_Alert_Base')) {
            $alerts_dir = vulnity_plugin_path('includes/alerts/');
            if (file_exists($alerts_dir . 'class-alert-base.php')) {
                require_once $alerts_dir . 'class-alert-base.php';
            }
        }
        
        $result = Vulnity_Alert_Base::retry_single_alert($alert_id);

        if ($result['success']) {
            $message = isset($result['message']) ? $this->sanitize_ajax_message($result['message']) : 'Alert retried successfully';
            wp_send_json_success($message);
        } else {
            $error = isset($result['error']) ? $this->sanitize_ajax_message($result['error']) : 'Unknown error';
            wp_send_json_error($error);
        }
    }

    public function ajax_retry_all_failed() {
        if ( ! $this->verify_admin_ajax_request() ) {
            return;
        }
        
        // Load alert base class if needed
        if (!class_exists('Vulnity_Alert_Base')) {
            $alerts_dir = vulnity_plugin_path('includes/alerts/');
            if (file_exists($alerts_dir . 'class-alert-base.php')) {
                require_once $alerts_dir . 'class-alert-base.php';
            }
        }
        
        $result = Vulnity_Alert_Base::retry_all_failed_alerts();

        if ($result['success']) {
            $message = isset($result['message']) ? $this->sanitize_ajax_message($result['message']) : 'Alerts retried successfully';
            wp_send_json_success($message);
        } else {
            wp_send_json_error('Failed to retry alerts');
        }
    }
    
    private function pair_plugin($site_id, $pair_code, $public_ip_whitelist = '') {
        if (!preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $site_id)) {
            return array('success' => false, 'message' => 'Invalid Site ID format');
        }
        
        if (strlen($pair_code) !== 8) {
            return array('success' => false, 'message' => 'Pair Code must be 8 characters');
        }
        
        $whitelist_ips = array('127.0.0.1', '::1');
        if ($public_ip_whitelist !== '' && filter_var($public_ip_whitelist, FILTER_VALIDATE_IP)) {
            $whitelist_ips[] = $public_ip_whitelist;
        }
        $whitelist_ips = array_values(array_unique($whitelist_ips));

        $args = array(
            'body' => json_encode(array(
                'site_id' => $site_id,
                'pair_code' => $pair_code,
                'plugin_version' => VULNITY_VERSION,
                'wordpress_version' => get_bloginfo('version'),
                'php_version' => PHP_VERSION,
                'site_url' => get_site_url(),
                'whitelist_ips' => $whitelist_ips,
            )),
            'headers' => array('Content-Type' => 'application/json'),
            'timeout' => 30,
            'sslverify' => true
        );
        
        $response = wp_remote_post(VULNITY_BASE_URL . '/pair-plugin', $args);
        
        if (is_wp_error($response)) {
            return array('success' => false, 'message' => $response->get_error_message());
        }
        
        $status = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        
        vulnity_log('[Vulnity] Pairing response status: ' . $status);
        vulnity_log('[Vulnity] Pairing response body: ' . $this->redact_sensitive_fields($body));
        
        if ($status === 200) {
            $data = json_decode($body, true);
            
            if ($data && isset($data['success']) && $data['success']) {
                // Store the correct fields from the response
                // token: for alert authentication
                // signing_secret: for HMAC signatures (this is the api_key)
                $token = isset($data['token']) ? $data['token'] : '';
                $signing_secret = isset($data['signing_secret']) ? $data['signing_secret'] : '';
                
                if (empty($token) || empty($signing_secret)) {
                    return array('success' => false, 'message' => 'Invalid pairing response - missing credentials');
                }
                
                update_option('vulnity_config', array(
                    'site_id' => trim($site_id),
                    'token' => trim($token),
                    'signing_secret' => trim($signing_secret), // This is the api_key for HMAC
                    'paired_at' => current_time('mysql'),
                    'status' => 'active'
                ));

                $success_message = 'Plugin successfully paired with Vulnity SIEM';
                if ($public_ip_whitelist !== '' && $this->add_public_ip_to_whitelist($public_ip_whitelist)) {
                    $success_message .= '. Public IP added to whitelist.';
                }

                vulnity_log('[Vulnity] Plugin paired successfully with token and signing_secret');
                return array('success' => true, 'message' => $success_message);
            } else {
                $error_code    = isset($data['error']) ? $data['error'] : 'Invalid response format';
                $error_message = $this->translate_pairing_error($error_code);
                return array('success' => false, 'message' => 'Pairing failed: ' . $error_message);
            }
        } else {
            $data       = json_decode($body, true);
            $error_code = isset($data['error']) ? $data['error'] : 'HTTP ' . $status;
            $error_message = $this->translate_pairing_error($error_code);
            return array('success' => false, 'message' => 'Pairing failed: ' . $error_message);
        }
    }

    private function add_public_ip_to_whitelist($ip) {
        $ip = trim((string) $ip);

        if ($ip === '' || !filter_var($ip, FILTER_VALIDATE_IP)) {
            return false;
        }

        $whitelist = get_option('vulnity_ip_whitelist', array());
        if (!is_array($whitelist)) {
            $whitelist = array();
        }

        $normalized = array();
        foreach ($whitelist as $entry) {
            if (!is_string($entry)) {
                continue;
            }

            $entry = trim($entry);
            if ($entry !== '') {
                $normalized[] = $entry;
            }
        }

        $normalized[] = $ip;
        $normalized[] = '127.0.0.1';
        $normalized[] = '::1';
        $normalized = array_values(array_unique($normalized));

        update_option('vulnity_ip_whitelist', $normalized);

        if (class_exists('Vulnity_Firewall_Manager')) {
            Vulnity_Firewall_Manager::get_instance()->sync_from_options();
        }

        return true;
    }

    private function translate_pairing_error($error_code) {
        $map = array(
            'missing_home_url'         => __('The domain configured for this WordPress site does not match the domain registered in SIEM. Verify the URL in Add Domain -> Settings -> Full WordPress URL (for example, https://example.com).', 'vulnity'),
            'invalid_pair_credentials' => __('The Site ID or Pair Code is incorrect.', 'vulnity'),
        );

        return isset($map[$error_code]) ? $map[$error_code] : $error_code;
    }

    private function redact_sensitive_fields($body) {
        if (!is_string($body) || $body === '') {
            return $body;
        }

        $decoded = json_decode($body, true);
        if (is_array($decoded)) {
            $decoded = $this->redact_sensitive_array($decoded, array('token', 'signing_secret', 'api_key', 'secret', 'anon_key'));
            $encoded = wp_json_encode($decoded);
            if (is_string($encoded) && $encoded !== '') {
                return $encoded;
            }
        }

        $patterns = array(
            '/("token"\\s*:\\s*")[^"]*"/i',
            '/("signing_secret"\\s*:\\s*")[^"]*"/i',
            '/("api_key"\\s*:\\s*")[^"]*"/i',
            '/("secret"\\s*:\\s*")[^"]*"/i',
            '/("anon_key"\\s*:\\s*")[^"]*"/i'
        );

        return preg_replace($patterns, '$1[redacted]"', $body);
    }

    private function redact_sensitive_array($data, $keys) {
        if (!is_array($data)) {
            return $data;
        }

        $key_lookup = array();
        foreach ($keys as $key) {
            $key_lookup[strtolower((string) $key)] = true;
        }

        foreach ($data as $key => $value) {
            $key_lower = strtolower((string) $key);
            if (isset($key_lookup[$key_lower])) {
                $data[$key] = '[redacted]';
                continue;
            }

            if (is_array($value)) {
                $data[$key] = $this->redact_sensitive_array($value, $keys);
            }
        }

        return $data;
    }
    
    // =====================================================
    // AQUÍ VA EL MÉTODO unpair_plugin
    // =====================================================
    public function unpair_plugin($reason = 'manual') {
        $config = get_option('vulnity_config');
        
        // Check for signing_secret (the api_key for HMAC)
        if (empty($config['site_id']) || empty($config['signing_secret'])) {
            vulnity_log('[Vulnity] Cannot unpair - missing site_id or signing_secret');
            return array('success' => false, 'error' => 'Missing configuration');
        }
        
        global $wp_version;
        
        $payload = array(
            'site_id' => $config['site_id'],
            'reason' => $reason,
            'plugin_version' => VULNITY_VERSION,
            'wordpress_version' => $wp_version,
            'php_version' => PHP_VERSION,
            'occurred_at' => current_time('c')
        );
        
        // Create JSON string for signing
        $body_json = wp_json_encode($payload);
        
        // Generate HMAC signature using the signing_secret
        $signature = base64_encode(hash_hmac('sha256', $body_json, $config['signing_secret'], true));
        
        $headers = array(
            'Content-Type' => 'application/json',
            'x-signature' => $signature
        );
        
        $args = array(
            'method' => 'POST',
            'headers' => $headers,
            'body' => $body_json,
            'timeout' => 10,
            'sslverify' => true
        );
        
        $response = wp_remote_post(VULNITY_BASE_URL . '/unpair-plugin', $args);
        
        if (is_wp_error($response)) {
            vulnity_log('[Vulnity] Failed to unpair plugin: ' . $response->get_error_message());
            return array('success' => false, 'error' => $response->get_error_message());
        }
        
        $status_code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        $response_data = json_decode($body, true);
        
        if ($status_code === 200 || $status_code === 201) {
            vulnity_log('[Vulnity] Plugin unpaired successfully - Reason: ' . $reason);
            
            // Update local status to reflect unpaired state
            $config['status'] = 'inactive';
            $config['unpaired_at'] = current_time('mysql');
            $config['unpair_reason'] = $reason;
            update_option('vulnity_config', $config);
            
            return array(
                'success' => true, 
                'data' => $response_data,
                'message' => 'Plugin unpaired successfully'
            );
        } else {
            vulnity_log('[Vulnity] Unpair request failed with status ' . $status_code . ' - Body: ' . $body);
            return array(
                'success' => false, 
                'error' => 'HTTP ' . $status_code,
                'data' => $response_data
            );
        }
    }
    
    public function cleanup($reason = 'deactivation') {
        $reason = is_string($reason) ? strtolower($reason) : 'deactivation';
        $full_cleanup = ($reason === 'uninstall');

        if (!$full_cleanup) {
            // Conservative behavior: deactivation/runtime cleanup must not remove pairing/data.
            if ($this->inventory_sync instanceof Vulnity_Inventory_Sync) {
                $this->inventory_sync->cleanup();
            }

            vulnity_log('[Vulnity] Runtime cleanup completed without deleting data. Reason: ' . $reason);
            return;
        }

        // Best-effort unpair notification before uninstall cleanup.
        $unpair_result = $this->unpair_plugin($reason);

        if ($unpair_result['success']) {
            vulnity_log('[Vulnity] Successfully notified SIEM before uninstall cleanup');
        } else {
            vulnity_log('[Vulnity] Warning: Could not notify SIEM before uninstall cleanup: ' . ($unpair_result['error'] ?? 'Unknown error'));
        }
        
        // Full cleanup for uninstall only.
        delete_option('vulnity_config');
        delete_option('vulnity_alerts');
        delete_option('vulnity_alerts_unread');

        if ($this->inventory_sync instanceof Vulnity_Inventory_Sync) {
            $this->inventory_sync->cleanup();
        }

        if ($this->mitigation_manager instanceof Vulnity_Mitigation_Manager) {
            $this->mitigation_manager->cleanup();
        }

        vulnity_log('[Vulnity] Full plugin cleanup completed');
    }

    public function force_logout($reason = 'heartbeat_failure') {
        vulnity_log('[Vulnity] Forced logout requested - automatic disconnects are disabled. Reason: ' . $reason);

        $config = get_option('vulnity_config');

        if (is_array($config)) {
            $config['status'] = 'active';
            $config['last_forced_logout_attempt'] = array(
                'reason' => $reason,
                'recorded_at' => current_time('mysql')
            );
            unset($config['forced_logout_reason'], $config['forced_logout_at']);

            update_option('vulnity_config', $config);
        }

        do_action('vulnity_forced_logout_blocked', $reason);
    }

    public function get_status() {
        $config = get_option('vulnity_config');
        if (empty($config) || empty($config['site_id']) || empty($config['token'])) {
            return 'not_configured';
        }
        return 'paired';
    }

    // -----------------------------------------------------------------------
    // Remote disconnect — triggered by SIEM or invalid-token detection
    // -----------------------------------------------------------------------

    /**
     * Display a one-time admin notice after a remote disconnect.
     * The notice is stored as an option so it survives until an admin sees it.
     */
    public function show_remote_disconnect_notice() {
        $notice = get_option('vulnity_remote_disconnect_notice');
        if (!$notice || !is_array($notice)) {
            return;
        }

        delete_option('vulnity_remote_disconnect_notice');
        ?>
        <div class="notice notice-warning is-dismissible vulnity-visual-text">
            <p>
                <strong>Vulnity Security:</strong>
                <?php echo esc_html__('This site was removed from the SIEM dashboard. The plugin disconnected automatically. You can pair it again whenever you need.', 'vulnity'); ?>
            </p>
        </div>
        <?php
    }

    /**
     * Perform a remote disconnect: wipe local config, stop crons, store notice.
     * Called either from the receive-mitigation REST endpoint or from
     * check_heartbeat() when a 401 (token invalidated) is received.
     *
     * @param array $body Payload from SIEM (may contain 'reason' and 'message').
     */
    public function perform_remote_disconnect($body = array()) {
        $reason  = isset($body['reason'])  ? sanitize_text_field($body['reason'])  : 'site_deleted_from_siem';
        $message = isset($body['message']) ? sanitize_text_field($body['message']) : '';

        vulnity_log('[Vulnity] Remote disconnect triggered. Reason: ' . $reason);

        // Store admin notice before wiping config (no autoload — no performance cost on every request).
        update_option('vulnity_remote_disconnect_notice', array(
            'reason'      => $reason,
            'message'     => $message,
            'received_at' => current_time('mysql'),
        ), false);

        // Stop scheduled crons.
        if ($this->inventory_sync instanceof Vulnity_Inventory_Sync) {
            $this->inventory_sync->cleanup();
        }
        $this->cancel_siem_crons();

        // Wipe local credentials (token, site_id, signing_secret, …).
        delete_option('vulnity_config');

        vulnity_log('[Vulnity] Remote disconnect completed — config deleted, crons cleared.');
    }

    /**
     * Cancel all cron hooks that require an active SIEM connection.
     * Called on both manual unpair and remote disconnect.
     */
    private function cancel_siem_crons() {
        $hooks = array(
            'vulnity_check_heartbeat',
            'vulnity_sync_mitigation_config',
            'vulnity_process_retry_queue',
            'vulnity_flush_alert_buffer',
        );
        foreach ($hooks as $hook) {
            wp_unschedule_hook($hook);
        }
    }

    // -----------------------------------------------------------------------
    // REST API — bidirectional SIEM ↔ Plugin endpoints
    // -----------------------------------------------------------------------

    public function register_rest_routes() {
        // SIEM requests current site state
        register_rest_route('vulnity/v1', '/on-demand-sync', array(
            'methods'             => 'POST',
            'callback'            => array($this, 'handle_on_demand_sync'),
            'permission_callback' => array($this, 'verify_siem_request'),
        ));
        // SIEM pushes mitigation changes to the plugin
        register_rest_route('vulnity/v1', '/receive-mitigation', array(
            'methods'             => 'POST',
            'callback'            => array($this, 'handle_receive_mitigation'),
            'permission_callback' => array($this, 'verify_siem_request'),
        ));
        // SIEM requests current hardening state (lightweight sync)
        register_rest_route('vulnity/v1', '/hardening-state', array(
            'methods'             => 'GET',
            'callback'            => array($this, 'handle_hardening_state'),
            'permission_callback' => array($this, 'verify_siem_request'),
        ));
    }

    public function verify_siem_request(WP_REST_Request $request) {
        $config = get_option('vulnity_config');

        if (empty($config['site_id']) || empty($config['token'])) {
            vulnity_log('[Vulnity] verify_siem_request: config missing (site_id or token empty)');
            return false;
        }

        $incoming_token   = $request->get_header('x-vulnity-token');
        $incoming_site_id = $request->get_header('x-site-id');
        $incoming_sig     = $request->get_header('x-signature');

        // Diagnostic log — prefix + length only, no full secrets
        vulnity_log(sprintf(
            '[Vulnity] SIEM auth attempt: incoming_site_id=%s token_len=%d stored_token_len=%d token_prefix=%s sig_present=%s',
            (string) $incoming_site_id,
            strlen((string) $incoming_token),
            strlen($config['token']),
            substr((string) $incoming_token, 0, 8),
            $incoming_sig ? 'yes' : 'no'
        ));

        // Method 1: Token-based auth (primary — x-vulnity-token + x-site-id)
        if (
            hash_equals($config['token'],   (string) $incoming_token) &&
            hash_equals($config['site_id'], (string) $incoming_site_id)
        ) {
            return true;
        }

        // Method 2: HMAC signature fallback (x-signature + x-site-id using signing_secret)
        if (
            !empty($incoming_sig) &&
            !empty($config['signing_secret']) &&
            hash_equals($config['site_id'], (string) $incoming_site_id)
        ) {
            $body     = $request->get_body();
            $expected = base64_encode(hash_hmac('sha256', $body, $config['signing_secret'], true));
            if (hash_equals($expected, (string) $incoming_sig)) {
                vulnity_log('[Vulnity] verify_siem_request: authenticated via HMAC signature');
                return true;
            }
            vulnity_log('[Vulnity] verify_siem_request: HMAC mismatch');
        }

        vulnity_log('[Vulnity] verify_siem_request: all auth methods failed');
        return false;
    }

    public function handle_on_demand_sync(WP_REST_Request $request) {
        $config = get_option('vulnity_config');

        // -- Inventory --
        $inventory = $this->inventory_sync->get_inventory_data('siem_pull');
        unset($inventory['site_id'], $inventory['scan_type']);

        // -- Mitigation --
        $raw_blocked = get_option('vulnity_blocked_ips', array());
        $blocked_ips = array();
        foreach ($raw_blocked as $ip => $data) {
            $blocked_ips[] = array(
                'ip'         => $ip,
                'reason'     => isset($data['reason'])     ? $data['reason']     : '',
                'blocked_at' => isset($data['blocked_at']) ? $data['blocked_at'] : '',
                'expires_at' => isset($data['expires_at']) ? $data['expires_at'] : ( isset($data['blocked_until']) ? $data['blocked_until'] : null ),
            );
        }
        $whitelist_ips = array_values(get_option('vulnity_ip_whitelist', array()));

        $hardening_settings = Vulnity_Static_Security::get_instance()->get_settings();

        vulnity_log('[Vulnity] on-demand-sync called by SIEM');

        return new WP_REST_Response(array(
            'success'      => true,
            'site_id'      => $config['site_id'],
            'requested_at' => current_time('mysql'),
            'data'         => array(
                'inventory' => $inventory,
                'mitigation' => array(
                    'blocked_ips'   => $blocked_ips,
                    'whitelist_ips' => $whitelist_ips,
                ),
                'hardening'  => $hardening_settings,
            ),
        ), 200);
    }

    public function handle_receive_mitigation(WP_REST_Request $request) {
        $body   = $request->get_json_params();
        $action = isset($body['action']) ? sanitize_text_field($body['action']) : '';
        $mm     = $this->mitigation_manager;

        switch ($action) {
            case 'push_rules':
            case 'push_full_config':
                $result = $mm->apply_push_rules($body);
                break;

            case 'push_block':
                $ip         = isset($body['ip'])         ? sanitize_text_field($body['ip'])         : '';
                $reason     = isset($body['reason'])     ? sanitize_text_field($body['reason'])     : 'SIEM block';
                $duration   = isset($body['duration'])   ? sanitize_text_field($body['duration'])   : null;
                $expires_at = isset($body['expires_at']) ? sanitize_text_field($body['expires_at']) : null;
                $result     = $mm->apply_push_block($ip, $reason, $duration, $expires_at);
                break;

            case 'push_unblock':
                $ip     = isset($body['ip']) ? sanitize_text_field($body['ip']) : '';
                $result = $mm->apply_push_unblock($ip);
                break;

            case 'push_whitelist_add':
                $ip     = isset($body['ip']) ? sanitize_text_field($body['ip']) : '';
                $result = $mm->apply_whitelist_add($ip);
                break;

            case 'push_whitelist_remove':
                $ip     = isset($body['ip']) ? sanitize_text_field($body['ip']) : '';
                $result = $mm->apply_whitelist_remove($ip);
                break;

            case 'push_hardening':
                $settings_data = isset( $body['settings'] ) && is_array( $body['settings'] )
                    ? $body['settings']
                    : array();
                if ( empty( $settings_data ) ) {
                    return new WP_REST_Response( array(
                        'success' => false,
                        'message' => 'Missing or invalid settings payload',
                    ), 400 );
                }
                $result = $this->apply_push_hardening( $settings_data );
                break;

            case 'remote_disconnect':
                $this->perform_remote_disconnect($body);
                vulnity_log('[Vulnity] receive-mitigation action=remote_disconnect result=ok');
                return new WP_REST_Response(array(
                    'success' => true,
                    'action'  => 'remote_disconnect',
                    'message' => 'Plugin disconnected successfully',
                ), 200);

            default:
                return new WP_REST_Response(array(
                    'success' => false,
                    'message' => 'Unknown action: ' . $action,
                ), 400);
        }

        $result_status = is_wp_error($result)
            ? 'error:' . $result->get_error_code()
            : ($result ? 'ok' : 'error');

        vulnity_log( '[Vulnity] receive-mitigation action=' . $action . ' result=' . $result_status );

        if (is_wp_error($result)) {
            return new WP_REST_Response(array(
                'success' => false,
                'applied' => false,
                'message' => $result->get_error_message(),
                'code' => $result->get_error_code(),
            ), 400);
        }

        if ($result) {
            return new WP_REST_Response(array(
                'success' => true,
                'applied' => true,
                'message' => 'Configuration updated',
            ), 200);
        }

        return new WP_REST_Response(array(
            'success' => false,
            'applied' => false,
            'message' => 'Failed to apply configuration',
        ), 500);
    }

    public function handle_hardening_state( WP_REST_Request $request ) {
        $config   = get_option( 'vulnity_config' );
        $settings = Vulnity_Static_Security::get_instance()->get_settings();

        vulnity_log( '[Vulnity] hardening-state requested by SIEM' );

        return new WP_REST_Response( array(
            'success'      => true,
            'site_id'      => isset( $config['site_id'] ) ? $config['site_id'] : '',
            'requested_at' => current_time( 'mysql' ),
            'hardening'    => $settings,
        ), 200 );
    }

    private function apply_push_hardening( array $settings_data ) {
        $static_security = Vulnity_Static_Security::get_instance();
        $applied         = $static_security->apply_remote_settings( $settings_data );

        if ( is_wp_error( $applied ) ) {
            vulnity_log(
                '[Vulnity] Hardening settings push rejected: ' .
                $applied->get_error_code() .
                ' - ' .
                $applied->get_error_message()
            );
            return $applied;
        }

        if ( $applied !== false ) {
            vulnity_log( '[Vulnity] Hardening settings updated by SIEM: ' . wp_json_encode( $applied ) );
            return true;
        }

        vulnity_log( '[Vulnity] Hardening settings push failed or no changes detected' );
        return false;
    }
}
