<?php
/**
 * Logging and Analytics Service
 * 
 * Handles event logging, metrics tracking, and local analytics calculation.
 * Local-first architecture:
 * - All events stored in local WordPress database
 * - Analytics calculated from local data
 * - Background sync to server every 24 hours
 *
 * @package VMPFence
 * @since 2.0.0
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * VMPFence Logging Service Class
 *
 * @since 2.0.0
 */
class VMPFence_Logging_Service {

    /**
     * Singleton instance
     *
     * @var VMPFence_Logging_Service
     */
    private static $instance = null;

    /**
     * Database instance
     *
     * @var VMPFence_Database
     */
    private $database;

    /**
     * Events table name
     *
     * @var string
     */
    private $events_table;

    /**
     * Analytics table name
     *
     * @var string
     */
    private $analytics_table;

    /**
     * Get singleton instance
     *
     * @since 2.0.0
     * @return VMPFence_Logging_Service
     */
    public static function get_instance() {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Constructor
     *
     * @since 2.0.0
     */
    private function __construct() {
        global $wpdb;
        
        // Load database instance
        require_once VMPFENCE_PLUGIN_DIR . 'includes/class-database.php';
        $this->database = new VMPFence_Logging_Database();
        $this->events_table = $wpdb->prefix . 'vmpfence_api_logs';
        $this->analytics_table = $wpdb->prefix . 'vmpfence_analytics';
    }

    // ============================================================================
    // EVENT LOGGING METHODS
    // ============================================================================

    /**
     * Log a security event
     *
     * @since 2.0.0
     * @param string $event_type Event type (e.g., 'malware_detected', 'firewall_block')
     * @param string $severity Severity level: info, warning, error, critical
     * @param array  $metadata Additional event data
     * @param string $category Optional category/module (e.g., 'firewall', 'scanner')
     * @return int|false Event ID on success, false on failure
     */
    public function log_event($event_type, $severity = 'info', $metadata = array(), $category = null) {
        global $wpdb;
        
        $api_key = VMPFence_Config::get('central_api_key', '');
        
        if (empty($api_key)) {
            $api_key = 'unknown';
        }

        // Get request context
        $ip_address = $this->get_client_ip();
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : null;
        $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : null;

        // Build message from metadata or use a default
        $message = isset($metadata['message']) ? $metadata['message'] : ucfirst(str_replace('_', ' ', $event_type));
        
        // Add category and version information to metadata
        if ($category) {
            $metadata['category'] = $category;
        }
        
        $metadata = array_merge($metadata, array(
            'plugin_version' => VMPFENCE_VERSION,
            'wp_version' => get_bloginfo('version'),
            'php_version' => phpversion(),
        ));
        
        // Ensure table exists before inserting (self-healing for frontend hooks)
        if (class_exists('VMPFence_Logging_Database') && !VMPFence_Logging_Database::tables_exist()) {
            VMPFence_Logging_Database::ensure_tables_exist();
        }

        // Insert event into database
        $result = $wpdb->insert(
            $this->events_table,
            array(
                'event_type' => sanitize_text_field($event_type),
                'severity' => sanitize_text_field($severity),
                'message' => sanitize_textarea_field($message),
                'ip_address' => $ip_address,
                'user_agent' => $user_agent,
                'request_uri' => $request_uri,
                'metadata' => wp_json_encode($metadata),
                'synced' => 0,
                'created_at' => current_time('mysql'),
                'updated_at' => current_time('mysql')
            ),
            array('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s')
        );

        if ($result === false) {
            VMPFence_Secure_Logger::log('error', 'Failed to insert event into database', array(
                'error' => $wpdb->last_error
            ));
            return false;
        }

        $event_id = $wpdb->insert_id;

        // Update daily analytics in background
        $this->update_daily_analytics(date('Y-m-d'));

        return $event_id;
    }

    // ============================================================================
    // ANALYTICS CALCULATION METHODS
    // ============================================================================

    /**
     * Update daily analytics
     *
     * @since 2.0.0
     * @param string $date Date in Y-m-d format
     * @return bool
     */
    public function update_daily_analytics($date = null) {
        global $wpdb;
        
        if (!$date) {
            $date = date('Y-m-d');
        }

        // Calculate analytics from events
        $analytics = $this->calculate_analytics_for_date($date);

        if (!$analytics) {
            return false;
        }

        // Check if record exists
        $exists = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM {$this->analytics_table} WHERE date = %s",
            $date
        ));

