HEX
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.4.30
System: Linux iZj6c1151k3ad370bosnmsZ 3.10.0-1160.76.1.el7.x86_64 #1 SMP Wed Aug 10 16:21:17 UTC 2022 x86_64
User: root (0)
PHP: 7.4.30
Disabled: NONE
Upload Files
File: /var/www/html/www.winghung.com/wp-content/plugins/mxchat-basic/admin/class-ajax-handler.php
<?php
/**
 * File: admin/class-ajax-handler.php
 *
 * Handles all AJAX requests for MxChat admin functionality
 */

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

class MxChat_Ajax_Handler {
    
    private $pinecone_manager = null;
        
    /**
     * Constructor - Register all AJAX hooks
     */
    public function __construct() {
        $this->mxchat_init_ajax_hooks();
    }
    

    /**
     * Register all AJAX action hooks
     */
private function mxchat_init_ajax_hooks() {
    // Settings AJAX
    add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
    add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
    add_action('wp_ajax_migrate_pinecone_settings', array($this, 'ajax_migrate_pinecone_settings'));
    
    // License AJAX 
    add_action('wp_ajax_mxchat_handle_activate_license', array($this, 'mxchat_handle_activate_license'));
    add_action('wp_ajax_mxchat_check_license_status', array($this, 'mxchat_check_license_status'));
    add_action('wp_ajax_mxchat_deactivate_license', array($this, 'mxchat_deactivate_license'));
    
    // Actions & Intents AJAX
    add_action('wp_ajax_mxchat_toggle_action', array($this, 'mxchat_toggle_action'));
    add_action('wp_ajax_mxchat_update_intent_threshold', array($this, 'mxchat_update_intent_threshold'));
    
    add_action('wp_ajax_mxchat_save_selected_bot', array($this, 'mxchat_save_selected_bot'));
}

