HEX
Server: Apache
System: Linux d5123.usc1.stableserver.net 5.14.0-570.17.1.el9_6.x86_64 #1 SMP PREEMPT_DYNAMIC Sat May 24 12:53:17 EDT 2025 x86_64
User: d5123 (1001)
PHP: 8.4.21
Disabled: NONE
Upload Files
File: /home/d5123/myboofola_com/wp-content/plugins/onwebchat/includes/woocommerce-sync.php
<?php
/**
 * WooCommerce Product Sync Module
 * Syncs WooCommerce products to onWebChat for AI bot training
 */

if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly
}

class OnWebChat_WooCommerce_Sync {
    
    private $api_endpoint_prod = 'https://www.onwebchat.com/api/integrations/woocommerce';
    private $api_endpoint_dev = 'http://127.0.0.1:81/api/integrations/woocommerce';
    private $max_description_length = 1000;
    private $batch_size = 50;
    private $use_testing_mode;
    
    /**
     * Get the API endpoint based on testing mode
     * @return string
     */
    private function get_api_endpoint() {
        return $this->use_testing_mode ? $this->api_endpoint_dev : $this->api_endpoint_prod;
    }
    
    public function __construct() {
        // Read testing mode from global constant (defined in onwebchat.php)
        $this->use_testing_mode = defined('ONWEBCHAT_WC_TESTING_MODE') ? ONWEBCHAT_WC_TESTING_MODE : false;
        // Initialize settings
        add_action('admin_init', array($this, 'register_settings'));
        
        // Show authentication error notice globally (not just on WooCommerce tab)
        add_action('admin_notices', array($this, 'show_auth_error_notice'));
        
        // Product hooks - use WooCommerce hooks that fire AFTER meta data is saved
        add_action('woocommerce_update_product', array($this, 'on_product_update'), 10, 1);
        add_action('woocommerce_new_product', array($this, 'on_product_update'), 10, 1);
        
        // Handle product deletion (both trash and permanent delete)
        add_action('wp_trash_post', array($this, 'on_product_trash'), 10, 1);
        add_action('before_delete_post', array($this, 'on_product_delete'), 10, 2);
        
        // Bulk sync via WP Cron
        add_action('onwebchat_wc_bulk_sync_batch', array($this, 'process_bulk_sync_batch'));
        
        // Admin AJAX handlers
        add_action('wp_ajax_onwebchat_wc_sync_now', array($this, 'ajax_sync_existing_products'));
        add_action('wp_ajax_onwebchat_wc_regenerate_secret', array($this, 'ajax_regenerate_secret'));
        add_action('wp_ajax_onwebchat_wc_reset_sync_status', array($this, 'ajax_reset_sync_status'));
        add_action('wp_ajax_onwebchat_wc_connect', array($this, 'ajax_connect_woocommerce'));
        add_action('wp_ajax_onwebchat_wc_manual_process_batch', array($this, 'ajax_manual_process_batch'));
        add_action('wp_ajax_onwebchat_wc_get_sync_status', array($this, 'ajax_get_sync_status'));
        add_action('wp_ajax_onwebchat_wc_save_sync_enabled', array($this, 'ajax_save_sync_enabled'));
    }
    
