<?php
/**
 * Sync Service Class
 *
 * Handles 24-hour background sync of local events to the central server.
 * Sends unsynced events in batches and marks them as synced on success.
 *
 * @package VMPFence
 * @since 2.0.0
 */

if (!defined('ABSPATH')) {
    exit;
}

class VMPFence_Sync_Service {
    
    /**
     * Batch size for syncing events
     */
    const SYNC_BATCH_SIZE = 500;
    
    /**
     * Singleton instance
     *
     * @var VMPFence_Sync_Service
     */
    private static $instance = null;
    
    /**
     * Logging service instance
     *
     * @var VMPFence_Logging_Service
     */
    private $logging_service;
    
    /**
     * API client instance
     *
     * @var VMPFence_API_Client
     */
    private $api_client;
    
    /**
     * Get singleton instance
     *
     * @return VMPFence_Sync_Service
     */
    public static function get_instance() {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * Constructor
     */
    private function __construct() {
        require_once VMPFENCE_PLUGIN_DIR . 'lib/class-logging-service.php';
        require_once VMPFENCE_PLUGIN_DIR . 'lib/class-api-client.php';
        
        $this->logging_service = VMPFence_Logging_Service::get_instance();
        $this->api_client = new VMPFence_API_Client();
    }
    
    /**
     * Perform daily sync to server
     *
     * @since 2.0.0
     * @since 2.2.3 - Added update of version data during sync with server
     * @return array Sync results
     */
    public function perform_daily_sync() {
        $api_key = VMPFence_Config::get('central_api_key', '');
        
        if (empty($api_key)) {
            return array(
                'success' => false,
                'error' => 'API key not configured'
            );
        }
        
        // Check if user has opted into network participation
        $participate = VMPFence_Config::get('participate_security_network', 1);
        if (!$participate) {
            return array(
                'success' => false,
                'error' => 'Network participation disabled by user',
                'skipped' => true
            );
        }
        
        // Update version data with server (backward compatibility - ensures existing users send version info)
        // This runs hourly via cron, so all sites will report versions within 1 hour
        if ($this->api_client && method_exists($this->api_client, 'check_api_key')) {
            try {
                $version_result = $this->api_client->check_api_key($api_key);
                // Log success only if not WP_Error (which means API call failed)
                if (!is_wp_error($version_result)) {
                    VMPFence_Secure_Logger::log('info', 'Version data synced successfully during daily sync');
                }
            } catch (Exception $e) {
                // Fail gracefully - version sync is not critical for event sync
                VMPFence_Secure_Logger::log('warning', 'Version sync failed during daily sync: ' . $e->getMessage());
            }
        }
        
        $total_synced = 0;
        $total_errors = 0;
        $sync_start_time = microtime(true);
        
        // Sync events in batches
        while (true) {
            $unsynced_events = $this->logging_service->get_unsynced_events(self::SYNC_BATCH_SIZE);
            
            if (empty($unsynced_events)) {
                break; // No more events to sync
            }
            
            $result = $this->sync_event_batch($api_key, $unsynced_events);
            
            if ($result['success']) {
                $total_synced += $result['synced_count'];
            } else {
                $total_errors++;
                
                // Log error
                VMPFence_Secure_Logger::log('error', 'Sync batch failed', array(
                    'error' => $result['error'],
                    'batch_size' => count($unsynced_events)
                ));
                
                // Stop if too many errors
                if ($total_errors >= 3) {
                    break;
                }
            }
            
            // Rate limiting: sleep briefly between batches
            if (count($unsynced_events) === self::SYNC_BATCH_SIZE) {
                sleep(1); // 1 second pause
            }
        }
        
        $sync_duration = microtime(true) - $sync_start_time;
        
        // Update last sync time
        update_option('vmpfence_last_sync_time', current_time('timestamp'));
        update_option('vmpfence_last_sync_count', $total_synced);
        
        // Log sync completion
        VMPFence_Secure_Logger::log('info', sprintf(
            'Daily sync completed: %d events synced in %.2f seconds',
            $total_synced,
            $sync_duration
        ));
        
        return array(
            'success' => true,
            'total_synced' => $total_synced,
            'total_errors' => $total_errors,
            'duration' => $sync_duration
        );
    }
    
    /**
     * Sync a batch of events to the server
     *
     * @param string $api_key API key
     * @param array  $events Array of event records
     * @return array Result with success status
     */
    private function sync_event_batch($api_key, $events) {
        // Prepare events for API
        $events_payload = array();
        $event_ids = array();
        
        foreach ($events as $event) {
            $events_payload[] = array(
                'plugin_event_id' => (int) $event['id'],
                'event_type' => $event['event_type'],
                'severity' => $event['severity'],
                'message' => $event['message'],
                'ip_address' => $event['ip_address'],
                'user_agent' => $event['user_agent'],
                'request_uri' => $event['request_uri'],
                'metadata' => $event['metadata'] ? json_decode($event['metadata'], true) : array(),
                'created_at' => $event['created_at']
            );
            
            $event_ids[] = (int) $event['id'];
        }
        
        // Send to API
        $response = $this->api_client->sync_events($api_key, $events_payload);
        
        if (is_wp_error($response)) {
            return array(
                'success' => false,
                'error' => $response->get_error_message(),
                'synced_count' => 0
            );
        }
        
        // Mark events as synced
        $this->logging_service->mark_events_as_synced($event_ids);
        
        return array(
            'success' => true,
            'synced_count' => count($event_ids),
            'error' => null
        );
    }
    
    /**
     * Sync analytics summary to server
     *
     * @return array Result
     */
    public function sync_analytics() {
        global $wpdb;
        
        $api_key = VMPFence_Config::get('central_api_key', '');
        
        if (empty($api_key)) {
            return array('success' => false, 'error' => 'API key not configured');
        }
        
        // Check if user has opted into network participation
        $participate = VMPFence_Config::get('participate_security_network', 1);
        if (!$participate) {
            return array('success' => false, 'error' => 'Network participation disabled');
        }
        
        require_once VMPFENCE_PLUGIN_DIR . 'includes/class-database.php';
        $database = new VMPFence_Logging_Database();
        $analytics_table = $wpdb->prefix . 'vmpfence_analytics';
        
        // Get last 30 days of analytics
        $analytics_data = $wpdb->get_results(
            "SELECT * FROM {$analytics_table} 
             WHERE date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) 
             ORDER BY date DESC",
            ARRAY_A
        );
        
        if (empty($analytics_data)) {
            return array('success' => true, 'synced_count' => 0);
        }
        
        // Prepare payload
        $analytics_payload = array();
        foreach ($analytics_data as $row) {
            $analytics_payload[] = array(
                'date' => $row['date'],
                'total_events' => (int) $row['total_events'],
                'events_by_severity' => json_decode($row['events_by_severity'], true),
                'events_by_type' => json_decode($row['events_by_type'], true),
                'threats_blocked' => (int) $row['threats_blocked'],
                'unique_ips' => (int) $row['unique_ips'],
                'unique_attackers' => (int) $row['unique_attackers']
            );
        }
        
        // Send to API
        $response = $this->api_client->sync_analytics($api_key, $analytics_payload);
        
        if (is_wp_error($response)) {
            return array(
                'success' => false,
                'error' => $response->get_error_message()
            );
        }
        
        return array(
            'success' => true,
            'synced_count' => count($analytics_payload)
        );
    }
    