    // ========================================
    // SETTINGS AJAX HANDLERS
    // ========================================

/**
 * Validates and saves chat settings via AJAX request
 */
public function mxchat_save_setting_callback() {
    check_ajax_referer('mxchat_save_setting_nonce');
    if (!current_user_can('manage_options')) {
        ('MXChat Save: Unauthorized access attempt');
        wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
    }

    $name = isset($_POST['name']) ? $_POST['name'] : '';
    // Strip slashes from the value before saving
    $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';

    //error_log('MXChat Save: Processing field name: ' . $name);
    //error_log('MXChat Save: Field value: ' . $value);

    if (empty($name)) {
        //error_log('MXChat Save: Empty field name detected');
        wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
    }

    // Load the full options array
    $options = get_option('mxchat_options', []);
    //error_log('MXChat Save: Current options array: ' . print_r($options, true));

    // Handle special cases
    switch ($name) {
        case 'model':
            //error_log('MXChat Save: Processing model selection');
            //error_log('MXChat Save: Model value received: ' . $value);
            //error_log('MXChat Save: Value type: ' . gettype($value));
            //error_log('MXChat Save: Value length: ' . strlen($value));
            //error_log('MXChat Save: Value === "openrouter": ' . ($value === 'openrouter' ? 'YES' : 'NO'));
            
            // Allow 'openrouter' or validate against whitelist
            if ($value === 'openrouter') {
                //error_log('MXChat Save: Setting model to openrouter');
                $options['model'] = 'openrouter';
            } else {
                //error_log('MXChat Save: Checking against whitelist');
                 $allowed_models = array(
                            'gemini-2.0-flash', 'gemini-2.0-flash-lite', 'gemini-1.5-pro', 'gemini-1.5-flash',
                            'grok-4-0709', 'grok-3-beta', 'grok-3-fast-beta', 'grok-3-mini-beta', 
                            'grok-3-mini-fast-beta', 'grok-2',
                            'deepseek-chat',
                            'claude-sonnet-4-5-20250929', 'claude-opus-4-1-20250805', 'claude-haiku-4-5-20251001',
                            'claude-opus-4-20250514', 'claude-sonnet-4-20250514', 'claude-3-7-sonnet-20250219',
                            'claude-3-5-sonnet-20241022', 'claude-3-opus-20240229', 'claude-3-sonnet-20240229',
                            'claude-3-haiku-20240307',
                            'gpt-5', 'gpt-5-mini', 'gpt-5-nano', 'gpt-4.1-2025-04-14',
                            'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo',
                        );
                
                //error_log('MXChat Save: in_array result: ' . (in_array($value, $allowed_models) ? 'YES' : 'NO'));
                
                if (in_array($value, $allowed_models)) {
                    //error_log('MXChat Save: Model is in whitelist, saving');
                    $options['model'] = sanitize_text_field($value);
                } else {
                    //error_log('MXChat Save: Invalid model rejected: ' . $value);
                    //error_log('MXChat Save: Allowed models: ' . print_r($allowed_models, true));
                    wp_send_json_error(['message' => esc_html__('Invalid model selected', 'mxchat')]);
                    return;
                }
            }
            break;
            
        case 'openrouter_selected_model':
            //error_log('MXChat Save: Processing OpenRouter model: ' . $value);
            $options['openrouter_selected_model'] = sanitize_text_field($value);
            // Force immediate save for new keys
            //error_log('MXChat Save: OpenRouter model saved immediately');
            break;
        
        case 'openrouter_selected_model_name':
            //error_log('MXChat Save: Processing OpenRouter model name: ' . $value);
            $options['openrouter_selected_model_name'] = sanitize_text_field($value);
            // Force immediate save for new keys
            //error_log('MXChat Save: OpenRouter model name saved immediately');
            break;
            
        case 'openrouter_api_key':
            //error_log('MXChat Save: Processing OpenRouter API key');
            $options['openrouter_api_key'] = sanitize_text_field($value);
            break;
            
        // REMOVED DUPLICATE case 'openrouter_selected_model_name' HERE!
            
        case 'additional_popular_questions':
            //error_log('MXChat Save: Processing additional_popular_questions');
            $questions = json_decode($value, true); // No need for stripslashes here
            if (is_array($questions)) {
                $options[$name] = $questions;
                // Also update old option for backwards compatibility
                update_option('additional_popular_questions', $questions);
                //error_log('MXChat Save: Saved ' . count($questions) . ' additional questions');
            } else {
                //error_log('MXChat Save: Failed to decode questions JSON');
            }
            break;
        case 'email_blocker_header_content':
            //error_log('MXChat Save: Processing email_blocker_header_content');
            // Allow HTML content but sanitize it safely
            $options[$name] = wp_kses_post($value);
            break;
        case 'email_blocker_button_text':
            //error_log('MXChat Save: Processing email_blocker_button_text');
            $options[$name] = sanitize_text_field($value);
            break;
        case 'name_field_placeholder':
            //error_log('MXChat Save: Processing name_field_placeholder');
            $options[$name] = sanitize_text_field($value);
            break;
        case 'similarity_threshold':
            //error_log('MXChat Save: Processing similarity_threshold');
            // Save to the options array
            $options[$name] = $value;
            break;
        case 'user_message_bg_color':
        case 'user_message_font_color':
        case 'bot_message_bg_color':
        case 'bot_message_font_color':
        case 'top_bar_bg_color':
        case 'send_button_font_color':
        case 'chatbot_background_color':
        case 'icon_color':
        case 'chat_input_font_color':
        case 'live_agent_message_bg_color':
        case 'live_agent_message_font_color':
        case 'mode_indicator_bg_color':
        case 'mode_indicator_font_color':
        case 'toolbar_icon_color':
        case 'quick_questions_toggle_color': 
            //error_log('MXChat Save: Processing color value: ' . $name);
            // Store color values directly
            $options[$name] = $value;
            break;
        case 'live_agent_status':
            //error_log('MXChat Save: Processing live_agent_status');
            // Set the new value
            $options[$name] = ($value === 'on') ? 'on' : 'off';
            break;
        case 'enable_woocommerce_integration':
            //error_log('MXChat Save: Processing enable_woocommerce_integration');
            // Handle values that used to be 1/0
            $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
            break;
        default:
            // First check for rate limits settings
            if (strpos($name, 'mxchat_options[rate_limits]') !== false) {
                //error_log('MXChat Save: Detected rate_limits field: ' . $name);

                // Extract role ID and setting from the name
                preg_match('/\[rate_limits\]\[(.*?)\]\[(.*?)\]/', $name, $matches);
                //error_log('MXChat Save: Regex matches: ' . print_r($matches, true));

                if (isset($matches[1]) && isset($matches[2])) {
                    $role_id = $matches[1];
                    $setting_key = $matches[2]; // limit, timeframe, or message

                    //error_log('MXChat Save: Role ID = ' . $role_id . ', Setting Key = ' . $setting_key);

                    // Initialize rate_limits if it doesn't exist
                    if (!isset($options['rate_limits'])) {
                       // //error_log('MXChat Save: Initializing rate_limits array');
                        $options['rate_limits'] = [];
                    }

                    // Initialize role settings if it doesn't exist
                    if (!isset($options['rate_limits'][$role_id])) {
                        //error_log('MXChat Save: Initializing rate_limits for role: ' . $role_id);
                        $options['rate_limits'][$role_id] = [
                            'limit' => ($role_id === 'logged_out') ? '10' : '100',
                            'timeframe' => 'daily',
                            'message' => 'Rate limit exceeded. Please try again later.'
                        ];
                    }

                    // Update the specific setting
                    $options['rate_limits'][$role_id][$setting_key] = $value;
                    //error_log('MXChat Save: Updated rate_limits[' . $role_id . '][' . $setting_key . '] = ' . $value);
                } else {
                    //error_log('MXChat Save: Failed to parse rate_limits pattern: ' . $name);
                }
            }
            // Then check for role rate limits (old format)
            else if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) {
                //error_log('MXChat Save: Processing role_rate_limits field: ' . $name);
                // Extract role ID from the name
                preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches);
                //error_log('MXChat Save: Regex matches: ' . print_r($matches, true));

                if (isset($matches[1])) {
                    $role_id = $matches[1];
                    // Initialize role_rate_limits if it doesn't exist
                    if (!isset($options['role_rate_limits'])) {
                        //error_log('MXChat Save: Initializing role_rate_limits array');
                        $options['role_rate_limits'] = [];
                    }
                    // Update the specific role's rate limit
                    $options['role_rate_limits'][$role_id] = sanitize_text_field($value);
                    //error_log('MXChat Save: Updated role_rate_limits[' . $role_id . '] = ' . $value);
                } else {
                    //error_log('MXChat Save: Failed to parse role_rate_limits pattern: ' . $name);
                }
            }
            // Handle toggles
            else if (strpos($name, 'toggle') !== false || in_array($name, [
                'chat_persistence_toggle',
                'privacy_toggle',
                'complianz_toggle',
                'chat_toolbar_toggle',
                'show_pdf_upload_button',
                'show_word_upload_button',
                'enable_streaming_toggle',
                'contextual_awareness_toggle',
                'enable_email_block',
                'enable_name_field'
            ])) {
                //error_log('MXChat Save: Processing toggle: ' . $name);
                $options[$name] = ($value === 'on') ? 'on' : 'off';
            } else {
                //error_log('MXChat Save: Processing standard field: ' . $name);
                // Store all other values directly
                $options[$name] = $value;
            }
            break;
    }

    // Save all updates to the options array
    $updated = update_option('mxchat_options', $options);
    //error_log('MXChat Save: Update result: ' . ($updated ? 'success' : 'unchanged') . ' for field: ' . $name);
    //error_log('MXChat Save: Updated options array: ' . print_r($options, true));

    // Always return success even if WordPress says nothing changed
    // (which happens when the value is the same as before)
    wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
}

