<?php
defined('ABSPATH') || exit;

/**
 * WPFort Webhooks System
 * 
 * This file contains functions for sending webhook notifications to the WPFort SAAS platform
 * to provide real-time updates on scan progress, failures, and completions.
 */

/**
 * Initialize WordPress Filesystem API
 *
 * @return WP_Filesystem_Base|false Filesystem object on success, false on failure
 */
function wpsec_init_webhooks_filesystem() {
    global $wp_filesystem;
    
    if (!function_exists('WP_Filesystem')) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
    }
    
    if (!$wp_filesystem) {
        if (false === WP_Filesystem()) {
            wpsec_debug_log('WPFort: Failed to initialize WordPress filesystem API', 'info');
            return false;
        }
    }
    
    return $wp_filesystem;
}

/**
 * Fetch webhook secret for the current site
 * 
 * @return array|WP_Error The webhook secret data or WP_Error on failure
 */
function wpsec_get_webhook_secret() {
    // Check if we have a cached secret
    $cached_secret = get_option('wpsec_webhook_secret', '');
    $cached_timestamp = get_option('wpsec_webhook_secret_timestamp', 0);
    
    // If we have a cached secret that's less than 12 hours old, use it
    if (!empty($cached_secret) && (time() - $cached_timestamp) < 43200) { // 12 hours in seconds
        wpsec_debug_log('✅ Using cached webhook secret (age: ' . round((time() - $cached_timestamp) / 3600, 1) . ' hours)', 'info');
        return json_decode($cached_secret, true);
    }
    
    // Get the current site domain
    $site_url = get_site_url();
    $domain = wp_parse_url($site_url, PHP_URL_HOST);
    
    // Make request to get webhook secret
    $webhook_url = 'http://62.171.130.143:3000/api/webhook-secrets/' . $domain . '/webhook-secret';
    
    $args = [
        'timeout' => 30,
        'sslverify' => false,
        'headers' => [
            'User-Agent' => 'WordPress/' . get_bloginfo('version') . '; ' . get_bloginfo('url'),
            'X-WPSec-Version' => defined('WPSEC_VERSION') ? WPSEC_VERSION : '1.0.0'
        ]
    ];
    
    wpsec_debug_log('🔄 Fetching webhook secret from: ' . $webhook_url, 'info');
    
    $response = wp_remote_post($webhook_url, $args);
    
    if (is_wp_error($response)) {
        wpsec_debug_log('❌ Failed to fetch webhook secret: ' . $response->get_error_message(), 'error');
        return $response;
    }
    
    $status_code = wp_remote_retrieve_response_code($response);
    if ($status_code !== 200) {
        wpsec_debug_log('❗ Unexpected HTTP response code when fetching webhook secret: ' . $status_code, 'info');
        return new WP_Error('webhook_error', 'Failed to fetch webhook secret. Status code: ' . $status_code);
    }
    
    $body = wp_remote_retrieve_body($response);
    if (empty($body)) {
        wpsec_debug_log('❗ Empty response when fetching webhook secret', 'info');
        return new WP_Error('webhook_error', 'Empty response when fetching webhook secret');
    }
    
    $data = json_decode($body, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        wpsec_debug_log('❗ JSON decode error when parsing webhook secret: ' . json_last_error_msg(), 'error');
        return new WP_Error('webhook_error', 'Invalid JSON response: ' . json_last_error_msg());
    }
    
    // Cache the secret
    update_option('wpsec_webhook_secret', $body);
    update_option('wpsec_webhook_secret_timestamp', time());
    
    wpsec_debug_log('✅ Webhook secret fetched and cached successfully', 'info');
    
    return $data;
}

/**
 * Generate the HMAC signature for webhook requests
 * 
 * @param string $secret The webhook secret
 * @param int $timestamp The current timestamp
 * @param array $payload The payload to send
 * @return array The headers for the webhook request
 */
function wpsec_generate_webhook_signature($secret, $timestamp, $payload) {
    $payload_json = json_encode($payload);
    $signature_data = $timestamp . '.' . $payload_json;
    $signature = hash_hmac('sha256', $signature_data, $secret);
    
    return [
        'x-wpfort-signature' => $signature,
        'x-wpfort-timestamp' => $timestamp,
        'Content-Type' => 'application/json'
    ];
}