    /**
     * Get sync status
     *
     * @return array Sync status information
     */
    public function get_sync_status() {
        $last_sync_time = get_option('vmpfence_last_sync_time', 0);
        $last_sync_count = get_option('vmpfence_last_sync_count', 0);
        
        require_once VMPFENCE_PLUGIN_DIR . 'includes/class-database.php';
        $database = new VMPFence_Logging_Database();
        $db_stats = $database->get_stats();
        
        $next_sync = wp_next_scheduled('vmpfence_daily_sync');
        
        return array(
            'last_sync_time' => $last_sync_time,
            'last_sync_count' => $last_sync_count,
            'last_sync_ago' => $last_sync_time ? human_time_diff($last_sync_time, current_time('timestamp')) . ' ago' : 'Never',
            'unsynced_events' => $db_stats['unsynced_events'],
            'next_sync' => $next_sync,
            'next_sync_in' => $next_sync ? human_time_diff(current_time('timestamp'), $next_sync) : 'Not scheduled'
        );
    }
    
    /**
     * Manually trigger sync (for admin action)
     *
     * @return array Sync result
     */
    public function manual_sync() {
        // Perform sync
        $result = $this->perform_daily_sync();
        
        // Also sync analytics
        if ($result['success']) {
            $analytics_result = $this->sync_analytics();
            $result['analytics_synced'] = $analytics_result['synced_count'] ?? 0;
        }
        
        return $result;
    }
}