/**
 * Save the selected bot for knowledge base operations
 */
public function mxchat_save_selected_bot() {
    // Check nonce
    if (!wp_verify_nonce($_POST['nonce'] ?? '', 'mxchat_save_setting_nonce')) {
        wp_send_json_error('Invalid nonce');
    }
    
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error('Unauthorized');
    }
    
    $bot_id = isset($_POST['bot_id']) ? sanitize_key($_POST['bot_id']) : 'default';
    
    // Save as user meta for the current user
    $user_id = get_current_user_id();
    update_user_meta($user_id, 'mxchat_selected_knowledge_bot', $bot_id);
    
    // Also save as an option for site-wide default
    update_option('mxchat_current_knowledge_bot', $bot_id);
    
    // No cache clearing needed since we removed caching
    
    wp_send_json_success(array(
        'message' => 'Bot selection saved',
        'bot_id' => $bot_id
    ));
}

    /**
     * Handles AJAX request for saving chat settings
     */
     public function mxchat_save_prompts_setting_callback() {
         check_ajax_referer('mxchat_prompts_setting_nonce');

         if (!current_user_can('manage_options')) {
             wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
         }

         $name = isset($_POST['name']) ? $_POST['name'] : '';
         $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';

         //error_log('[MXCHAT-PROMPTS] Saving setting: ' . $name . ' = ' . $value);

         if (empty($name)) {
             wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
         }

// Handle Pinecone settings - BYPASS WORDPRESS SANITIZATION
if (strpos($name, 'mxchat_pinecone_addon_options') !== false) {
    //error_log('[MXCHAT-PROMPTS] Processing Pinecone setting: ' . $name);

    // Extract the field name
    if (preg_match('/mxchat_pinecone_addon_options\[([^\]]+)\]/', $name, $matches)) {
        $field_name = $matches[1];
        //error_log('[MXCHAT-PROMPTS] Extracted field name: ' . $field_name);

        // Get current options directly from database - NO WordPress filters
        global $wpdb;
        $current_options_raw = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
                'mxchat_pinecone_addon_options'
            )
        );

        // FIX: Handle the case where the option doesn't exist yet
        if ($current_options_raw === null) {
            // Option doesn't exist, create it with default values
            $current_options = array(
                'mxchat_use_pinecone' => '0',
                'mxchat_pinecone_api_key' => '',
                'mxchat_pinecone_host' => '',
                'mxchat_pinecone_index' => '',
                'mxchat_pinecone_environment' => ''
            );
            //error_log('[MXCHAT-PROMPTS] Option does not exist, creating with defaults');
        } else {
            // Unserialize the raw data
            $current_options = maybe_unserialize($current_options_raw);
            if (!is_array($current_options)) {
                // Fallback to defaults if unserialization fails
                $current_options = array(
                    'mxchat_use_pinecone' => '0',
                    'mxchat_pinecone_api_key' => '',
                    'mxchat_pinecone_host' => '',
                    'mxchat_pinecone_index' => '',
                    'mxchat_pinecone_environment' => ''
                );
                //error_log('[MXCHAT-PROMPTS] Failed to unserialize, using defaults');
            }
        }

        //error_log('[MXCHAT-PROMPTS] Current options from DB: ' . print_r($current_options, true));

        // Update the specific field with proper sanitization
        switch ($field_name) {
            case 'mxchat_use_pinecone':
                $new_value = ($value === '1') ? '1' : '0';
                break;
            case 'mxchat_pinecone_api_key':
            case 'mxchat_pinecone_host':
            case 'mxchat_pinecone_index':
            case 'mxchat_pinecone_environment':
                $new_value = sanitize_text_field($value);
                if ($field_name === 'mxchat_pinecone_host') {
                    $new_value = str_replace(['https://', 'http://'], '', $new_value);
                }
                break;
            default:
                wp_send_json_error(['message' => esc_html__('Unknown Pinecone field', 'mxchat')]);
        }

        $current_options[$field_name] = $new_value;
        //error_log('[MXCHAT-PROMPTS] New value for ' . $field_name . ': "' . $new_value . '"');
        //error_log('[MXCHAT-PROMPTS] Updated options: ' . print_r($current_options, true));

        // Save directly to database to bypass WordPress sanitization
        $serialized_options = maybe_serialize($current_options);
        
        // FIX: Use INSERT ... ON DUPLICATE KEY UPDATE or separate INSERT/UPDATE logic
        $option_exists = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name = %s",
                'mxchat_pinecone_addon_options'
            )
        );

        if ($option_exists > 0) {
            // Update existing option
            $save_result = $wpdb->update(
                $wpdb->options,
                array('option_value' => $serialized_options),
                array('option_name' => 'mxchat_pinecone_addon_options'),
                array('%s'),
                array('%s')
            );
            //error_log('[MXCHAT-PROMPTS] Updated existing option, result: ' . ($save_result !== false ? 'SUCCESS' : 'FAILED'));
        } else {
            // Insert new option
            $save_result = $wpdb->insert(
                $wpdb->options,
                array(
                    'option_name' => 'mxchat_pinecone_addon_options',
                    'option_value' => $serialized_options,
                    'autoload' => 'yes'
                ),
                array('%s', '%s', '%s')
            );
            //error_log('[MXCHAT-PROMPTS] Inserted new option, result: ' . ($save_result !== false ? 'SUCCESS' : 'FAILED'));
        }

        // Clear any WordPress option cache to ensure get_option() returns fresh data
        wp_cache_delete('mxchat_pinecone_addon_options', 'options');

        // IMPROVED VERIFICATION - Check if the database operation succeeded
        if ($save_result !== false) {
            // Double-check by reading fresh from database
            $verification_raw = $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
                    'mxchat_pinecone_addon_options'
                )
            );
            $verification_options = maybe_unserialize($verification_raw);
            $verified_value = isset($verification_options[$field_name]) ? $verification_options[$field_name] : 'NOT_FOUND';

            //error_log('[MXCHAT-PROMPTS] Final verification - Expected: "' . $new_value . '", Got: "' . $verified_value . '"');

            // Use loose comparison (==) instead of strict (===) to avoid type issues
            if ($verified_value == $new_value || $save_result > 0) {
                wp_send_json_success(['message' => esc_html__('Pinecone setting saved', 'mxchat')]);
            } else {
                // Still return success if the DB operation worked, even if verification is quirky
                //error_log('[MXCHAT-PROMPTS] Verification mismatch but DB operation succeeded');
                wp_send_json_success(['message' => esc_html__('Pinecone setting saved (DB success)', 'mxchat')]);
            }
        } else {
            wp_send_json_error(['message' => esc_html__('Database save failed', 'mxchat')]);
        }
    } else {
        wp_send_json_error(['message' => esc_html__('Invalid field name format', 'mxchat')]);
    }

    return; // Exit here for Pinecone settings
}
         // Handle auto-sync settings (existing functionality)
         if (strpos($name, 'mxchat_auto_sync_') === 0) {
             $value = ($value === 'on' || $value === '1') ? '1' : '0';
             $updated = update_option($name, $value);

             if ($updated || get_option($name) === $value) {
                 wp_send_json_success(['message' => esc_html__('Auto-sync setting saved', 'mxchat')]);
             } else {
                 wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
             }
         }

         // Handle other prompts options
         $options = get_option('mxchat_prompts_options', []);
         $options[$name] = $value;
         $updated = update_option('mxchat_prompts_options', $options);

         if ($updated) {
             wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
         } else {
             wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
         }
     }


    /**
     * Handles AJAX request for Pinecone settings migration
     */
     public function ajax_migrate_pinecone_settings() {
         // Verify nonce
         if (!wp_verify_nonce($_POST['_ajax_nonce'] ?? '', 'mxchat_save_setting_nonce')) {
             wp_send_json_error('Invalid nonce');
         }

         // Check permissions
         if (!current_user_can('manage_options')) {
             wp_send_json_error('Unauthorized access');
         }

         // Check if old Pinecone addon options exist
         $old_options = get_option('mxchat_pinecone_addon_options', array());

         if (empty($old_options)) {
             wp_send_json_success(array('migrated' => false, 'message' => 'No old settings found'));
         }

         // Get current core plugin options
         $current_options = get_option('mxchat_pinecone_addon_options', array());

         // Only migrate if core options are empty or if explicitly requested
         $should_migrate = empty($current_options) ||
                          (empty($current_options['mxchat_pinecone_api_key']) && !empty($old_options['mxchat_pinecone_api_key']));

         if ($should_migrate) {
             // Migrate settings with proper sanitization
             $migrated_options = array(
                 'mxchat_use_pinecone' => $old_options['mxchat_use_pinecone'] ?? '0',
                 'mxchat_pinecone_api_key' => sanitize_text_field($old_options['mxchat_pinecone_api_key'] ?? ''),
                 'mxchat_pinecone_host' => sanitize_text_field($old_options['mxchat_pinecone_host'] ?? ''),
                 'mxchat_pinecone_index' => sanitize_text_field($old_options['mxchat_pinecone_index'] ?? ''),
                 'mxchat_pinecone_environment' => sanitize_text_field($old_options['mxchat_pinecone_environment'] ?? '')
             );

             update_option('mxchat_pinecone_addon_options', $migrated_options);

             wp_send_json_success(array(
                 'migrated' => true,
                 'message' => 'Settings migrated successfully from Pinecone add-on'
             ));
         } else {
             wp_send_json_success(array(
                 'migrated' => false,
                 'message' => 'Settings already exist in core plugin'
             ));
         }
     }


    // ========================================
    // LICENSE AJAX HANDLERS
    // ========================================