/**
 * Send webhook notification to the WPFort SAAS platform
 * 
 * @param string $endpoint The webhook endpoint
 * @param array $payload The payload to send
 * @param bool $is_retry Whether this is a retry attempt after refreshing the secret
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_webhook($endpoint, $payload, $is_retry = false) {
    // Get the webhook secret
    $secret_data = wpsec_get_webhook_secret();
    
    if (is_wp_error($secret_data)) {
        wpsec_debug_log('❌ WPFort Webhook: Failed to get webhook secret: ' . $secret_data->get_error_message(), 'error');
        return $secret_data;
    }
    
    if (!isset($secret_data['secret'])) {
        wpsec_debug_log('❌ WPFort Webhook: Invalid webhook secret data: secret key missing', 'error');
        return new WP_Error('webhook_error', 'Invalid webhook secret data: secret key missing');
    }
    
    $secret = $secret_data['secret'];
    $timestamp = time();
    
    // Generate signature and headers
    $headers = wpsec_generate_webhook_signature($secret, $timestamp, $payload);
    
    // Prepare the request
    $webhook_url = 'http://62.171.130.143:3000/api/webhooks/' . $endpoint;
    
    $args = [
        'method' => 'POST',
        'timeout' => 30,
        'sslverify' => false,
        'headers' => $headers,
        'body' => json_encode($payload)
    ];
    
    wpsec_debug_log('🔄 WPFort Webhook: Sending webhook to: ' . $webhook_url, 'info');
    wpsec_debug_log('📦 WPFort Webhook: Payload: ' . json_encode($payload), 'info');
    wpsec_debug_log('🔑 WPFort Webhook: Using signature: ' . $headers['x-wpfort-signature'], 'info');
    wpsec_debug_log('⏱️ WPFort Webhook: Using timestamp: ' . $headers['x-wpfort-timestamp'], 'info');
    
    // Send the request
    $response = wp_remote_post($webhook_url, $args);
    
    if (is_wp_error($response)) {
        wpsec_debug_log('❌ WPFort Webhook: Failed to send webhook: ' . $response->get_error_message(), 'error');
        return $response;
    }
    
    $status_code = wp_remote_retrieve_response_code($response);
    $response_body = wp_remote_retrieve_body($response);
    
    if ($status_code < 200 || $status_code >= 300) {
        wpsec_debug_log('❗ WPFort Webhook: Unexpected HTTP response code when sending webhook: ' . $status_code, 'info');
        wpsec_debug_log('📄 WPFort Webhook: Response body: ' . $response_body, 'info');
        
        // Check for 401 error with specific message about webhook secret being invalid
        if ($status_code === 401 && !$is_retry) {
            $response_data = json_decode($response_body, true);
            
            // Check for the specific error message
            if (is_array($response_data) && isset($response_data['error']) && $response_data['error'] === 'No webhook secret configured') {
                wpsec_debug_log('🔄 WPFort Webhook: Detected invalid webhook secret. Refreshing secret and retrying...', 'info');
                
                // Force delete the cached secret to ensure we get a fresh one
                delete_option('wpsec_webhook_secret');
                delete_option('wpsec_webhook_secret_timestamp');
                
                // Retry the webhook with the newly fetched secret
                return wpsec_send_webhook($endpoint, $payload, true);
            }
        }
        
        return new WP_Error('webhook_error', 'Failed to send webhook. Status code: ' . $status_code);
    }
    
    wpsec_debug_log('✅ WPFort Webhook: Successfully sent webhook to ' . $endpoint . ' (Status: ' . $status_code . ')', 'info');
    wpsec_debug_log('📄 WPFort Webhook: Response body: ' . $response_body, 'info');
    
    return true;
}

/**
 * Send scan progress webhook
 * 
 * @param string $scan_id The scan ID
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_scan_progress_webhook($scan_id) {
    // Check if webhooks should be bypassed for this scan
    $bypass_webhooks = get_option('wpsec_scan_' . $scan_id . '_bypass_webhooks', false);
    if ($bypass_webhooks) {
        wpsec_debug_log('📣 WPFort Webhook: Bypassing progress webhook for scan ID: ' . $scan_id, 'info');
        return true; // Return success without sending the webhook
    }
    
    // Get scan status
    $status = get_option('wpsec_scan_' . $scan_id . '_status', '');
    $progress = get_option('wpsec_scan_' . $scan_id . '_progress', 0);
    $files_scanned = get_option('wpsec_scan_' . $scan_id . '_files_scanned', 0);
    $total_files = get_option('wpsec_scan_' . $scan_id . '_total_files', 0);
    
    if (empty($status)) {
        wpsec_debug_log('❌ WPFort Webhook: Cannot send progress webhook: scan not found or expired', 'error');
        return new WP_Error('webhook_error', 'Scan not found or expired');
    }
    
    // Map 'scanning' status to 'running' for the API
    $api_status = $status === 'scanning' ? 'running' : $status;
    
    $payload = [
        'scan_id' => $scan_id,
        'status' => $api_status,
        'progress' => intval($progress),
        'files_scanned' => strval($files_scanned),
        'total_files' => strval($total_files)
    ];
    
    wpsec_debug_log(sprintf(
        '📤 WPFort Webhook: Sending progress update - ID: %s, Status: %s, Progress: %d%%, Files: %s/%s',
        $scan_id,
        $api_status,
        intval($progress),
        $files_scanned,
        $total_files
    ), 'info');
    
    return wpsec_send_webhook('scan-progress', $payload);
}

/**
 * Send scan failed webhook
 * 
 * @param string $scan_id The scan ID
 * @param string $error_message The error message
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_scan_failed_webhook($scan_id, $error_message) {
    // Check if webhooks should be bypassed for this scan
    $bypass_webhooks = get_option('wpsec_scan_' . $scan_id . '_bypass_webhooks', false);
    if ($bypass_webhooks) {
        wpsec_debug_log('📣 WPFort Webhook: Bypassing failed webhook for scan ID: ' . $scan_id, 'info');
        return true; // Return success without sending the webhook
    }
    $payload = [
        'scan_id' => $scan_id,
        'error_message' => $error_message
    ];
    
    return wpsec_send_webhook('scan-failed', $payload);
}

/**
 * Send scan complete webhook
 * 
 * @param string $scan_id The scan ID
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_scan_complete_webhook($scan_id) {
    // Check if webhooks should be bypassed for this scan
    $bypass_webhooks = get_option('wpsec_scan_' . $scan_id . '_bypass_webhooks', false);
    if ($bypass_webhooks) {
        wpsec_debug_log('📣 WPFort Webhook: Bypassing complete webhook for scan ID: ' . $scan_id, 'info');
        return true; // Return success without sending the webhook
    }
    $payload = [
        'scan_id' => $scan_id
    ];
    
    return wpsec_send_webhook('scan-complete', $payload);
}

/**
 * Schedule periodic scan progress updates
 * 
 * @param string $scan_id The scan ID
 */