        if ($exists) {
            // Update existing
            $result = $wpdb->update(
                $this->analytics_table,
                array(
                    'total_events' => $analytics['total_events'],
                    'critical_events' => $analytics['events_by_severity']['critical'],
                    'high_events' => $analytics['events_by_severity']['error'], // Map error to high
                    'medium_events' => $analytics['events_by_severity']['warning'], // Map warning to medium
                    'low_events' => 0, // Not tracked separately
                    'info_events' => $analytics['events_by_severity']['info'],
                    'firewall_blocks' => isset($analytics['events_by_type']['firewall_block']) ? $analytics['events_by_type']['firewall_block'] : 0,
                    'malware_detections' => isset($analytics['events_by_type']['malware_detected']) ? $analytics['events_by_type']['malware_detected'] : 0,
                    'unique_ips' => $analytics['unique_ips'],
                    'unique_attackers' => $analytics['unique_attackers'],
                    'event_types' => wp_json_encode($analytics['events_by_type']),
                    'top_ips' => wp_json_encode($analytics['top_attacked_urls']), // Reuse this field for top IPs
                    'updated_at' => current_time('mysql')
                ),
                array('date' => $date),
                array('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%s'),
                array('%s')
            );
        } else {
            // Insert new
            $result = $wpdb->insert(
                $this->analytics_table,
                array(
                    'date' => $date,
                    'total_events' => $analytics['total_events'],
                    'critical_events' => $analytics['events_by_severity']['critical'],
                    'high_events' => $analytics['events_by_severity']['error'], // Map error to high
                    'medium_events' => $analytics['events_by_severity']['warning'], // Map warning to medium
                    'low_events' => 0, // Not tracked separately
                    'info_events' => $analytics['events_by_severity']['info'],
                    'firewall_blocks' => isset($analytics['events_by_type']['firewall_block']) ? $analytics['events_by_type']['firewall_block'] : 0,
                    'malware_detections' => isset($analytics['events_by_type']['malware_detected']) ? $analytics['events_by_type']['malware_detected'] : 0,
                    'unique_ips' => $analytics['unique_ips'],
                    'unique_attackers' => $analytics['unique_attackers'],
                    'event_types' => wp_json_encode($analytics['events_by_type']),
                    'top_ips' => wp_json_encode($analytics['top_attacked_urls']), // Reuse this field for top IPs
                    'created_at' => current_time('mysql'),
                    'updated_at' => current_time('mysql')
                ),
                array('%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%s', '%s')
            );
        }