/**
 * Validates and activates chat license via AJAX
 */
public function mxchat_handle_activate_license() {
    // Check nonce
    if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
        wp_send_json_error(esc_html__('Invalid security token', 'mxchat'));
        return;
    }
    
    // Verify user capabilities
    if (!current_user_can('manage_options')) {
        wp_send_json_error(esc_html__('Unauthorized access', 'mxchat'));
        return;
    }
    
    $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
    $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
    
    if (empty($license_key) || empty($customer_email)) {
        wp_send_json_error(esc_html__('Email or License Key is missing', 'mxchat'));
        return;
    }
    
    $product_id = 'MxChatPRO';
    $domain = parse_url(home_url(), PHP_URL_HOST); // Get the current domain
    
    // Call WooCommerce Software API for activation (not just validation)
    $response = wp_remote_get(
        add_query_arg(
            array(
                'wc-api' => 'software-api',
                'request' => 'activation',
                'email' => $customer_email,
                'license_key' => $license_key,
                'product_id' => $product_id,
                'instance' => $domain, // THIS IS KEY - include the domain as instance
                'platform' => 'wordpress' // Optional but good to include
            ),
            'https://mxchat.ai/'
        ),
        array(
            'timeout' => 60,
            'sslverify' => true
        )
    );
    
    if (is_wp_error($response)) {
        $error_message = $response->get_error_message();
        //error_log('MxChat License Activation Error: ' . $error_message);
        wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $error_message);
        return;
    }
    
    $response_code = wp_remote_retrieve_response_code($response);
    $body = wp_remote_retrieve_body($response);
    
    // Log response for debugging
    //error_log('MxChat License Response Code: ' . $response_code);
    //error_log('MxChat License Response Body: ' . $body);
    
    if ($response_code !== 200) {
        wp_send_json_error(esc_html__('Server returned error code: ', 'mxchat') . $response_code);
        return;
    }
    
    $data = json_decode($body);
    
    if ($data && isset($data->activated) && $data->activated) {
        // Success - save local options
        update_option('mxchat_license_status', 'active');
        update_option('mxchat_pro_email', $customer_email);
        update_option('mxchat_activation_key', $license_key);
        delete_option('mxchat_license_error');
        
        // Also track on your website (this is your existing domain tracking)
        $this->track_domain_on_website($license_key, $customer_email, $domain);
        
        wp_send_json_success(array('message' => esc_html__('License activated successfully', 'mxchat')));
    } else {
        $error_message = isset($data->error) ? $data->error : esc_html__('Activation failed', 'mxchat');
        update_option('mxchat_license_status', 'inactive');
        update_option('mxchat_license_error', $error_message);
        
        //error_log('MxChat Activation failed: ' . $error_message);
        wp_send_json_error($error_message);
    }
}