function wpsec_schedule_scan_progress_updates($scan_id) {
    // Check if webhooks should be bypassed for this scan
    $bypass_webhooks = get_option('wpsec_scan_' . $scan_id . '_bypass_webhooks', false);
    if ($bypass_webhooks) {
        wpsec_debug_log('📣 WPFort Webhook: Bypassing scheduled progress updates for scan ID: ' . $scan_id, 'info');
        return; // Don't schedule any updates if webhooks are bypassed
    }
    
    wpsec_debug_log('🔄 WPFort Webhook: Setting up scheduled progress updates for scan ' . $scan_id, 'info');
    
    // Clear any existing scheduled events for this scan
    wp_clear_scheduled_hook('wpsec_send_scan_progress_update', [$scan_id]);
    
    // Schedule the first update immediately and then every 5 seconds
    wpsec_debug_log('🔄 WPFort Webhook: Scheduling first progress update immediately', 'info');
    wp_schedule_single_event(time(), 'wpsec_send_scan_progress_update', [$scan_id]);
    
    // Store the scan ID for the progress updates
    update_option('wpsec_current_progress_scan_id', $scan_id);
}

/**
 * Handle the scheduled scan progress update event
 * 
 * @param string $scan_id The scan ID
 */
function wpsec_handle_scan_progress_update($scan_id) {
    // Check if webhooks should be bypassed for this scan
    $bypass_webhooks = get_option('wpsec_scan_' . $scan_id . '_bypass_webhooks', false);
    if ($bypass_webhooks) {
        wpsec_debug_log('📣 WPFort Webhook: Bypassing scheduled progress update for scan ID: ' . $scan_id, 'info');
        return; // Don't send any updates if webhooks are bypassed
    }
    
    // Get the current scan status
    $status = get_option('wpsec_scan_' . $scan_id . '_status', '');
    $progress = get_option('wpsec_scan_' . $scan_id . '_progress', 0);
    $files_scanned = get_option('wpsec_scan_' . $scan_id . '_files_scanned', 0);
    $total_files = get_option('wpsec_scan_' . $scan_id . '_total_files', 0);
    
    wpsec_debug_log('🔄 WPFort Webhook: Processing scheduled progress update for scan ' . $scan_id, 'info');
    wpsec_debug_log('📊 WPFort Webhook: Current status=' . $status . ', progress=' . $progress . '%, files=' . $files_scanned . '/' . $total_files, 'info');
    
    // Send the progress webhook
    $result = wpsec_send_scan_progress_webhook($scan_id);
    
    if (is_wp_error($result)) {
        wpsec_debug_log('❌ WPFort Webhook: Failed to send progress update: ' . $result->get_error_message(), 'error');
    } else {
        wpsec_debug_log('✅ WPFort Webhook: Progress update sent successfully', 'info');
    }
    
    // If the scan is still running, schedule the next update
    if ($status === 'scanning' || $status === 'running') {
        wpsec_debug_log('🔄 WPFort Webhook: Scheduling next progress update in 5 seconds', 'info');
        wp_schedule_single_event(time() + 5, 'wpsec_send_scan_progress_update', [$scan_id]);
    } else {
        wpsec_debug_log('🛑 WPFort Webhook: Scan is no longer running, stopping progress updates', 'info');
        // Scan is no longer running, clean up
        delete_option('wpsec_current_progress_scan_id');
    }
}

