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

class Vulnity_SIEM_Connector {
    
    private static $instance = null;
    private $api_base_url = VULNITY_BASE_URL;
    
    public static function get_instance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * Send alert to SIEM with anti-collapse protection
     */
    public function send_alert($alert) {
        // Aplicar filtro anti-colapso
        $alert = apply_filters('vulnity_before_send_alert', $alert);
        
        // Si el filtro devuelve false, la alerta fue agregada/deduplicada
        if ($alert === false) {
            return array('success' => true, 'data' => array('aggregated' => true));
        }
        
        $config = get_option('vulnity_config');
        
        if (empty($config['site_id']) || empty($config['token'])) {
            vulnity_log('[Vulnity] Cannot send alert to SIEM - missing configuration');
            return array('success' => false, 'error' => 'Missing configuration');
        }
        
        // Determine the correct endpoint and format based on alert type
        $endpoint = $this->get_endpoint_for_alert($alert['type']);

        if ($endpoint === null) {
            vulnity_log('[Vulnity] Skipping SIEM alert for blocked type: ' . $alert['type']);

            return array(
                'success' => true,
                'data' => array(
                    'skipped' => true,
                    'reason' => 'blocked_alert_type',
                    'alert_type' => $alert['type']
                )
            );
        }

        $formatted_payload = $this->format_alert_for_endpoint($alert, $endpoint);
        
        // Headers with token authentication
        $headers = array(
            'Content-Type' => 'application/json',
            'x-vulnity-token' => $config['token'],
            'x-site-id' => $config['site_id']
        );
        
        $args = array(
            'method' => 'POST',
            'headers' => $headers,
            'body' => wp_json_encode($formatted_payload),
            'timeout' => 30,
            'sslverify' => true,
            'redirection' => 5,
            'httpversion' => '1.1',
            'blocking' => true
        );
        
        $full_url = $this->api_base_url . $endpoint;
        
        vulnity_log('[Vulnity] Sending alert to: ' . $full_url);
        vulnity_log('[Vulnity] Alert payload: ' . wp_json_encode($formatted_payload));
        
        $response = wp_remote_post($full_url, $args);
        
        if (is_wp_error($response)) {
            $error_message = $response->get_error_message();
            vulnity_log('[Vulnity] SIEM alert request failed: ' . $error_message);
            
            if (strpos($error_message, 'cURL error 28') !== false || strpos($error_message, 'timeout') !== false) {
                return array('success' => false, 'error' => 'Timeout - will retry', 'retry' => true);
            }
            
            return array('success' => false, 'error' => $error_message);
        }
        
        $status_code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        
        vulnity_log('[Vulnity] SIEM response status: ' . $status_code);
        vulnity_log('[Vulnity] SIEM response body: ' . $body);
        
        $response_data = json_decode($body, true);
        
        if ($status_code === 200 || $status_code === 201) {
            vulnity_log('[Vulnity] Alert sent to SIEM successfully: ' . $alert['type'] . ' (ID: ' . $alert['id'] . ')');
            return array(
                'success' => true, 
                'data' => $response_data,
                'alert_id' => isset($response_data['alert_id']) ? $response_data['alert_id'] : null,
                'siem_id' => isset($response_data['id']) ? $response_data['id'] : null
            );
        } else if ($status_code === 401) {
            vulnity_log('[Vulnity] Authentication failed - token may be expired');
            return array('success' => false, 'error' => 'Authentication failed', 'retry' => false);
        } else if ($status_code === 404) {
            // Try generic endpoint as fallback
            if ($endpoint !== '/generic-alert') {
                vulnity_log('[Vulnity] Endpoint not found, trying generic endpoint');
                return $this->send_to_generic_endpoint($alert, $headers);
            }
            return array('success' => false, 'error' => 'Endpoint not found', 'retry' => false);
        } else if ($status_code >= 500) {
            vulnity_log('[Vulnity] Server error - will retry');
            return array('success' => false, 'error' => 'Server error: ' . $status_code, 'retry' => true);
        } else {
            $error_message = isset($response_data['error']) ? $response_data['error'] : 'HTTP ' . $status_code;
            vulnity_log('[Vulnity] SIEM alert failed: ' . $error_message);
            return array('success' => false, 'error' => $error_message, 'data' => $response_data);
        }
    }
    