/**
 * Track domain on your website (separate from WooCommerce activation)
 */
private function track_domain_on_website($license_key, $email, $domain) {
    // This calls your website's tracking API
    wp_remote_post('https://mxchat.ai/mxchat-api/activate-license', array(
        'body' => array(
            'mxchat_pro_email' => $email,
            'mxchat_activation_key' => $license_key,
            'domain' => $domain
        ),
        'timeout' => 10,
        'sslverify' => true
    ));
}

    /**
     * Validates license via AJAX with email and key
     */
    public function mxchat_check_license_status() {
        // Verify nonce
        if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
            wp_send_json_error('Security check failed');
            return;
        }
    
        // Add isset checks for safety
        $email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
        $key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : '';
    
        // Check if this license is actually active in your system
        $is_active = (get_option('mxchat_license_status') === 'active' &&
                      get_option('mxchat_pro_email') === $email &&
                      get_option('mxchat_activation_key') === $key);
    
        wp_send_json(array(
            'is_active' => $is_active
        ));
    }
     
/**
 * Handle license deactivation - Complete version for plugin
 */
function mxchat_deactivate_license() {
    // Add debugging
    //error_log('MxChat deactivate function called');
    
    // Check nonce
    if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
        //error_log('MxChat deactivate: Nonce check failed');
        wp_send_json_error('Security check failed.');
        return;
    }
    
    //error_log('MxChat deactivate: Nonce check passed');
    
    $license_key = get_option('mxchat_activation_key');
    $email = get_option('mxchat_pro_email');
    $domain = parse_url(home_url(), PHP_URL_HOST);
    
    //error_log('MxChat deactivate: License: ' . $license_key . ', Email: ' . $email . ', Domain: ' . $domain);
    
    if (empty($license_key) || empty($email)) {
        //error_log('MxChat deactivate: No active license found');
        wp_send_json_error('No active license found.');
        return;
    }
    
    // Clear local license data first
    delete_option('mxchat_license_status');
    delete_option('mxchat_pro_email');
    delete_option('mxchat_activation_key');
    delete_option('mxchat_license_error');
    
    //error_log('MxChat deactivate: Local data cleared');
    
    // Notify your website's API to properly deactivate
    $response = wp_remote_post('https://mxchat.ai/mxchat-api/deactivate-license', array(
        'body' => array(
            'license_key' => $license_key,
            'email' => $email,
            'domain' => $domain
        ),
        'timeout' => 15,
        'sslverify' => true
    ));
    
    if (is_wp_error($response)) {
        //error_log('MxChat deactivate: Server error - ' . $response->get_error_message());
        wp_send_json_success(array(
            'message' => 'License deactivated locally. Server could not be contacted to free activation slot.',
            'server_notified' => false
        ));
        return;
    }
    
    $response_body = wp_remote_retrieve_body($response);
    $response_data = json_decode($response_body, true);
    
    //error_log('MxChat deactivate: Server response - ' . $response_body);
    
    if (isset($response_data['success']) && $response_data['success']) {
        //error_log('MxChat deactivate: Success with server notification');
        wp_send_json_success(array(
            'message' => 'License deactivated successfully. Activation slot has been freed up.',
            'server_notified' => true
        ));
    } else {
        //error_log('MxChat deactivate: Server responded but deactivation may have failed');
        wp_send_json_success(array(
            'message' => 'License deactivated locally. Please check your account dashboard to verify the activation was freed.',
            'server_notified' => false
        ));
    }
}


    // ========================================
    // ACTIONS & INTENTS AJAX HANDLERS
    // ========================================

    /**
     * Validates nonce and returns JSON error on failure
     */
     public function mxchat_toggle_action() {
         // Check nonce
         if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mxchat_actions_nonce')) {
             wp_send_json_error(array('message' => 'Security check failed'));
             return;
         }

         // Check permissions
         if (!current_user_can('manage_options')) {
             wp_send_json_error(array('message' => 'Permission denied'));
             return;
         }

         // Validate params
         $intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
         $enabled = isset($_POST['enabled']) ? (bool)$_POST['enabled'] : false;

         if (!$intent_id) {
             wp_send_json_error(array('message' => 'Invalid action ID'));
             return;
         }

         // Update the intent/action status in the database
         global $wpdb;
         $table_name = $wpdb->prefix . 'mxchat_intents';

         // Using the 'enabled' field - add this field if it doesn't exist
         $result = $wpdb->update(
             $table_name,
             array('enabled' => $enabled ? 1 : 0),
             array('id' => $intent_id),
             array('%d'),
             array('%d')
         );

         if ($result === false) {
             wp_send_json_error(array('message' => 'Database error'));
             return;
         }

         wp_send_json_success();
     }


    /**
     * Validates permissions for AJAX request handling
     */
     public function mxchat_update_intent_threshold() {
         // Check permissions
         if (!current_user_can('manage_options')) {
             if (wp_doing_ajax()) {
                 wp_send_json_error(array('message' => 'Unauthorized user'));
                 return;
             }
             wp_die(esc_html__('Unauthorized user', 'mxchat'));
         }

         // Verify nonce
         check_admin_referer('mxchat_update_intent_threshold_nonce');

         // Process the update if we have valid data
         if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
             global $wpdb;
             $table_name = $wpdb->prefix . 'mxchat_intents';
             $intent_id = intval($_POST['intent_id']);
             $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
             $similarity_threshold = $threshold_percentage / 100;

             $result = $wpdb->update(
                 $table_name,
                 ['similarity_threshold' => $similarity_threshold],
                 ['id' => $intent_id],
                 ['%f'],
                 ['%d']
             );

             // Handle AJAX requests
             if (wp_doing_ajax()) {
                 if ($result === false) {
                     wp_send_json_error(array('message' => 'Failed to update threshold'));
                 } else {
                     wp_send_json_success(array('threshold' => $threshold_percentage));
                 }
                 return;
             }
         }

         // Redirect for regular form submissions
         wp_safe_redirect(admin_url('admin.php?page=mxchat-actions&updated=true'));
         exit;
     }

    // ========================================
    // HELPER METHODS
    // ========================================

    /**
     * Returns a specific nonce action string
     */
     private function mxchat_get_nonce_action() {
         return 'mxchat_license_nonce';
     }

}

// Initialize the AJAX handler
new MxChat_Ajax_Handler();