/**
 * Check if a scan has timed out
 * 
 * @param string $scan_id The scan ID
 */
function wpsec_check_scan_timeout($scan_id) {
    // Check if webhooks should be bypassed for this scan
    $bypass_webhooks = get_option('wpsec_scan_' . $scan_id . '_bypass_webhooks', false);
    
    wpsec_debug_log('🕒 WPFort Webhook: Checking for scan timeout for scan ' . $scan_id, 'info');
    
    // Get the current scan status
    $status = get_option('wpsec_scan_' . $scan_id . '_status', '');
    $last_update = get_option('wpsec_scan_' . $scan_id . '_last_progress_update', 0);
    $current_time = time();
    $timeout_duration = 600; // 10 minutes timeout - increased from 2 minutes to handle large files
    
    // If the scan is still running but hasn't updated in 2 minutes, consider it stalled
    if (($status === 'scanning' || $status === 'running') && ($current_time - $last_update) > $timeout_duration) {
        wpsec_debug_log('⚠️ WPFort Webhook: Scan appears to be stalled. Last update was ' . ($current_time - $last_update) . ' seconds ago', 'warning');
        
        // Get current system stats for diagnosis
        $memory_usage = function_exists('memory_get_usage') ? round(memory_get_usage(true) / 1024 / 1024, 2) . 'MB' : 'Unknown';
        $memory_limit = ini_get('memory_limit');
        $load_average = function_exists('sys_getloadavg') ? sys_getloadavg() : array('Unknown');
        $disk_free_space = function_exists('disk_free_space') ? round(disk_free_space(ABSPATH) / 1024 / 1024 / 1024, 2) . 'GB' : 'Unknown';
        
        // Get last scanned file and progress
        $last_scanned_file = get_option('wpsec_scan_' . $scan_id . '_last_scanned_file', 'Unknown');
        $progress = get_option('wpsec_scan_' . $scan_id . '_progress', 0);
        $files_scanned = get_option('wpsec_scan_' . $scan_id . '_files_scanned', 0);
        $total_files = get_option('wpsec_scan_' . $scan_id . '_total_files', 0);
        
        // Get the file size and type if available
        $file_size = 'Unknown';
        $file_type = 'Unknown';
        $file_mod_time = 'Unknown';
        
        // Use WP_Filesystem for file operations
        $wp_filesystem = wpsec_init_webhooks_filesystem();
        
        if ($wp_filesystem && $wp_filesystem->exists($last_scanned_file)) {
            $file_size = round($wp_filesystem->size($last_scanned_file) / 1024, 2) . 'KB';
            $file_type = pathinfo($last_scanned_file, PATHINFO_EXTENSION);
            // Note: WP_Filesystem doesn't have direct equivalent to filemtime
            // so we'll use the PHP function but with gmdate instead of date
            $file_mod_time = gmdate('Y-m-d H:i:s', filemtime($last_scanned_file));
        } elseif (file_exists($last_scanned_file)) {
            // Fallback to direct file operations if WP_Filesystem fails
            $file_size = round(filesize($last_scanned_file) / 1024, 2) . 'KB';
            $file_type = pathinfo($last_scanned_file, PATHINFO_EXTENSION);
            $file_mod_time = gmdate('Y-m-d H:i:s', filemtime($last_scanned_file));
        }
        
        // Create a diagnostic report
        $diagnostic_data = array(
            'memory_usage' => $memory_usage,
            'memory_limit' => $memory_limit,
            'load_average' => is_array($load_average) ? implode(', ', $load_average) : $load_average,
            'disk_free_space' => $disk_free_space,
            'last_scanned_file' => $last_scanned_file,
            'file_size' => $file_size,
            'file_type' => $file_type,
            'file_mod_time' => $file_mod_time,
            'progress' => $progress,
            'files_scanned' => $files_scanned,
            'total_files' => $total_files,
            'time_since_update' => ($current_time - $last_update) . 's',
            'timestamp' => gmdate('Y-m-d H:i:s')
        );
        
        // Store diagnostic data with the scan
        update_option('wpsec_scan_' . $scan_id . '_timeout_diagnostics', json_encode($diagnostic_data));
        
        // Create a more informative error message
        $error_message = 'Scan timed out after ' . $timeout_duration . ' seconds of inactivity. '
            . 'Last file: ' . basename($last_scanned_file) . ' (' . $file_size . '). '
            . 'Memory: ' . $memory_usage . '/' . $memory_limit . '.';
        
        // Log detailed diagnostics
        wpsec_debug_log('💥 WPFort Scan Timeout Diagnostics:\n' .
            '- Memory: ' . $memory_usage . '/' . $memory_limit . '\n' .
            '- File: ' . $last_scanned_file . '\n' .
            '- File Size: ' . $file_size . '\n' .
            '- Progress: ' . $progress . '%\n' .
            '- Files: ' . $files_scanned . '/' . $total_files, 'info');
        
        // Mark the scan as failed with improved error details
        update_option('wpsec_scan_' . $scan_id . '_status', 'error');
        update_option('wpsec_scan_' . $scan_id . '_error', $error_message);
        
        // Create a structured critical error for the scan-errors endpoint
        $critical_error = array(
            'message' => $error_message,
            'type' => 'timeout',
            'last_file' => $last_scanned_file,
            'diagnostics' => $diagnostic_data,
            'time' => gmdate('Y-m-d H:i:s')
        );
        update_option('wpsec_scan_' . $scan_id . '_critical_error', json_encode($critical_error));
        
        // Send scan failed webhook if not bypassed
        if (!$bypass_webhooks) {
            wpsec_send_scan_failed_webhook($scan_id, $error_message);
        } else {
            wpsec_debug_log('📣 WPFort Webhook: Bypassing failed webhook on timeout for scan ID: ' . $scan_id, 'info');
        }
        
        wpsec_debug_log('❌ WPFort Webhook: Marked scan as failed due to timeout', 'error');
    } else if ($status === 'scanning' || $status === 'running') {
        // Scan is still running and has updated recently, schedule another check
        wpsec_debug_log('🕒 WPFort Webhook: Scan is still active, scheduling another timeout check', 'info');
        wp_schedule_single_event(time() + 60, 'wpsec_check_scan_timeout', [$scan_id]);
    } else {
        wpsec_debug_log('🕒 WPFort Webhook: Scan is no longer running (status: ' . $status . '), no need for timeout checks', 'info');
    }
}