    /**
     * Format alert payload according to endpoint requirements
     */
    private function format_alert_for_endpoint($alert, $endpoint) {
        // Check if alert is aggregated or deduplicated (from anti-collapse system)
        if (isset($alert['details']['aggregated']) && $alert['details']['aggregated']) {
            return $this->format_aggregated_alert($alert);
        }
        
        if (isset($alert['details']['deduplicated']) && $alert['details']['deduplicated']) {
            return $this->format_deduplicated_alert($alert);
        }
        
        // Special handling for system alerts (panic mode, recovery)
        if (in_array($alert['type'], array('system_panic', 'system_recovery'))) {
            return $this->format_system_alert($alert);
        }
        
        // Special handling for brute-force-alert
        if ($endpoint === '/brute-force-alert') {
            return $this->format_brute_force_alert($alert);
        }
        
        // Special handling for plugin-change-alert
        if ($endpoint === '/plugin-change-alert') {
            return $this->format_plugin_change_alert($alert);
        }
        
        // Special handling for user management alerts
        if ($endpoint === '/manage-user') {
            return $this->format_user_management_alert($alert);
        }
        
        // For all other endpoints, use standard format
        return $this->format_standard_alert($alert);
    }
    
    /**
     * Format aggregated alerts from anti-collapse system
     */
    private function format_aggregated_alert($alert) {
        return array(
            'alert_type' => 'aggregated_events',
            'severity' => $alert['severity'],
            'title' => $alert['title'],
            'message' => $alert['message'],
            'details' => array(
                'event_type' => $alert['type'],
                'event_count' => $alert['details']['event_count'],
                'unique_sources' => $alert['details']['unique_sources'],
                'duration' => $alert['details']['duration'],
                'severity_breakdown' => $alert['details']['severity_breakdown'],
                'sample_event' => $alert['details']['sample_event']
            ),
            'site_id' => $this->get_site_id(),
            'aggregation' => true
        );
    }
    
    /**
     * Format deduplicated alerts from anti-collapse system
     */
    private function format_deduplicated_alert($alert) {
        return array(
            'alert_type' => 'deduplicated_events',
            'severity' => $alert['severity'],
            'title' => $alert['title'],
            'message' => $alert['message'],
            'details' => array(
                'original_type' => $alert['type'],
                'duplicate_count' => $alert['details']['count'],
                'duration' => $alert['details']['duration'],
                'original_details' => $alert['details']['original_details']
            ),
            'site_id' => $this->get_site_id(),
            'deduplication' => true
        );
    }
    
    /**
     * Format system alerts (panic mode, recovery)
     */
    private function format_system_alert($alert) {
        return array(
            'alert_type' => 'system_notification',
            'severity' => $alert['severity'],
            'title' => $alert['title'],
            'message' => $alert['message'],
            'details' => $alert['details'],
            'site_id' => $this->get_site_id(),
            'system_event' => true
        );
    }
    
    /**
     * Format user management alert with specific structure
     */
    private function format_user_management_alert($alert) {
        $details = $alert['details'];
        
        // Map the action to alert_type
        $action_to_type = array(
            'user_created' => 'user_created',
            'user_deleted' => 'user_deleted',
            'role_elevated' => 'user_role_changed',
            'role_degraded' => 'user_role_changed'
        );
        
        $action = isset($details['action']) ? $details['action'] : 'user_management';
        $alert_type = isset($action_to_type[$action]) ? $action_to_type[$action] : 'user_management';
        
        // Build the payload with all required fields
        $payload = array(
            'alert_type' => $alert_type,
            'severity' => $alert['severity'],
            'title' => $alert['title'],
            'message' => $alert['message'],
            'details' => array(
                'action' => $action,
                'user_id' => isset($details['user_id']) ? intval($details['user_id']) : (isset($details['deleted_user_id']) ? intval($details['deleted_user_id']) : 0),
                'user_login' => isset($details['user_login']) ? $details['user_login'] : '',
                'user_email' => isset($details['user_email']) ? $details['user_email'] : '',
                'user_roles' => isset($details['user_roles']) ? $details['user_roles'] : (isset($details['new_roles']) ? $details['new_roles'] : array()),
                'created_by' => isset($details['created_by']) ? $details['created_by'] : (isset($details['elevated_by']) ? $details['elevated_by'] : (isset($details['degraded_by']) ? $details['degraded_by'] : (isset($details['deleted_by']) ? $details['deleted_by'] : ''))),
                'created_by_id' => isset($details['created_by_id']) ? intval($details['created_by_id']) : (isset($details['elevated_by_id']) ? intval($details['elevated_by_id']) : (isset($details['degraded_by_id']) ? intval($details['degraded_by_id']) : (isset($details['deleted_by_id']) ? intval($details['deleted_by_id']) : 0))),
                'created_from_ip' => isset($details['created_from_ip']) ? $details['created_from_ip'] : (isset($details['elevated_from_ip']) ? $details['elevated_from_ip'] : (isset($details['degraded_from_ip']) ? $details['degraded_from_ip'] : (isset($details['deleted_from_ip']) ? $details['deleted_from_ip'] : ''))),
                'timestamp' => isset($details['timestamp']) ? $details['timestamp'] : current_time('mysql')
            ),
            'site_id' => $this->get_site_id()
        );
        
        // Add role-specific fields if it's a role change
        if (in_array($action, array('role_elevated', 'role_degraded'))) {
            $payload['details']['old_roles'] = isset($details['old_roles']) ? $details['old_roles'] : array();
            $payload['details']['new_roles'] = isset($details['new_roles']) ? $details['new_roles'] : array();
            $payload['details']['old_weight'] = isset($details['old_weight']) ? $details['old_weight'] : 0;
            $payload['details']['new_weight'] = isset($details['new_weight']) ? $details['new_weight'] : 0;
        }
        
        return $payload;
    }
    