        return $result !== false;
    }

    /**
     * Calculate analytics for a specific date
     *
     * @since 2.0.0
     * @param string $date Date in Y-m-d format
     * @return array|false
     */
    private function calculate_analytics_for_date($date) {
        global $wpdb;

        $start = $date . ' 00:00:00';
        $end = $date . ' 23:59:59';

        // Total events
        $total_events = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->events_table} 
             WHERE created_at BETWEEN %s AND %s",
            $start, $end
        ));

        // Events by severity
        $severity_counts = $wpdb->get_results($wpdb->prepare(
            "SELECT severity, COUNT(*) as count FROM {$this->events_table} 
             WHERE created_at BETWEEN %s AND %s 
             GROUP BY severity",
            $start, $end
        ), ARRAY_A);

        $events_by_severity = array(
            'critical' => 0,
            'error' => 0,
            'warning' => 0,
            'info' => 0
        );

        foreach ($severity_counts as $row) {
            $events_by_severity[$row['severity']] = (int) $row['count'];
        }

        // Events by type
        $type_counts = $wpdb->get_results($wpdb->prepare(
            "SELECT event_type, COUNT(*) as count FROM {$this->events_table} 
             WHERE created_at BETWEEN %s AND %s 
             GROUP BY event_type",
            $start, $end
        ), ARRAY_A);

        $events_by_type = array();
        foreach ($type_counts as $row) {
            $events_by_type[$row['event_type']] = (int) $row['count'];
        }

        // Threats blocked (firewall_block + malware_detected + ip_blocked)
        $threats_blocked = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->events_table} 
             WHERE created_at BETWEEN %s AND %s 
             AND event_type IN ('firewall_block', 'malware_detected', 'ip_blocked')",
            $start, $end
        ));

        // Unique IPs
        $unique_ips = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT ip_address) FROM {$this->events_table} 
             WHERE created_at BETWEEN %s AND %s 
             AND ip_address IS NOT NULL",
            $start, $end
        ));

        // Unique attackers (IPs with malicious events)
        $unique_attackers = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(DISTINCT ip_address) FROM {$this->events_table} 
             WHERE created_at BETWEEN %s AND %s 
             AND event_type IN ('firewall_block', 'malware_detected', 'ip_blocked', 'login_failed') 
             AND ip_address IS NOT NULL",
            $start, $end
        ));

        // Top attacked URLs
        $top_urls = $wpdb->get_results($wpdb->prepare(
            "SELECT request_uri, COUNT(*) as count FROM {$this->events_table} 
             WHERE created_at BETWEEN %s AND %s 
             AND request_uri IS NOT NULL 
             AND event_type IN ('firewall_block', 'malware_detected')
             GROUP BY request_uri 
             ORDER BY count DESC 
             LIMIT 10",
            $start, $end
        ), ARRAY_A);

        $top_attacked_urls = array();
        foreach ($top_urls as $row) {
            $top_attacked_urls[] = array(
                'url' => $row['request_uri'],
                'count' => (int) $row['count']
            );
        }

        return array(
            'total_events' => $total_events,
            'events_by_severity' => $events_by_severity,
            'events_by_type' => $events_by_type,
            'threats_blocked' => $threats_blocked,
            'unique_ips' => $unique_ips,
            'unique_attackers' => $unique_attackers,
            'top_attacked_urls' => $top_attacked_urls
        );
    }

    /**
     * Get dashboard analytics for period
     *
     * @since 2.0.0
     * @param string $period Period: 'today', 'week', 'month', '7d', '30d', 'all'
     * @return array
     */
    public function get_dashboard_analytics($period = '7d') {
        global $wpdb;

        // Build date condition with proper prepared statements
        $query_params = array();
        switch ($period) {
            case 'today':
                $date_value = date('Y-m-d');
                $query = "SELECT * FROM {$this->analytics_table} WHERE date = %s ORDER BY date DESC";
                $query_params[] = $date_value;
                break;
            case '7d':
            case 'week':
                $date_value = date('Y-m-d', strtotime('-7 days'));
                $query = "SELECT * FROM {$this->analytics_table} WHERE date >= %s ORDER BY date DESC";
                $query_params[] = $date_value;
                break;
            case '30d':
            case 'month':
                $date_value = date('Y-m-d', strtotime('-30 days'));
                $query = "SELECT * FROM {$this->analytics_table} WHERE date >= %s ORDER BY date DESC";
                $query_params[] = $date_value;
                break;
            default:
                $query = "SELECT * FROM {$this->analytics_table} ORDER BY date DESC";
        }

        // Execute query with proper preparation
        if (!empty($query_params)) {
            $results = $wpdb->get_results($wpdb->prepare($query, $query_params), ARRAY_A);
        } else {
            $results = $wpdb->get_results($query, ARRAY_A);
        }

        if (empty($results)) {
            // Fallback: calculate from events directly
            return $this->calculate_analytics_from_events($period);
        }

        // Aggregate results from new schema (individual severity columns)
        $total_events = 0;
        $critical_events = 0;
        $high_events = 0;
        $medium_events = 0;
        $low_events = 0;
        $info_events = 0;
        $firewall_blocks = 0;
        $malware_detections = 0;
        $login_attempts = 0;
        $failed_logins = 0;
        $unique_ips_total = 0;
        $unique_attackers_total = 0;
        $all_event_types = array();
        $all_top_ips = array();

        foreach ($results as $row) {
            $total_events += (int) ($row['total_events'] ?? 0);
            $critical_events += (int) ($row['critical_events'] ?? 0);
            $high_events += (int) ($row['high_events'] ?? 0);
            $medium_events += (int) ($row['medium_events'] ?? 0);
            $low_events += (int) ($row['low_events'] ?? 0);
            $info_events += (int) ($row['info_events'] ?? 0);
            $firewall_blocks += (int) ($row['firewall_blocks'] ?? 0);
            $malware_detections += (int) ($row['malware_detections'] ?? 0);
            $login_attempts += (int) ($row['login_attempts'] ?? 0);
            $failed_logins += (int) ($row['failed_logins'] ?? 0);
            
            // These are max, not sum
            if ((int) ($row['unique_ips'] ?? 0) > $unique_ips_total) {
                $unique_ips_total = (int) $row['unique_ips'];
            }
            if ((int) ($row['unique_attackers'] ?? 0) > $unique_attackers_total) {
                $unique_attackers_total = (int) $row['unique_attackers'];
            }
            
            // Merge event types
            if (!empty($row['event_types'])) {
                $types = json_decode($row['event_types'], true);
                if ($types && is_array($types)) {
                    foreach ($types as $type => $count) {
                        if (!isset($all_event_types[$type])) {
                            $all_event_types[$type] = 0;
                        }
                        // Ensure count is numeric to prevent type errors
                        $all_event_types[$type] += is_numeric($count) ? (int)$count : 0;
                    }
                }
            }
            
            // Merge top IPs
            if (!empty($row['top_ips'])) {
                $ips = json_decode($row['top_ips'], true);
                if ($ips && is_array($ips)) {
                    foreach ($ips as $ip => $count) {
                        if (!isset($all_top_ips[$ip])) {
                            $all_top_ips[$ip] = 0;
                        }
                        // Ensure count is numeric to prevent type errors
                        $all_top_ips[$ip] += is_numeric($count) ? (int)$count : 0;
                    }
                }
            }
        }

        // Build severity array for backward compatibility
        $events_by_severity = array(
            'critical' => $critical_events,
            'high' => $high_events,
            'medium' => $medium_events,
            'low' => $low_events,
            'info' => $info_events
        );

        $return_data = array(
            'total_events' => $total_events,
            'critical_events' => $critical_events,
            'high_events' => $high_events,
            'medium_events' => $medium_events,
            'low_events' => $low_events,
            'info_events' => $info_events,
            'events_by_severity' => $events_by_severity,
            'events_by_type' => $all_event_types,
            'firewall_blocks' => $firewall_blocks,
            'malware_detections' => $malware_detections,
            'login_attempts' => $login_attempts,
            'failed_logins' => $failed_logins,
            'unique_ips' => $unique_ips_total,
            'unique_attackers' => $unique_attackers_total,
            'top_ips' => $all_top_ips
        );
        
        return $return_data;
    }

    /**
     * Calculate analytics directly from events (fallback)
     *
     * @since 2.0.0
     * @param string $period Period identifier
     * @return array
     */
    private function calculate_analytics_from_events($period) {
        global $wpdb;

        // Build date range with proper parameters
        $query_params = array();
        switch ($period) {
            case 'today':
                $start = date('Y-m-d') . ' 00:00:00';
                $end = date('Y-m-d') . ' 23:59:59';
                $date_where = "created_at BETWEEN %s AND %s";
                $query_params = array($start, $end);
                break;
            case 'week':
            case '7d':
                $start = date('Y-m-d', strtotime('-7 days')) . ' 00:00:00';
                $end = date('Y-m-d') . ' 23:59:59';
                $date_where = "created_at BETWEEN %s AND %s";
                $query_params = array($start, $end);
                break;
            case 'month':
            case '30d':
                $start = date('Y-m-d', strtotime('-30 days')) . ' 00:00:00';
                $end = date('Y-m-d') . ' 23:59:59';
                $date_where = "created_at BETWEEN %s AND %s";
                $query_params = array($start, $end);
                break;
            default:
                $date_where = "1=1";
                $query_params = array();
        }

        // Total events
        if (!empty($query_params)) {
            $total_events = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$this->events_table} WHERE {$date_where}",
                $query_params
            ));
        } else {
            $total_events = (int) $wpdb->get_var(
                "SELECT COUNT(*) FROM {$this->events_table}"
            );
        }

        // Events by severity
        if (!empty($query_params)) {
            $severity_counts = $wpdb->get_results($wpdb->prepare(
                "SELECT severity, COUNT(*) as count FROM {$this->events_table} 
                 WHERE {$date_where} GROUP BY severity",
                $query_params
            ), ARRAY_A);
        } else {
            $severity_counts = $wpdb->get_results(
                "SELECT severity, COUNT(*) as count FROM {$this->events_table} GROUP BY severity",
                ARRAY_A
            );
        }

        $events_by_severity = array('critical' => 0, 'error' => 0, 'warning' => 0, 'info' => 0);
        foreach ($severity_counts as $row) {
            $events_by_severity[$row['severity']] = (int) $row['count'];
        }

        // Events by type
        if (!empty($query_params)) {
            $type_counts = $wpdb->get_results($wpdb->prepare(
                "SELECT event_type, COUNT(*) as count FROM {$this->events_table} 
                 WHERE {$date_where} GROUP BY event_type",
                $query_params
            ), ARRAY_A);
        } else {
            $type_counts = $wpdb->get_results(
                "SELECT event_type, COUNT(*) as count FROM {$this->events_table} GROUP BY event_type",
                ARRAY_A
            );
        }

        $events_by_type = array();
        foreach ($type_counts as $row) {
            $events_by_type[$row['event_type']] = (int) $row['count'];
        }

        // Threats blocked
        if (!empty($query_params)) {
            $threats_blocked = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$this->events_table} 
                 WHERE {$date_where} 
                 AND event_type IN ('firewall_block', 'malware_detected', 'ip_blocked')",
                $query_params
            ));
        } else {
            $threats_blocked = (int) $wpdb->get_var(
                "SELECT COUNT(*) FROM {$this->events_table} 
                 WHERE event_type IN ('firewall_block', 'malware_detected', 'ip_blocked')"
            );
        }

        $unique_ips = $this->get_unique_ips_for_period($period);
        $unique_attackers = $this->get_unique_attackers_for_period($period);

        return array(
            'total_events' => $total_events,
            'events_by_severity' => $events_by_severity,
            'events_by_type' => $events_by_type,
            'threats_blocked' => $threats_blocked,
            'unique_ips' => $unique_ips,
            'unique_attackers' => $unique_attackers,
            'critical_events' => $events_by_severity['critical']
        );
    }

    /**
     * Get unique IPs for period
     *
     * @since 2.0.0
     * @param string $period Period identifier
     * @return int
     */
    private function get_unique_ips_for_period($period) {
        global $wpdb;

        // Build date range query
        switch ($period) {
            case 'today':
                $start = date('Y-m-d') . ' 00:00:00';
                $end = date('Y-m-d') . ' 23:59:59';
                return (int) $wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(DISTINCT ip_address) FROM {$this->events_table} 
                     WHERE created_at BETWEEN %s AND %s AND ip_address IS NOT NULL",
                    $start, $end
                ));
            case 'week':
            case '7d':
                $start = date('Y-m-d', strtotime('-7 days')) . ' 00:00:00';
                $end = date('Y-m-d') . ' 23:59:59';
                return (int) $wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(DISTINCT ip_address) FROM {$this->events_table} 
                     WHERE created_at BETWEEN %s AND %s AND ip_address IS NOT NULL",
                    $start, $end
                ));
            case 'month':
            case '30d':
                $start = date('Y-m-d', strtotime('-30 days')) . ' 00:00:00';
                $end = date('Y-m-d') . ' 23:59:59';
                return (int) $wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(DISTINCT ip_address) FROM {$this->events_table} 
                     WHERE created_at BETWEEN %s AND %s AND ip_address IS NOT NULL",
                    $start, $end
                ));
            default:
                return (int) $wpdb->get_var(
                    "SELECT COUNT(DISTINCT ip_address) FROM {$this->events_table} 
                     WHERE ip_address IS NOT NULL"
                );
        }
    }

    /**
     * Get unique attackers for period
     *
     * @since 2.0.0
     * @param string $period Period identifier
     * @return int
     */
    private function get_unique_attackers_for_period($period) {
        global $wpdb;

        // Build date range query
        switch ($period) {
            case 'today':
                $start = date('Y-m-d') . ' 00:00:00';
                $end = date('Y-m-d') . ' 23:59:59';
                return (int) $wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(DISTINCT ip_address) FROM {$this->events_table} 
                     WHERE created_at BETWEEN %s AND %s 
                     AND event_type IN ('firewall_block', 'malware_detected', 'ip_blocked', 'login_failed') 
                     AND ip_address IS NOT NULL",
                    $start, $end
                ));
            case 'week':
            case '7d':
                $start = date('Y-m-d', strtotime('-7 days')) . ' 00:00:00';
                $end = date('Y-m-d') . ' 23:59:59';
                return (int) $wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(DISTINCT ip_address) FROM {$this->events_table} 
                     WHERE created_at BETWEEN %s AND %s 
                     AND event_type IN ('firewall_block', 'malware_detected', 'ip_blocked', 'login_failed') 
                     AND ip_address IS NOT NULL",
                    $start, $end
                ));
            case 'month':
            case '30d':
                $start = date('Y-m-d', strtotime('-30 days')) . ' 00:00:00';
                $end = date('Y-m-d') . ' 23:59:59';
                return (int) $wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(DISTINCT ip_address) FROM {$this->events_table} 
                     WHERE created_at BETWEEN %s AND %s 
                     AND event_type IN ('firewall_block', 'malware_detected', 'ip_blocked', 'login_failed') 
                     AND ip_address IS NOT NULL",
                    $start, $end
                ));
            default:
                return (int) $wpdb->get_var(
                    "SELECT COUNT(DISTINCT ip_address) FROM {$this->events_table} 
                     WHERE event_type IN ('firewall_block', 'malware_detected', 'ip_blocked', 'login_failed') 
                     AND ip_address IS NOT NULL"
                );
        }
    }

    // ============================================================================
    // SERVER SYNC METHODS
    // ============================================================================

    /**
     * Get unsynced events for server sync
     *
     * @since 2.0.0
     * @param int $limit Maximum number of events to fetch
     * @return array
     */
    public function get_unsynced_events($limit = 500) {
        global $wpdb;

        $events = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$this->events_table} 
             WHERE synced = 0 
             ORDER BY created_at ASC 
             LIMIT %d",
            $limit
        ), ARRAY_A);

        return $events;
    }

    /**
     * Mark events as synced
     *
     * @since 2.0.0
     * @param array $event_ids Array of event IDs
     * @return bool
     */
    public function mark_events_as_synced($event_ids) {
        global $wpdb;

        if (empty($event_ids)) {
            return false;
        }

        $ids_placeholder = implode(',', array_fill(0, count($event_ids), '%d'));

        $result = $wpdb->query($wpdb->prepare(
            "UPDATE {$this->events_table} 
             SET synced = 1 
             WHERE id IN ({$ids_placeholder})",
            $event_ids
        ));

        return $result !== false;
    }

    // ============================================================================
    // COMMON EVENT LOGGING SHORTCUTS
    // ============================================================================

    /**
     * Log scan completion
     *
     * @since 2.0.0
     * @param int   $files_scanned Number of files scanned
     * @param int   $threats_found Number of threats found
     * @param float $duration Scan duration in seconds
     * @return int|false
     */
    public static function log_scan_completed($files_scanned, $threats_found, $duration) {
        return self::get_instance()->log_event(
            'scan_completed',
            'info',
            array(
                'message' => sprintf('Scanned %d files, found %d threats in %.2f seconds', $files_scanned, $threats_found, $duration),
                'files_scanned' => $files_scanned,
                'threats_found' => $threats_found,
                'duration_seconds' => round($duration, 2),
            )
        );
    }

    /**
     * Log malware detection
     *
     * @since 2.0.0
     * @param string $file_path Infected file path
     * @param string $signature Malware signature matched
     * @param string $action Action taken (quarantine, delete, etc.)
     * @return int|false
     */
    public static function log_malware_detected($file_path, $signature, $action = 'detected') {
        return self::get_instance()->log_event(
            'malware_detected',
            'error',
            array(
                'message' => sprintf('Malware detected: %s (signature: %s, action: %s)', basename($file_path), $signature, $action),
                'file' => $file_path,
                'signature' => $signature,
                'action' => $action,
            )
        );
    }

    /**
     * Log firewall block
     *
     * @since 2.0.0
     * @param string $blocked_path Blocked request path
     * @param string $rule_id Rule that triggered block
     * @param string $attack_type Attack type
     * @return int|false
     */
    public static function log_firewall_block($blocked_path, $rule_id, $attack_type) {
        return self::get_instance()->log_event(
            'firewall_block',
            'warning',
            array(
                'message' => sprintf('Firewall blocked: %s (rule: %s, type: %s)', $blocked_path, $rule_id, $attack_type),
                'blocked_path' => $blocked_path,
                'rule_id' => $rule_id,
                'attack_type' => $attack_type,
            )
        );
    }

    /**
     * Log IP block
     *
     * @since 2.0.0
     * @param string $ip_address Blocked IP
     * @param string $reason Block reason
     * @param int    $duration Block duration in minutes
     * @return int|false
     */
    public static function log_ip_blocked($ip_address, $reason, $duration) {
        return self::get_instance()->log_event(
            'ip_blocked',
            'warning',
            array(
                'message' => sprintf('IP blocked: %s (reason: %s, duration: %d min)', $ip_address, $reason, $duration),
                'ip_address' => $ip_address,
                'reason' => $reason,
                'duration_minutes' => $duration,
            )
        );
    }

    /**
     * Log login failure
     *
     * @since 2.0.0
     * @param string $username Attempted username
     * @param string $reason Failure reason
     * @return int|false
     */
    public static function log_login_failed($username, $reason) {
        return self::get_instance()->log_event(
            'login_failed',
            'warning',
            array(
                'message' => sprintf('Login failed for user: %s (reason: %s)', $username, $reason),
                'username' => $username,
                'reason' => $reason,
            )
        );
    }

    /**
     * Log 2FA event
     *
     * @since 2.0.0
     * @param string $event_subtype Event subtype (enabled, disabled, verified, failed)
     * @param string $username Username
     * @param string $severity Severity level
     * @return int|false
     */
    public static function log_2fa_event($event_subtype, $username, $severity = 'info') {
        return self::get_instance()->log_event(
            '2fa_' . $event_subtype,
            $severity,
            array(
                'message' => sprintf('2FA %s for user: %s', $event_subtype, $username),
                'username' => $username,
            )
        );
    }

    /**
     * Log new admin user creation
     *
     * @since 2.0.0
     * @param int    $user_id New user ID
     * @param string $username Username
     * @param string $email Email
     * @return int|false
     */
    public static function log_admin_user_created($user_id, $username, $email) {
        return self::get_instance()->log_event(
            'admin_user_created',
            'critical',
            array(
                'message' => sprintf('New admin user created: %s (%s)', $username, $email),
                'user_id' => $user_id,
                'username' => $username,
                'email' => $email,
            )
        );
    }

    /**
     * Log plugin activation/deactivation
     *
     * @since 2.0.0
     * @param string $action Action: activated, deactivated
     * @param string $plugin Plugin slug
     * @return int|false
     */
    public static function log_plugin_action($action, $plugin) {
        return self::get_instance()->log_event(
            'plugin_' . $action,
            'info',
            array(
                'message' => sprintf('Plugin %s: %s', $action, $plugin),
                'plugin' => $plugin,
            )
        );
    }

    // ============================================================================
    // UTILITY METHODS
    // ============================================================================

    /**
     * Get client IP address
     *
     * @since 2.0.0
     * @return string|null
     */
    private function get_client_ip() {
        $ip = null;

        if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
            // Cloudflare
            $ip = sanitize_text_field(wp_unslash($_SERVER['HTTP_CF_CONNECTING_IP']));
        } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            // Proxy
            $ip_list = explode(',', sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR'])));
            $ip = trim($ip_list[0]);
        } elseif (isset($_SERVER['REMOTE_ADDR'])) {
            // Direct connection
            $ip = sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR']));
        }

        return $ip ? sanitize_text_field($ip) : null;
    }

    /**
     * Get count of pending events to sync
     *
     * @since 2.0.0
     * @since 2.2.2 -- Updated the DB query to correctly count unsynced events.
     * @return int
     */
    public function get_pending_sync_count() {
        global $wpdb;

        return (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$this->events_table} WHERE synced = 0"
        );
    }

    /**
     * Get last sync timestamp
     *
     * @since 2.0.0
     * @since 2.2.2 -- Updated the DB query to correctly fetch the last synced event timestamp.
     * @return string|null
     */
    public function get_last_sync_time() {
        global $wpdb;

        return $wpdb->get_var(
            "SELECT MAX(updated_at) FROM {$this->events_table} WHERE synced = 1"
        );
    }

    /**
     * Static wrapper for log_event (for backward compatibility)
     *
     * @since 2.0.0
     * @param string $event_type Event type
     * @param string $severity Severity level
     * @param array  $metadata Additional data
     * @param string $category Event category
     * @return int|false
     */
    public static function log($event_type, $severity = 'info', $metadata = array(), $category = null) {
        return self::get_instance()->log_event($event_type, $severity, $metadata, $category);
    }
}