/**
 * Send core reinstall progress webhook
 * 
 * @param string $operation_id The operation ID
 * @param string $status The current status
 * @param string $message The status message
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_core_reinstall_progress_webhook($operation_id, $status = 'in_progress', $message = '') {
    // Check if webhooks should be bypassed for this operation
    $bypass_webhooks = get_option('wpsec_core_reinstall_bypass_webhooks_' . $operation_id, false);
    if ($bypass_webhooks) {
        wpsec_debug_log('📣 WPFort Webhook: Bypassing progress webhook for core reinstall operation ID: ' . $operation_id, 'info');
        return true; // Return success without sending the webhook
    }
    
    // Default message if not provided
    if (empty($message)) {
        $message = 'Reinstall is running';
    }
    
    $payload = [
        'operation_id' => $operation_id,
        'status' => $status,
        'message' => $message
    ];
    
    wpsec_debug_log(sprintf(
        '📤 WPFort Webhook: Sending core reinstall progress webhook for operation ID: %s (Status: %s)',
        $operation_id,
        $status
    ), 'info');
    
    // Send the webhook
    $result = wpsec_send_webhook('core-reinstall-progress', $payload);
    
    if (is_wp_error($result)) {
        wpsec_debug_log('❌ WPFort Webhook: Failed to send core reinstall progress webhook: ' . $result->get_error_message(), 'error');
        return $result;
    }
    
    // Update last webhook time
    update_option('wpsec_core_reinstall_last_webhook_time_' . $operation_id, time());
    
    wpsec_debug_log('✅ WPFort Webhook: Core reinstall progress webhook sent successfully', 'info');
    return true;
}

/**
 * Send core reinstall complete webhook
 * 
 * @param string $operation_id The operation ID
 * @param string $message The completion message
 * @param string $completed_at ISO8601 timestamp when the reinstall completed
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_core_reinstall_complete_webhook($operation_id, $message = '', $completed_at = '') {
    // Check if webhooks should be bypassed for this operation
    $bypass_webhooks = get_option('wpsec_core_reinstall_bypass_webhooks_' . $operation_id, false);
    if ($bypass_webhooks) {
        wpsec_debug_log('📣 WPFort Webhook: Bypassing complete webhook for core reinstall operation ID: ' . $operation_id, 'info');
        return true; // Return success without sending the webhook
    }
    
    // Default message if not provided
    if (empty($message)) {
        $message = 'Reinstall completed successfully';
    }
    
    // Default completed_at if not provided
    if (empty($completed_at)) {
        $completed_at = gmdate('c'); // ISO8601 format
    }
    
    $payload = [
        'operation_id' => $operation_id,
        'status' => 'completed',
        'message' => $message,
        'completed_at' => $completed_at
    ];
    
    wpsec_debug_log(sprintf(
        '📤 WPFort Webhook: Sending core reinstall complete webhook for operation ID: %s',
        $operation_id
    ), 'info');
    
    // Send the webhook
    $result = wpsec_send_webhook('core-reinstall-complete', $payload);
    
    if (is_wp_error($result)) {
        wpsec_debug_log('❌ WPFort Webhook: Failed to send core reinstall complete webhook: ' . $result->get_error_message(), 'error');
        return $result;
    }
    
    wpsec_debug_log('✅ WPFort Webhook: Core reinstall complete webhook sent successfully', 'info');
    return true;
}

/**
 * Send core reinstall failed webhook
 * 
 * @param string $operation_id The operation ID
 * @param string $error_message The error message
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_core_reinstall_failed_webhook($operation_id, $error_message) {
    // Check if webhooks should be bypassed for this operation
    $bypass_webhooks = get_option('wpsec_core_reinstall_bypass_webhooks_' . $operation_id, false);
    if ($bypass_webhooks) {
        wpsec_debug_log('📣 WPFort Webhook: Bypassing failed webhook for core reinstall operation ID: ' . $operation_id, 'info');
        return true; // Return success without sending the webhook
    }
    
    $payload = [
        'operation_id' => $operation_id,
        'status' => 'failed',
        'error_message' => $error_message
    ];
    
    wpsec_debug_log(sprintf(
        '📤 WPFort Webhook: Sending core reinstall failed webhook for operation ID: %s',
        $operation_id
    ), 'info');
    
    // Send the webhook
    $result = wpsec_send_webhook('core-reinstall-failed', $payload);
    
    if (is_wp_error($result)) {
        wpsec_debug_log('❌ WPFort Webhook: Failed to send core reinstall failed webhook: ' . $result->get_error_message(), 'error');
        return $result;
    }
    
    wpsec_debug_log('✅ WPFort Webhook: Core reinstall failed webhook sent successfully', 'info');
    return true;
}

/**
 * Send update progress webhook
 * 
 * @param string $update_id The update operation ID
 * @param array $items The items being updated with their status
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_update_progress_webhook($update_id, $items) {
    if (empty($update_id)) {
        wpsec_debug_log('❌ WPFort Webhook: Cannot send update progress webhook: missing update_id', 'error');
        return new WP_Error('webhook_error', 'Missing update_id parameter');
    }
    
    $payload = [
        'update_id' => $update_id,
        'items' => $items
    ];
    
    wpsec_debug_log(sprintf(
        '📣 WPFort Webhook: Sending update progress webhook for update ID: %s with %d items',
        $update_id,
        count($items)
    ), 'info');
    
    return wpsec_send_webhook('updates-progress', $payload);
}

/**
 * Send update completed webhook
 * 
 * @param string $update_id The update operation ID
 * @param array $items The final status of all updated items
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_update_completed_webhook($update_id, $items) {
    if (empty($update_id)) {
        wpsec_debug_log('❌ WPFort Webhook: Cannot send update completed webhook: missing update_id', 'error');
        return new WP_Error('webhook_error', 'Missing update_id parameter');
    }
    
    $payload = [
        'update_id' => $update_id,
        'items' => $items
    ];
    
    wpsec_debug_log(sprintf(
        '📣 WPFort Webhook: Sending update completed webhook for update ID: %s with %d items',
        $update_id,
        count($items)
    ), 'info');
    
    return wpsec_send_webhook('updates-completed', $payload);
}

/**
 * Send individual item update progress webhook
 * 
 * @param string $slug The plugin/theme slug being updated
 * @param string $status Current status (e.g., 'in-progress')
 * @param string|null $error Error message, if any
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_update_item_progress_webhook($slug, $status = 'in-progress', $error = null) {
    if (empty($slug)) {
        wpsec_debug_log('❌ WPFort Webhook: Cannot send item update progress webhook: missing slug', 'error');
        return new WP_Error('webhook_error', 'Missing slug parameter');
    }
    
    // Get the current site domain
    $site_url = get_site_url();
    $domain = wp_parse_url($site_url, PHP_URL_HOST);
    
    $payload = [
        'domain' => $domain,
        'slug' => $slug,
        'status' => $status,
        'error' => $error
    ];
    
    wpsec_debug_log(sprintf(
        '📣 WPFort Webhook: Sending item update progress webhook for slug: %s with status: %s',
        $slug,
        $status
    ), 'info');
    
    return wpsec_send_webhook('update-item-progress', $payload);
}

/**
 * Send individual item update completion webhook
 * 
 * @param string $slug The plugin/theme slug that was updated
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_update_item_complete_webhook($slug) {
    if (empty($slug)) {
        wpsec_debug_log('❌ WPFort Webhook: Cannot send item update complete webhook: missing slug', 'error');
        return new WP_Error('webhook_error', 'Missing slug parameter');
    }
    
    // Get the current site domain
    $site_url = get_site_url();
    $domain = wp_parse_url($site_url, PHP_URL_HOST);
    
    $payload = [
        'domain' => $domain,
        'slug' => $slug
    ];
    
    wpsec_debug_log(sprintf(
        '📣 WPFort Webhook: Sending item update complete webhook for slug: %s',
        $slug
    ), 'info');
    
    return wpsec_send_webhook('update-item-complete', $payload);
}

/**
 * Send individual item update failed webhook
 * 
 * @param string $slug The plugin/theme slug that failed to update
 * @param string $error_message The error message
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_update_item_failed_webhook($slug, $error_message) {
    if (empty($slug)) {
        wpsec_debug_log('❌ WPFort Webhook: Cannot send item update failed webhook: missing slug', 'error');
        return new WP_Error('webhook_error', 'Missing slug parameter');
    }
    
    // Get the current site domain
    $site_url = get_site_url();
    $domain = wp_parse_url($site_url, PHP_URL_HOST);
    
    $payload = [
        'domain' => $domain,
        'slug' => $slug,
        'error_message' => $error_message
    ];
    
    wpsec_debug_log(sprintf(
        '📣 WPFort Webhook: Sending item update failed webhook for slug: %s with error: %s',
        $slug,
        $error_message
    ), 'error');
    
    return wpsec_send_webhook('update-item-failed', $payload);
}

// Register the actions for the scheduled events
add_action('wpsec_send_scan_progress_update', 'wpsec_handle_scan_progress_update');
add_action('wpsec_check_scan_timeout', 'wpsec_check_scan_timeout');

/**
 * Send backup progress webhook
 * 
 * @param string $backup_id The backup ID
 * @param float $progress The backup progress (0-100)
 * @param string $status The current status (pending, in_progress)
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_backup_progress_webhook($backup_id, $progress = 0, $status = 'in_progress') {
    // Rate limiting: Check if we've sent a webhook for this backup recently
    $last_sent_option = 'wpsec_backup_progress_webhook_last_' . $backup_id;
    $last_sent_time = get_option($last_sent_option, 0);
    $current_time = time();
    
    // Only send if 5+ seconds have passed since the last webhook
    if ($current_time - $last_sent_time < 5) {
        wpsec_debug_log(sprintf(
            'Skipping backup progress webhook - Rate limited (ID: %s, Last sent: %d seconds ago)',
            $backup_id,
            $current_time - $last_sent_time
        ), 'debug');
        return true; // Return true to indicate success even though we skipped
    }
    
    // Get the current site domain
    $site_url = get_site_url();
    $domain = wp_parse_url($site_url, PHP_URL_HOST);
    
    // Round progress to integer as per the spec
    $progress = intval(round($progress));
    
    $payload = [
        'domain' => $domain,
        'backup_id' => $backup_id,
        'status' => $status,
        'progress' => $progress
    ];
    
    wpsec_debug_log(sprintf(
        '📤 WPFort Webhook: Sending backup progress update - ID: %s, Status: %s, Progress: %d%%',
        $backup_id,
        $status,
        $progress
    ), 'info');
    
    $result = wpsec_send_webhook('backup-progress', $payload);
    
    // If the webhook was sent successfully, update the last sent time
    if ($result === true) {
        update_option($last_sent_option, $current_time);
    }
    
    return $result;
}

/**
 * Send backup complete webhook
 * 
 * @param string $backup_id The backup ID
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_backup_complete_webhook($backup_id) {
    // Get the current site domain
    $site_url = get_site_url();
    $domain = wp_parse_url($site_url, PHP_URL_HOST);
    
    $payload = [
        'domain' => $domain,
        'backup_id' => $backup_id
    ];
    
    wpsec_debug_log(sprintf(
        '📤 WPFort Webhook: Sending backup completion notification - ID: %s',
        $backup_id
    ), 'info');
    
    return wpsec_send_webhook('backup-complete', $payload);
}

/**
 * Send backup failed webhook
 * 
 * @param string $backup_id The backup ID
 * @param string $error_message The error message
 * @return bool|WP_Error True on success, WP_Error on failure
 */
function wpsec_send_backup_failed_webhook($backup_id, $error_message = '') {
    // Get the current site domain
    $site_url = get_site_url();
    $domain = wp_parse_url($site_url, PHP_URL_HOST);
    
    $payload = [
        'domain' => $domain,
        'backup_id' => $backup_id,
        'error_message' => $error_message
    ];
    
    wpsec_debug_log(sprintf(
        '📤 WPFort Webhook: Sending backup failure notification - ID: %s, Error: %s',
        $backup_id,
        $error_message
    ), 'info');
    
    return wpsec_send_webhook('backup-failed', $payload);
}