    /**
     * Format brute force alert with specific structure
     */
    private function format_brute_force_alert($alert) {
        $details = $alert['details'];
        
        // Extract data from details and put at root level as expected
        return array(
            'site_id' => $this->get_site_id(),
            'ip_address' => isset($details['ip']) ? $details['ip'] : (isset($details['ip_address']) ? $details['ip_address'] : 'unknown'),
            'username' => isset($details['usernames']) && is_array($details['usernames']) && !empty($details['usernames']) 
                ? $details['usernames'][0] 
                : (isset($details['username']) ? $details['username'] : ''),
            'total_attempts' => isset($details['total_attempts']) ? intval($details['total_attempts']) : 0,
            'recent_attempts' => isset($details['recent_attempts']) ? intval($details['recent_attempts']) : (isset($details['total_attempts']) ? intval($details['total_attempts']) : 0),
            'attack_duration_seconds' => isset($details['duration_seconds']) ? intval($details['duration_seconds']) : 0,
            
            // Include standard fields for compatibility
            'alert_type' => 'brute_force_attack',
            'severity' => $alert['severity'],
            'title' => $alert['title'],
            'message' => $alert['message'],
            'details' => $details
        );
    }
    
    /**
     * Format plugin change alert with correct alert_type
     */
    private function format_plugin_change_alert($alert) {
        $details = $alert['details'];
        
        // Map action to correct alert_type
        $action_map = array(
            'activated' => 'plugin_activated',
            'deactivated' => 'plugin_deactivated',
            'installed' => 'plugin_installed',
            'deleted' => 'plugin_deleted',
            'updated' => 'plugin_updated'
        );
        
        $action = isset($details['action']) ? $details['action'] : 'updated';
        $mapped_type = isset($action_map[$action]) ? $action_map[$action] : 'plugin_updated';
        
        return array(
            'alert_type' => $mapped_type,  // Use mapped type instead of 'plugin_change'
            'severity' => $alert['severity'],
            'title' => $alert['title'],
            'message' => $alert['message'],
            'details' => $details,
            'site_id' => $this->get_site_id()
        );
    }
    
    /**
     * Format standard alert for most endpoints
     */
    private function format_standard_alert($alert) {
        // Map WordPress alert types to SIEM expected types
        $type_map = array(
            'admin_created' => 'admin_created',
            'permission_elevated' => 'permission_elevated',
            'file_editor_used' => 'file_editor_used',
            'php_file_uploaded' => 'php_file_uploaded',
            'theme_change' => 'theme_change',
            'core_updated' => 'core_updated',
            'suspicious_query' => 'suspicious_query',
            'mitigation_action' => 'system_notification',
            'test_connection' => 'test_connection',
            'scanner_detected' => 'scanner_detected'
        );
        
        $alert_type = isset($type_map[$alert['type']]) ? $type_map[$alert['type']] : $alert['type'];
        
        return array(
            'alert_type' => $alert_type,
            'severity' => $alert['severity'],
            'title' => $alert['title'],
            'message' => $alert['message'],
            'details' => isset($alert['details']) ? $alert['details'] : array(),
            'site_id' => $this->get_site_id()
        );
    }