    /**
     * AJAX: Connect WooCommerce with authentication
     */
    public function ajax_connect_woocommerce() {
        check_ajax_referer('onwebchat_wc_sync_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        $email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
        $password = isset($_POST['password']) ? sanitize_text_field($_POST['password']) : '';
        
        if (empty($email) || empty($password)) {
            wp_send_json_error('Email and password are required');
        }
        
        $result = $this->request_secret_with_auth($email, $password);
        
        if ($result['success']) {
            // Clear any previous authentication errors
            delete_transient('onwebchat_wc_auth_error');
            
            wp_send_json_success(array(
                'message' => 'WooCommerce sync connected successfully!'
            ));
        } else {
            wp_send_json_error($result['error']);
        }
    }
    
    /**
     * Show authentication error notice globally across all admin pages
     * (Hidden when already on WooCommerce tab since it has its own error message)
     */
    public function show_auth_error_notice() {
        $auth_error = get_transient('onwebchat_wc_auth_error');
        
        // Don't show if we're already on the WooCommerce tab (it has its own error message)
        $is_woocommerce_tab = isset($_GET['page']) && $_GET['page'] === 'onwebchat_settings' 
                            && isset($_GET['tab']) && $_GET['tab'] === 'woocommerce';
        
        if ($auth_error && class_exists('WooCommerce') && !$is_woocommerce_tab) {
            ?>
            <div class="notice notice-error">
                <p>
                    <strong>⚠️ onWebChat WooCommerce Sync Error:</strong> 
                    <?php echo esc_html($auth_error); ?>
                    <a href="<?php echo esc_url(admin_url('admin.php?page=onwebchat_settings&tab=woocommerce')); ?>" class="button button-small" style="margin-left: 10px;">
                        Fix Authentication
                    </a>
                </p>
            </div>
            <?php
        }
    }
    
    /**
     * Register WooCommerce sync settings
     */
    public function register_settings() {
        register_setting('onwebchat_wc_sync', 'onwebchat_wc_sync_enabled');
        register_setting('onwebchat_wc_sync', 'onwebchat_wc_sync_mode');
        register_setting('onwebchat_wc_sync', 'onwebchat_wc_sync_include_price');
        register_setting('onwebchat_wc_sync', 'onwebchat_wc_sync_secret');
        register_setting('onwebchat_wc_sync', 'onwebchat_wc_last_bulk_sync');
        register_setting('onwebchat_wc_sync', 'onwebchat_wc_excluded_categories');
    }
    
    /**
     * Hook: Product update (WooCommerce specific hook - fires AFTER all meta is saved)
     */
    public function on_product_update($product_id) {
        // Check if sync is enabled
        if (!get_option('onwebchat_wc_sync_enabled', false)) {
            return;
        }
        
        // Get product object (at this point all meta data including SKU is already saved)
        $product = wc_get_product($product_id);
        if (!$product) {
            return;
        }
        
        // Only sync published products
        if ($product->get_status() !== 'publish') {
            return;
        }
        
        // Check if product category is excluded
        if ($this->is_product_excluded($product)) {
            return;
        }
        
        // Prepare and send product data
        $product_data = $this->prepare_product_data($product);
        $this->send_product_upsert($product_data, $product_id);
    }
    
    /**
     * Hook: Product trash (when moved to trash)
     */
    public function on_product_trash($post_id) {
        // Check if it's a product
        if (get_post_type($post_id) !== 'product') {
            return;
        }
        
        if (!get_option('onwebchat_wc_sync_enabled', false)) {
            return;
        }
        
        // Send delete request when product is trashed
        $this->send_product_delete($post_id);
    }
    
    /**
     * Hook: Product permanent delete
     */
    public function on_product_delete($post_id, $post) {
        if ($post->post_type !== 'product') {
            return;
        }
        
        if (!get_option('onwebchat_wc_sync_enabled', false)) {
            return;
        }
        
        // Send delete request when product is permanently deleted
        $this->send_product_delete($post_id);
    }
    
    /**
     * Check if product is in excluded categories
     */
    private function is_product_excluded($product) {
        $excluded_categories = get_option('onwebchat_wc_excluded_categories', array());
        if (empty($excluded_categories)) {
            return false;
        }
        
        $product_categories = $product->get_category_ids();
        foreach ($product_categories as $cat_id) {
            if (in_array($cat_id, $excluded_categories)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Prepare product data for sync
     */
    private function prepare_product_data($product) {
        $sync_mode = get_option('onwebchat_wc_sync_mode', 'short_fallback_full');
        $include_price = get_option('onwebchat_wc_sync_include_price', false);
        
        // Get description based on sync mode
        $description = '';
        $short_description = strip_tags($product->get_short_description());
        
        if ($sync_mode === 'short_only') {
            $description = $short_description;
        } else if ($sync_mode === 'short_fallback_full') {
            if (!empty($short_description)) {
                $description = $short_description;
            } else {
                // Fallback to first 60-80 words of full description
                $full_description = strip_tags($product->get_description());
                $words = str_word_count($full_description, 2);
                $word_array = array_keys($words);
                
                if (count($word_array) > 80) {
                    $end_pos = $word_array[79];
                    $description = substr($full_description, 0, $end_pos) . '...';
                } else {
                    $description = $full_description;
                }
            }
        }
        
        // Enforce max length
        if (strlen($description) > $this->max_description_length) {
            $description = substr($description, 0, $this->max_description_length) . '...';
        }
        
        // Build text field for embeddings
        $sku = $product->get_sku();
        $categories = $this->get_product_category_names($product);
        $url = get_permalink($product->get_id());
        
        // Format: "Product: name\nSKU: sku (if available)\nDescription: description\nCategories: cat1, cat2\nURL: url"
        $text_parts = array();
        $text_parts[] = 'Product: ' . $product->get_name();
        
        if (!empty($sku)) {
            $text_parts[] = 'SKU: ' . $sku;
        }
        
        if (!empty($description)) {
            $text_parts[] = 'Description: ' . $description;
        }
        
        if (!empty($categories)) {
            $text_parts[] = 'Categories: ' . implode(', ', $categories);
        }
        
        $text_parts[] = 'URL: ' . $url;
        
        $text = implode("\n", $text_parts);
        
        // Prepare data array
        $data = array(
            'product_id' => $product->get_id(),
            'name' => $product->get_name(),
            'short_description' => trim($description),
            'url' => $url,
            'sku' => $sku,
            'categories' => $categories,
            'text' => $text,  // New field with formatted text for embeddings
        );
        
        // Optionally include price
        if ($include_price) {
            $data['price'] = $product->get_price();
            $data['regular_price'] = $product->get_regular_price();
            $data['sale_price'] = $product->get_sale_price();
        }
        
        return $data;
    }
    
    /**
     * Get product category names
     */
    private function get_product_category_names($product) {
        $categories = array();
        $category_ids = $product->get_category_ids();
        
        foreach ($category_ids as $cat_id) {
            $term = get_term($cat_id, 'product_cat');
            if ($term && !is_wp_error($term)) {
                $categories[] = $term->name;
            }
        }
        
        return $categories;
    }
    
    /**
     * Send batch of products to API (optimized)
     * @param array $products - Array of product data
     */
    private function send_product_batch($products) {
        $chatId = get_option('onwebchat_plugin_option');
        $chatId = (is_array($chatId) && isset($chatId['text_string'])) ? $chatId['text_string'] : '';
        
        if (empty($chatId)) {
            error_log('onWebChat WooCommerce Sync - Chat ID not configured');
            return false;
        }
        
        // Ensure we have a secret (must be obtained via authenticated connection in WooCommerce settings)
        $secret = $this->get_secret(false);
        if (empty($secret)) {
            error_log('onWebChat WooCommerce Sync - No secret configured. Please connect WooCommerce in the plugin settings.');
            return array(
                'success' => false,
                'error' => 'No secret configured. Please connect WooCommerce integration.',
                'needs_reconnect' => true
            );
        }
        
        // Extract key part (before first slash if present)
        $chatIdKey = explode('/', $chatId)[0];
        
        $endpoint = $this->get_api_endpoint() . '/product/batch';
        $payload = array(
            'site_id' => $chatIdKey,
            'site_url' => get_site_url(),
            'products' => $products
        );
        
        // Generate authentication headers (same as send_authenticated_request)
        $timestamp = time();
        $nonce = base64_encode(random_bytes(16));
        $body_json = wp_json_encode($payload);
        
        // Create signature: HMAC_SHA256(secret, site_id.timestamp.nonce.body)
        $message = $chatIdKey . '.' . $timestamp . '.' . $nonce . '.' . $body_json;
        $signature = hash_hmac('sha256', $message, $secret);
        
        // Send request
        $request_args = array(
            'method' => 'POST',
            'timeout' => 30, // Longer timeout for batch operations
            'headers' => array(
                'Content-Type' => 'application/json',
                'X-OWC-SiteId' => $chatIdKey,
                'X-OWC-Timestamp' => $timestamp,
                'X-OWC-Nonce' => $nonce,
                'X-OWC-Signature' => $signature,
            ),
            'body' => $body_json,
        );
        
        // Disable SSL verification for local dev server
        if ($this->use_testing_mode) {
            $request_args['sslverify'] = false;
        }
        
        $response = wp_remote_post($endpoint, $request_args);
        
        if (is_wp_error($response)) {
            error_log('onWebChat WooCommerce Sync - Batch sync error: ' . $response->get_error_message());
            return array(
                'success' => false,
                'error' => 'Network error: ' . $response->get_error_message()
            );
        }
        
        $response_code = wp_remote_retrieve_response_code($response);
        $body = json_decode(wp_remote_retrieve_body($response), true);
        
        // If authentication failed (401), the secret is invalid or out of sync
        if ($response_code === 401) {
            // Clear the invalid secret
            delete_option('onwebchat_wc_sync_secret');
            
            error_log('onWebChat WooCommerce Sync - Authentication failed (401): Secret is invalid or out of sync. Please reconnect WooCommerce in the plugin settings.');
            
            // Store admin notice about authentication failure
            set_transient('onwebchat_wc_auth_error', 'Authentication failed. Your WooCommerce secret is invalid or out of sync. Please reconnect WooCommerce integration in the plugin settings.', 3600);
            
            return array(
                'success' => false,
                'error' => 'Authentication failed. Secret is invalid. Please reconnect WooCommerce integration.',
                'needs_reconnect' => true
            );
        }
        
        if ($response_code === 200 && isset($body['success']) && $body['success']) {
            // Clear any previous auth errors on success
            delete_transient('onwebchat_wc_auth_error');
            return $body; // Return full response with stats
        }
        
        $error_msg = 'Batch sync failed';
        if (isset($body['error'])) {
            $error_msg .= ': ' . $body['error'];
        }
        error_log('onWebChat WooCommerce Sync - ' . $error_msg . ' - Response: ' . print_r($body, true));
        
        return array(
            'success' => false,
            'error' => $error_msg
        );
    }
    
    /**
     * Send sync completion notification to server (triggers Angular modal)
     */
    private function send_sync_completion_notification($total_stats) {
        $chatId = get_option('onwebchat_plugin_option');
        $chatId = (is_array($chatId) && isset($chatId['text_string'])) ? $chatId['text_string'] : '';
        
        if (empty($chatId)) {
            error_log('onWebChat WooCommerce Sync - Chat ID not configured');
            return false;
        }
        
        $secret = $this->get_secret(false);
        if (empty($secret)) {
            error_log('onWebChat WooCommerce Sync - No secret configured');
            return false;
        }
        
        $chatIdKey = explode('/', $chatId)[0];
        
        $endpoint = $this->get_api_endpoint() . '/product/sync-complete';
        $payload = array(
            'site_id' => $chatIdKey,
            'stats' => $total_stats
        );
        
        // Generate authentication headers
        $timestamp = time();
        $nonce = base64_encode(random_bytes(16));
        $body_json = wp_json_encode($payload);
        $message = $chatIdKey . '.' . $timestamp . '.' . $nonce . '.' . $body_json;
        $signature = hash_hmac('sha256', $message, $secret);
        
        $request_args = array(
            'method' => 'POST',
            'timeout' => 10,
            'headers' => array(
                'Content-Type' => 'application/json',
                'X-OWC-SiteId' => $chatIdKey,
                'X-OWC-Timestamp' => $timestamp,
                'X-OWC-Nonce' => $nonce,
                'X-OWC-Signature' => $signature,
            ),
            'body' => $body_json,
        );
        
        if ($this->use_testing_mode) {
            $request_args['sslverify'] = false;
        }
        
        $response = wp_remote_post($endpoint, $request_args);
        
        if (is_wp_error($response)) {
            error_log('onWebChat WooCommerce Sync - Completion notification failed: ' . $response->get_error_message());
            return false;
        }
        
        $response_code = wp_remote_retrieve_response_code($response);
        
        // If authentication failed (401), the secret is invalid or out of sync
        if ($response_code === 401) {
            delete_option('onwebchat_wc_sync_secret');
            error_log('onWebChat WooCommerce Sync - Completion notification authentication failed (401): Secret is invalid. Please reconnect WooCommerce.');
            set_transient('onwebchat_wc_auth_error', 'Authentication failed. Your WooCommerce secret is invalid or out of sync. Please reconnect WooCommerce integration in the plugin settings.', 3600);
            return false;
        }
        
        if ($response_code >= 200 && $response_code < 300) {
            error_log('onWebChat WooCommerce Sync - Completion notification sent successfully');
            return true;
        }
        
        error_log('onWebChat WooCommerce Sync - Completion notification failed with code: ' . $response_code);
        return false;
    }
    
    /**
     * Send product upsert to server (uses batch endpoint with single product)
     */
    private function send_product_upsert($product_data, $product_id) {
        // Use batch endpoint with single product
        $result = $this->send_product_batch(array($product_data));
        
        if ($result && isset($result['success']) && $result['success']) {
            // Clear any previous errors
            delete_post_meta($product_id, '_onwebchat_sync_error');
            update_post_meta($product_id, '_onwebchat_last_sync', current_time('timestamp'));
            return true;
        } else {
            // Log error if batch failed
            $error_message = 'Failed to sync product';
            if ($result && isset($result['error'])) {
                $error_message = $result['error'];
            } elseif (!$result) {
                $error_message = 'Batch sync request failed';
            }
            $this->log_error($product_id, $error_message);
            return false;
        }
    }
    
    /**
     * Send product delete to server
     */
    private function send_product_delete($product_id) {
        $chatId = get_option('onwebchat_plugin_option');
        $chatId = (is_array($chatId) && isset($chatId['text_string'])) ? $chatId['text_string'] : '';
        
        if (empty($chatId)) {
            return false;
        }
        
        // Extract key part (before first slash if present)
        $chatIdKey = explode('/', $chatId)[0];
        
        $endpoint = $this->get_api_endpoint() . '/product/delete';
        $payload = array(
            'site_id' => $chatIdKey,  // Use key part only
            'site_url' => get_site_url(),
            'product_id' => $product_id
        );
        
        $this->send_authenticated_request($endpoint, $payload, $product_id);
    }
    
    /**
     * Get cached secret from local options
     * @param {bool} force_refresh - Not used (kept for compatibility), secret must be obtained via authenticated request
     */
    private function get_secret($force_refresh = false) {
        // Always return cached secret - never fetch automatically
        // Secret must be obtained via authenticated request in WooCommerce settings
        $secret = get_option('onwebchat_wc_sync_secret');
        
        if (!empty($secret)) {
            return $secret;
        }
        
        // No secret available - user must authenticate in WooCommerce settings
        return null;
    }
    
    /**
     * Request secret from server with authentication
     * This is called when user clicks "Connect WooCommerce" with their password
     * 
     * @param {string} email - User's onWebChat email
     * @param {string} password - User's onWebChat password
     * @return {array} - ['success' => bool, 'secret' => string, 'error' => string]
     */
    public function request_secret_with_auth($email, $password) {
        $chatId = get_option('onwebchat_plugin_option');
        $chatId = (is_array($chatId) && isset($chatId['text_string'])) ? $chatId['text_string'] : '';
        
        if (empty($chatId)) {
            return array('success' => false, 'error' => 'No Chat ID configured');
        }
        
        // Extract key part (before first slash if present)
        $key = explode('/', $chatId)[0];
        
        // Request secret from server with authentication
        $secret_endpoint = $this->get_api_endpoint() . '/secret';
        
        $response = wp_remote_post($secret_endpoint, array(
            'timeout' => 15,
            'sslverify' => !$this->use_testing_mode,
            'headers' => array(
                'Content-Type' => 'application/json',
            ),
            'body' => wp_json_encode(array(
                'email' => $email,
                'password' => $password,
                'site_key' => $key,
                'version' => defined('ONWEBCHAT_PLUGIN_VERSION') ? ONWEBCHAT_PLUGIN_VERSION : '',
            )),
        ));
        
        if (is_wp_error($response)) {
            $error_message = $response->get_error_message();
            error_log('onWebChat WooCommerce Sync - Connection error: ' . $error_message);
            return array('success' => false, 'error' => 'Connection failed: ' . $error_message);
        }
        
        $status_code = wp_remote_retrieve_response_code($response);
        $response_body_raw = wp_remote_retrieve_body($response);
        $body = json_decode($response_body_raw, true);
        
        // Log response for debugging
        error_log('onWebChat WooCommerce Sync - API response: Status=' . $status_code . ', Body=' . substr($response_body_raw, 0, 500));
        
        // Handle specific HTTP status codes
        if ($status_code === 401) {
            return array('success' => false, 'error' => 'Invalid email or password');
        }
        
        if ($status_code === 403) {
            return array('success' => false, 'error' => 'You do not have access to this site');
        }
        
        // Success case
        if ($status_code >= 200 && $status_code < 300 && isset($body['success']) && $body['success']) {
            $secret = isset($body['secret']) ? $body['secret'] : null;
            if (empty($secret)) {
                error_log('onWebChat WooCommerce Sync - Success response but no secret provided');
                return array('success' => false, 'error' => 'Server response missing secret');
            }
            update_option('onwebchat_wc_sync_secret', $secret);
            return array('success' => true, 'secret' => $secret);
        }
        
        // Extract error message from various possible response formats
        $error_message = 'Unknown error';
        
        if (is_array($body)) {
            // Try different possible error fields
            if (isset($body['error'])) {
                $error_message = is_string($body['error']) ? $body['error'] : json_encode($body['error']);
            } elseif (isset($body['message'])) {
                $error_message = is_string($body['message']) ? $body['message'] : json_encode($body['message']);
            } elseif (isset($body['errors']) && is_array($body['errors'])) {
                $error_message = implode(', ', $body['errors']);
            }
        } elseif (!empty($response_body_raw)) {
            // If body is not JSON or empty, use raw response (truncated)
            $error_message = 'Server returned: ' . substr(strip_tags($response_body_raw), 0, 200);
        }
        
        // Include status code in error message if not already included
        if ($status_code && strpos($error_message, 'HTTP') === false) {
            $error_message = 'HTTP ' . $status_code . ': ' . $error_message;
        }
        
        error_log('onWebChat WooCommerce Sync - Connection failed: ' . $error_message);
        return array('success' => false, 'error' => $error_message);
    }
    
    /**
     * Send authenticated request with HMAC signature
     */
    private function send_authenticated_request($endpoint, $payload, $product_id = null) {
        // Get cached secret (must be obtained via authenticated connection in WooCommerce settings)
        $secret = $this->get_secret(false);
        
        if (empty($secret)) {
            if ($product_id) {
                $this->log_error($product_id, 'No secret configured. Please connect WooCommerce in the plugin settings.');
            }
            return array('success' => false, 'error' => 'Secret not available. Please connect WooCommerce in plugin settings.');
        }
        
        $chatId = get_option('onwebchat_plugin_option');
        $chatId = (is_array($chatId) && isset($chatId['text_string'])) ? $chatId['text_string'] : '';
        
        // Extract key part (before first slash if present) for consistency with server
        // e.g., "5f02c87b60726a4663b25463a424a034/1/1" -> "5f02c87b60726a4663b25463a424a034"
        $chatIdKey = explode('/', $chatId)[0];
        
        // Generate authentication headers
        $timestamp = time();
        $nonce = base64_encode(random_bytes(16));
        $body_json = wp_json_encode($payload);
        
        // Create signature: HMAC_SHA256(secret, site_id.timestamp.nonce.body)
        // IMPORTANT: Use the key part (not full chat_id) to match server-side verification
        $message = $chatIdKey . '.' . $timestamp . '.' . $nonce . '.' . $body_json;
        $signature = hash_hmac('sha256', $message, $secret);
        
        // Send request
        $request_args = array(
            'method' => 'POST',
            'timeout' => 10,
            'headers' => array(
                'Content-Type' => 'application/json',
                'X-OWC-SiteId' => $chatIdKey,  // Use key part only
                'X-OWC-Timestamp' => $timestamp,
                'X-OWC-Nonce' => $nonce,
                'X-OWC-Signature' => $signature,
            ),
            'body' => $body_json,
        );
        
        // Disable SSL verification for local dev server
        if ($this->use_testing_mode) {
            $request_args['sslverify'] = false;
        }
        
        $response = wp_remote_post($endpoint, $request_args);
        
        // Handle response
        if (is_wp_error($response)) {
            $error_message = $response->get_error_message();
            if ($product_id) {
                $this->log_error($product_id, $error_message);
            }
            return array('success' => false, 'error' => $error_message);
        }
        
        $status_code = wp_remote_retrieve_response_code($response);
        
        // Success
        if ($status_code >= 200 && $status_code < 300) {
            return array('success' => true);
        }
        
        // If authentication failed (401), the secret may be invalid
        if ($status_code === 401) {
            // Clear the invalid secret
            delete_option('onwebchat_wc_sync_secret');
            
            if ($product_id) {
                $this->log_error($product_id, 'Authentication failed. Please reconnect WooCommerce in the plugin settings.');
            }
            return array('success' => false, 'error' => 'Authentication failed. Please reconnect WooCommerce in plugin settings.');
        }
        
        // Error
        $error_body = wp_remote_retrieve_body($response);
        if ($product_id) {
            $this->log_error($product_id, "HTTP $status_code: $error_body");
        }
        
        return array('success' => false, 'error' => "HTTP $status_code", 'status_code' => $status_code);
    }
    
    /**
     * Log sync error to product meta
     */
    private function log_error($product_id, $error_message) {
        update_post_meta($product_id, '_onwebchat_sync_error', array(
            'message' => $error_message,
            'timestamp' => current_time('timestamp')
        ));
    }
    
    /**
     * AJAX: Start bulk sync
     */
    public function ajax_sync_existing_products() {
        check_ajax_referer('onwebchat_wc_sync_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        // Check if sync is already in progress
        if (get_option('onwebchat_wc_bulk_in_progress', false)) {
            wp_send_json_error('A sync is already in progress. Please wait for it to complete.');
        }
        
        // Rate limiting: prevent syncing more than once every 5 minutes
        $last_sync_time = get_option('onwebchat_wc_last_sync_start', 0);
        $cooldown_period = 5 * 60; // 5 minutes in seconds //also in the file woocommerce.php // 5 * 60
        $time_since_last_sync = time() - $last_sync_time;
        
        if ($time_since_last_sync < $cooldown_period) {
            $wait_time = $cooldown_period - $time_since_last_sync;
            $minutes = ceil($wait_time / 60);
            wp_send_json_error('Please wait ' . $minutes . ' minute(s) before syncing again.');
        }
        
        // Store the current sync start time
        update_option('onwebchat_wc_last_sync_start', time());
        
        // Reset bulk sync progress
        update_option('onwebchat_wc_bulk_page', 0);
        update_option('onwebchat_wc_bulk_done', 0);
        
        // Count total products
        $args = array(
            'post_type' => 'product',
            'post_status' => 'publish',
            'posts_per_page' => -1,
            'fields' => 'ids',
        );
        
        $products = get_posts($args);
        $total = count($products);
        
        update_option('onwebchat_wc_bulk_total', $total);
        update_option('onwebchat_wc_bulk_done', 0); // Initialize progress counter
        update_option('onwebchat_wc_bulk_in_progress', true);
        
        // Process sync directly instead of using unreliable WP Cron
        $sync_result = $this->do_bulk_sync_all();
        
        wp_send_json_success(array(
            'message' => 'Bulk sync completed',
            'total' => $total,
            'result' => $sync_result
        ));
    }
    
    /**
     * Process all products in bulk sync directly (not via cron)
     */
    private function do_bulk_sync_all() {
        $total = get_option('onwebchat_wc_bulk_total', 0);
        $page = 0;
        $total_done = 0;
        $all_stats = array('created' => 0, 'updated' => 0, 'skipped' => 0, 'errors' => 0);
        
        // Process all products in batches
        while (true) {
            $args = array(
                'post_type' => 'product',
                'post_status' => 'publish',
                'posts_per_page' => $this->batch_size,
                'paged' => $page + 1,
                'orderby' => 'ID',
                'order' => 'ASC',
            );
            
            $query = new WP_Query($args);
            
            if (!$query->have_posts()) {
                break;
            }
            
            // Collect products in this batch
            $products_batch = array();
            foreach ($query->posts as $post) {
                $product = wc_get_product($post->ID);
                if ($product && !$this->is_product_excluded($product)) {
                    $products_batch[] = $this->prepare_product_data($product);
                }
            }
            
            // Send batch
            if (!empty($products_batch)) {
                $result = $this->send_product_batch($products_batch);
                if ($result && isset($result['stats'])) {
                    $total_done += $result['stats']['created'] + $result['stats']['updated'] + $result['stats']['skipped'];
                    $all_stats['created'] += $result['stats']['created'];
                    $all_stats['updated'] += $result['stats']['updated'];
                    $all_stats['skipped'] += $result['stats']['skipped'];
                    $all_stats['errors'] += $result['stats']['errors'];
                } else {
                    // Fallback
                    $total_done += count($products_batch);
                }
                
                // Update progress after each batch so AJAX polling can see it
                update_option('onwebchat_wc_bulk_done', $total_done);
                
                // Wait 4 seconds before next batch
                sleep(4);
            }
            
            wp_reset_postdata();
            $page++;
            
            // Safety check - don't loop forever
            if ($page > 100) {
                break;
            }
        }
        
        // Send completion notification to Angular dashboard with total stats
        $this->send_sync_completion_notification($all_stats);
        
        // Mark sync as complete
        update_option('onwebchat_wc_bulk_in_progress', false);
        update_option('onwebchat_wc_bulk_done', $total_done);
        update_option('onwebchat_wc_last_bulk_sync', current_time('timestamp'));
        
        return array(
            'done' => $total_done,
            'total' => $total,
            'stats' => $all_stats
        );
    }
    
    /**
     * Process bulk sync batch (via WP Cron) - Uses batch API endpoint
     */
    public function process_bulk_sync_batch() {
        error_log('onWebChat WooCommerce Sync - process_bulk_sync_batch called');
        
        if (!get_option('onwebchat_wc_bulk_in_progress', false)) {
            error_log('onWebChat WooCommerce Sync - Sync not in progress, exiting');
            return;
        }
        
        $page = get_option('onwebchat_wc_bulk_page', 0);
        $done = get_option('onwebchat_wc_bulk_done', 0);
        $total = get_option('onwebchat_wc_bulk_total', 0);
        
        error_log('onWebChat WooCommerce Sync - Starting batch: page=' . $page . ', done=' . $done . ', total=' . $total);
        
        // Get batch of products
        $args = array(
            'post_type' => 'product',
            'post_status' => 'publish',
            'posts_per_page' => $this->batch_size,
            'paged' => $page + 1,
            'orderby' => 'ID',
            'order' => 'ASC',
        );
        
        $query = new WP_Query($args);
        
        if ($query->have_posts()) {
            // Collect all products in this batch
            $products_batch = array();
            
            foreach ($query->posts as $post) {
                $product = wc_get_product($post->ID);
                if ($product && !$this->is_product_excluded($product)) {
                    $products_batch[] = $this->prepare_product_data($product);
                }
            }
            
            // Send entire batch in one request
            $batch_done = 0;
            if (!empty($products_batch)) {
                $result = $this->send_product_batch($products_batch);
                error_log('onWebChat WooCommerce Sync - Batch result: ' . print_r($result, true));
                if ($result && isset($result['stats'])) {
                    // Count created + updated + skipped as "done"
                    $batch_done = $result['stats']['created'] + $result['stats']['updated'] + $result['stats']['skipped'];
                    error_log('onWebChat WooCommerce Sync - Batch done: ' . $batch_done);
                } else {
                    // Fallback: assume all sent
                    $batch_done = count($products_batch);
                    error_log('onWebChat WooCommerce Sync - No stats in result, using fallback count: ' . $batch_done);
                }
            }
            
            // Update progress
            $new_done = $done + $batch_done;
            error_log('onWebChat WooCommerce Sync - Progress update: done=' . $done . ' + batch_done=' . $batch_done . ' = new_done=' . $new_done . ' / total=' . $total);
            update_option('onwebchat_wc_bulk_page', $page + 1);
            update_option('onwebchat_wc_bulk_done', $new_done);
            
            // Check if we've processed all products
            if ($new_done >= $total || !$query->have_posts()) {
                // Sync complete
                error_log('onWebChat WooCommerce Sync - Marking sync as complete');
                update_option('onwebchat_wc_bulk_in_progress', false);
                update_option('onwebchat_wc_last_bulk_sync', current_time('timestamp'));
                update_option('onwebchat_wc_bulk_done', $total); // Ensure it shows 100%
            } else {
                // Schedule next batch
                error_log('onWebChat WooCommerce Sync - Scheduling next batch in 60 seconds');
                wp_schedule_single_event(time() + 60, 'onwebchat_wc_bulk_sync_batch');
            }
        } else {
            // No more products - sync complete
            update_option('onwebchat_wc_bulk_in_progress', false);
            update_option('onwebchat_wc_last_bulk_sync', current_time('timestamp'));
            update_option('onwebchat_wc_bulk_done', $total); // Ensure it shows 100%
        }
        
        wp_reset_postdata();
    }
    
    /**
     * AJAX: Regenerate secret (fetch from server)
     */
    public function ajax_regenerate_secret() {
        check_ajax_referer('onwebchat_wc_sync_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        // Clear cached secret - user will need to re-authenticate
        delete_option('onwebchat_wc_sync_secret');
        
        // Clear any authentication error notices
        delete_transient('onwebchat_wc_auth_error');
        
        wp_send_json_success(array(
            'message' => 'Secret cleared. Please reconnect WooCommerce with your credentials.',
            'needs_reconnect' => true
        ));
    }
    
    /**
     * AJAX: Reset sync status
     */
    public function ajax_reset_sync_status() {
        check_ajax_referer('onwebchat_wc_sync_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        $total = get_option('onwebchat_wc_bulk_total', 0);
        
        // Mark sync as complete
        update_option('onwebchat_wc_bulk_in_progress', false);
        update_option('onwebchat_wc_bulk_done', $total);
        update_option('onwebchat_wc_last_bulk_sync', current_time('timestamp'));
        
        wp_send_json_success(array(
            'message' => 'Sync status reset successfully'
        ));
    }
    
    /**
     * AJAX handler to get current sync status
     */
    public function ajax_get_sync_status() {
        check_ajax_referer('onwebchat_wc_sync_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        $in_progress = get_option('onwebchat_wc_bulk_in_progress', false);
        $done = get_option('onwebchat_wc_bulk_done', 0);
        $total = get_option('onwebchat_wc_bulk_total', 0);
        
        wp_send_json_success(array(
            'in_progress' => $in_progress,
            'done' => $done,
            'total' => $total
        ));
    }
    
    /**
     * AJAX handler to save sync enabled setting
     */
    public function ajax_save_sync_enabled() {
        check_ajax_referer('onwebchat_wc_sync_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        $sync_enabled = isset($_POST['sync_enabled']) && $_POST['sync_enabled'] === '1';
        
        update_option('onwebchat_wc_sync_enabled', $sync_enabled);
        
        wp_send_json_success(array(
            'message' => $sync_enabled ? 'WooCommerce product sync enabled' : 'WooCommerce product sync disabled',
            'enabled' => $sync_enabled
        ));
    }
    
    /**
     * Get sync status for admin display
     */
    public function get_sync_status() {
        $last_sync = get_option('onwebchat_wc_last_bulk_sync', 0);
        $in_progress = get_option('onwebchat_wc_bulk_in_progress', false);
        $done = get_option('onwebchat_wc_bulk_done', 0);
        $total = get_option('onwebchat_wc_bulk_total', 0);
        
        return array(
            'last_sync' => $last_sync,
            'in_progress' => $in_progress,
            'done' => $done,
            'total' => $total,
        );
    }
    
    /**
     * AJAX: Manually process batch (for debugging)
     */
    public function ajax_manual_process_batch() {
        check_ajax_referer('onwebchat_wc_sync_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        // Manually trigger the cron job
        error_log('onWebChat WooCommerce Sync - Manual batch process triggered via AJAX');
        $this->process_bulk_sync_batch();
        
        // Return current status
        $status = $this->get_sync_status();
        wp_send_json_success(array(
            'message' => 'Batch processed',
            'status' => $status
        ));
    }
}

// Initialize the sync module
global $onwebchat_wc_sync;
$onwebchat_wc_sync = new OnWebChat_WooCommerce_Sync();