    private function get_site_id() {
        $config = get_option('vulnity_config');
        if (!is_array($config)) {
            return '';
        }

        return isset($config['site_id']) ? (string) $config['site_id'] : '';
    }
    
    /**
     * Send to generic endpoint as fallback
     */
    private function send_to_generic_endpoint($alert, $headers) {
        // Generic endpoint is more flexible, so we can send more data
        $generic_payload = array(
            'alert_type' => $this->map_to_generic_type($alert['type']),
            'severity' => $alert['severity'],
            'title' => $alert['title'],
            'message' => $alert['message'],
            'details' => isset($alert['details']) ? $alert['details'] : array()
        );
        
        $args = array(
            'method' => 'POST',
            'headers' => $headers,
            'body' => wp_json_encode($generic_payload),
            'timeout' => 30,
            'sslverify' => true
        );
        
        vulnity_log('[Vulnity] Fallback to generic endpoint with payload: ' . wp_json_encode($generic_payload));
        
        $response = wp_remote_post($this->api_base_url . '/generic-alert', $args);
        
        if (is_wp_error($response)) {
            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] Alert sent to generic endpoint successfully');
            return array('success' => true, 'data' => $response_data);
        } else {
            return array('success' => false, 'error' => 'Generic endpoint failed: HTTP ' . $status_code);
        }
    }
    
    /**
     * Map alert type to generic types
     */
    private function map_to_generic_type($alert_type) {
        // Map to valid generic alert types
        $generic_map = array(
            'brute_force' => 'unauthorized_access',
            'php_file_uploaded' => 'malware_detected',
            'admin_created' => 'unauthorized_access',
            'permission_elevated' => 'security_violation',
            'user_management' => 'custom_alert',
            'file_editor_used' => 'suspicious_activity',
            'plugin_change' => 'custom_alert',
            'theme_change' => 'custom_alert',
            'core_updated' => 'custom_alert',
            'suspicious_query' => 'suspicious_query',
            'mitigation_action' => 'system_notification',
            'system_panic' => 'system_notification',
            'system_recovery' => 'system_notification'
        );
        
        return isset($generic_map[$alert_type]) ? $generic_map[$alert_type] : 'custom_alert';
    }
    
    /**
     * Get the appropriate endpoint for each alert type
     */
    private function get_endpoint_for_alert($alert_type) {
        $blocked_alerts = array(
            'heartbeat_failure',
        );

        if (in_array($alert_type, $blocked_alerts, true)) {
            return null;
        }

        $endpoints = array(
            'brute_force' => '/brute-force-alert',
            'php_file_uploaded' => '/file-security-alert',
            'user_management' => '/manage-user',
            'admin_created' => '/user-management-alert',
            'permission_elevated' => '/permission-change-alert',
            'file_editor_used' => '/file-editor-alert',
            'plugin_change' => '/plugin-change-alert',
            'theme_change' => '/theme-change-alert',
            'core_updated' => '/core-update-alert',
            'suspicious_query' => '/suspicious-query-alert',
            'test_connection' => '/generic-alert',
            'scanner_detected' => '/scanner-detected-alert',
            'system_panic' => '/generic-alert',
            'system_recovery' => '/generic-alert'
        );
        
        return isset($endpoints[$alert_type]) ? $endpoints[$alert_type] : '/generic-alert';
    }

    /**
     * Execute a lightweight SIEM connection test
     */
    public function test_connection() {
        $config = get_option('vulnity_config');

        if (empty($config['site_id']) || empty($config['token'])) {
            return array('success' => false, 'error' => 'Missing configuration');
        }

        $headers = array(
            'Content-Type' => 'application/json',
            'x-vulnity-token' => $config['token'],
            'x-site-id' => $config['site_id']
        );

        $payload = array(
            'timestamp' => current_time('c'),
            'plugin_version' => VULNITY_VERSION,
            'wordpress_version' => get_bloginfo('version'),
        );

        $args = array(
            'method' => 'POST',
            'headers' => $headers,
            'body' => wp_json_encode($payload),
            'timeout' => 15,
            'sslverify' => true,
        );

        $endpoint = $this->api_base_url . '/connection-test';

        vulnity_log('[Vulnity] Testing SIEM connection at: ' . $endpoint);

        $response = wp_remote_post($endpoint, $args);

        if (is_wp_error($response)) {
            $error_message = $response->get_error_message();
            vulnity_log('[Vulnity] Connection test failed: ' . $error_message);
            return array('success' => false, 'error' => $error_message);
        }

        $status_code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);

        vulnity_log('[Vulnity] Connection test status: ' . $status_code);
        vulnity_log('[Vulnity] Connection test body: ' . $body);

        $response_data = json_decode($body, true);

        if ($status_code !== 200) {
            $error_message = isset($response_data['error']) ? $response_data['error'] : 'HTTP ' . $status_code;
            return array('success' => false, 'error' => $error_message);
        }

        if (is_array($response_data)) {
            if (isset($response_data['success']) && !$response_data['success']) {
                $error_message = isset($response_data['error']) ? $response_data['error'] : 'Connection test failed';
                return array('success' => false, 'error' => $error_message);
            }

            if (isset($response_data['message'])) {
                return array('success' => true, 'message' => $response_data['message']);
            }
        }

        return array('success' => true);
    }

    /**
     * Send an immediate heartbeat to SIEM (fire-and-forget).
     * Used on pair ('online') and unpair ('offline') to update the
     * Domains panel in real time without waiting for the cron window.
     *
     * @param string $status 'online' | 'offline'
     */
    public function send_heartbeat($status = 'online') {
        $config = get_option('vulnity_config');

        if (empty($config['site_id']) || empty($config['token'])) {
            vulnity_log('[Vulnity] send_heartbeat: missing config, skipping.');
            return array('success' => false, 'error' => 'Missing configuration');
        }

        $theme            = function_exists('wp_get_theme') ? wp_get_theme() : null;
        $timezone_string  = function_exists('wp_timezone_string') ? wp_timezone_string() : get_option('timezone_string');

        $system_info = array(
            'site_id'          => $config['site_id'],
            'site_url'         => get_site_url(),
            'home_url'         => home_url(),
            'site_name'        => get_bloginfo('name'),
            'language'         => get_locale(),
            'timezone'         => $timezone_string ?: date_default_timezone_get(),
            'multisite'        => is_multisite(),
            'theme'            => $theme ? $theme->get('Name') : null,
            'theme_version'    => $theme ? $theme->get('Version') : null,
            'php_memory_limit' => ini_get('memory_limit'),
            'server'           => php_sapi_name(),
            'timestamp'        => current_time('c'),
        );

        $body = array(
            'status'            => $status,
            'plugin_version'    => VULNITY_VERSION,
            'wordpress_version' => get_bloginfo('version'),
            'php_version'       => PHP_VERSION,
            'system_info'       => $system_info,
        );

        $args = array(
            'method'   => 'POST',
            'headers'  => array(
                'Content-Type'    => 'application/json',
                'x-vulnity-token' => $config['token'],
                'x-site-id'       => $config['site_id'],
            ),
            'body'      => wp_json_encode($body),
            'timeout'   => 8,
            'sslverify' => true,
            'blocking'  => false,
        );

        vulnity_log('[Vulnity] Sending heartbeat: status=' . $status);
        wp_remote_post($this->api_base_url . '/heartbeat', $args);

        return array('success' => true);
    }

    /**
     * Send inventory to SIEM (uses HMAC signature authentication)
     */
    public function send_inventory($inventory) {
        $config = get_option('vulnity_config');
        
        if (empty($config['site_id']) || empty($config['signing_secret'])) {
            vulnity_log('[Vulnity] Cannot send inventory - missing site_id or signing_secret');
            return array('success' => false, 'error' => 'Missing configuration');
        }
        
        $body_json = wp_json_encode($inventory);
        $signature = base64_encode(hash_hmac('sha256', $body_json, $config['signing_secret'], true));
        
        $headers = array(
            'Content-Type' => 'application/json',
            'x-signature' => $signature,
            'x-site-id' => $config['site_id'],
            'x-plugin-version' => VULNITY_VERSION
        );
        
        $args = array(
            'method' => 'POST',
            'headers' => $headers,
            'body' => $body_json,
            'timeout' => 30,
            'sslverify' => true
        );
        
        $response = wp_remote_post($this->api_base_url . '/scan-site-info', $args);
        
        if (is_wp_error($response)) {
            vulnity_log('[Vulnity] Inventory sync request failed: ' . $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] Inventory sent to SIEM successfully');
            return array('success' => true, 'data' => $response_data);
        } else {
            vulnity_log('[Vulnity] Inventory sync failed with status ' . $status_code . ' - Body: ' . $body);
            return array('success' => false, 'error' => 'HTTP ' . $status_code, 'data' => $response_data);
        }
    }
}

