# mxchat-basic/1.3/includes/class-mxchat-integrator.php

MxChat – AI Chatbot &amp; Content Generation for WordPress, version 1.3. 1,547 lines.

- Page: https://pluginprobe.com/plugins/mxchat-basic/1.3/code/includes/class-mxchat-integrator.php
- Raw: https://pluginprobe.com/plugins/mxchat-basic/1.3/raw/includes/class-mxchat-integrator.php
- Modified: 2024-11-07T15:22:56+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/mxchat-basic/1.3/code/includes/class-mxchat-integrator.php#L10-L20`.

```php
<?php
if (!defined('ABSPATH')) {
    exit;
}

class MxChat_Integrator {
    private $options;
    private $chat_count;

public function __construct() {
    $this->options = get_option('mxchat_options');
    $this->chat_count = get_option('mxchat_chat_count', 0);

    // Add WooCommerce hooks
    add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);

    // Ensure embeddings are removed when a product is moved to trash or permanently deleted
    add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
    add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));

    add_action('wp_enqueue_scripts', array($this, 'mxchat_enqueue_scripts_styles'));
    add_action('wp_ajax_mxchat_handle_chat_request', array($this, 'mxchat_handle_chat_request'));
    add_action('wp_ajax_nopriv_mxchat_handle_chat_request', array($this, 'mxchat_handle_chat_request'));

    add_action('wp_ajax_mxchat_dismiss_pre_chat_message', array($this, 'mxchat_dismiss_pre_chat_message'));
    add_action('wp_ajax_nopriv_mxchat_dismiss_pre_chat_message', array($this, 'mxchat_dismiss_pre_chat_message'));
    // Add the AJAX actions for checking if the pre-chat message was dismissed
    add_action('wp_ajax_mxchat_check_pre_chat_message_status', array($this, 'mxchat_check_pre_chat_message_status'));
    add_action('wp_ajax_nopriv_mxchat_check_pre_chat_message_status', array($this, 'mxchat_check_pre_chat_message_status'));

    add_action('wp_ajax_mxchat_fetch_conversation_history', [$this, 'mxchat_fetch_conversation_history']);
    add_action('wp_ajax_nopriv_mxchat_fetch_conversation_history', [$this, 'mxchat_fetch_conversation_history']);


        add_action('wp_ajax_mxchat_add_to_cart', [$this, 'mxchat_add_to_cart']);
        add_action('wp_ajax_nopriv_mxchat_add_to_cart', [$this, 'mxchat_add_to_cart']);

    if (!wp_next_scheduled('mxchat_reset_rate_limits')) {
        wp_schedule_event(time(), 'daily', 'mxchat_reset_rate_limits');
    }

    add_action('mxchat_reset_rate_limits', array($this, 'mxchat_reset_rate_limits'));
}

public function mxchat_handle_product_change($post_id, $post, $update) {
    // Ensure this is a product post type
    if ($post->post_type !== 'product') {
        return;
    }

    // Only generate embeddings if the product is published
    if ($post->post_status === 'publish') {
        // Delay the embedding slightly to ensure all product data is available
        add_action('shutdown', function() use ($post_id) {
            $product = wc_get_product($post_id);
            if ($product && $product->get_price() !== '') {
                $this->mxchat_store_product_embedding($product);
            } else {
                // Optionally, log or handle the case where product data is incomplete
               // error_log("Product {$post_id} does not have complete data. Embedding not generated.");
            }
        });
    }
}

public function mxchat_handle_product_delete($post_id) {
    if (get_post_type($post_id) !== 'product') {
        return;
    }

    global $wpdb;
    $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';

    // Delete the embedding associated with this product
    $wpdb->delete($table_name, array('source_url' => get_permalink($post_id)), array('%s'));
}

private function mxchat_store_product_embedding($product) {
    if (isset($this->options['enable_woocommerce_integration']) && $this->options['enable_woocommerce_integration'] === '1') {

        $source_url = get_permalink($product->get_id());
        $regular_price = $product->get_regular_price();
        $sale_price = $product->get_sale_price();
        $price = $sale_price ?: $regular_price;

        $description = $product->get_description() . "\n\n" .
                       "Short Description: " . $product->get_short_description() . "\n" .
                       "Price: " . $regular_price . "\n" .
                       "Sale Price: " . ($sale_price ?: 'N/A') . "\n" .
                       "SKU: " . $product->get_sku();

        global $wpdb;
        $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';

        // Delete any existing embedding for this product
        $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));

        // Submit the new content and embedding to the database
        MxChat_Utils::submit_content_to_db($description, $source_url, $this->options['api_key']);
    }
}





    private function mxchat_increment_chat_count() {
        $chat_count = get_option('mxchat_chat_count', 0);
        $chat_count++;
        update_option('mxchat_chat_count', $chat_count);
    }

function mxchat_fetch_conversation_history() {
    if (empty($_POST['session_id'])) {
        wp_send_json_error(['message' => 'Session ID missing.']);
        wp_die();
    }

    $session_id = sanitize_text_field($_POST['session_id']);
    $history = get_option("mxchat_history_{$session_id}", []); // Retrieve stored history

    if (empty($history)) {
        wp_send_json_error(['message' => 'No history found.']);
        wp_die();
    }

    wp_send_json_success(['conversation' => $history]);
    wp_die();
}
private function mxchat_fetch_conversation_history_for_ajax($session_id) {
    $history = get_option("mxchat_history_{$session_id}", []); // Retrieve stored history based on session ID
    $formatted_history = [];

    // Format the history to align with the expected structure for OpenAI
    foreach ($history as $entry) {
        $formatted_history[] = [
            'role' => $entry['role'],  // Ensure this matches 'user' or 'assistant'
            'content' => $entry['content']
        ];
    }

    return $formatted_history;
}
private function mxchat_fetch_conversation_history_for_ai($session_id) {
    $history = get_option("mxchat_history_{$session_id}", []);

    $formatted_history = [];

    foreach ($history as $entry) {
        // Skip messages containing HTML
        if ($entry['content'] !== strip_tags($entry['content'])) {
            continue;
        }

        $formatted_history[] = [
            'role' => $entry['role'],
            'content' => $entry['content']
        ];
    }

    return $formatted_history;
}


private function mxchat_save_chat_message($session_id, $role, $message) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';

    $user_id = is_user_logged_in() ? get_current_user_id() : 0;
    $user_identifier = MxChat_User::mxchat_get_user_identifier();
    $user_email = MxChat_User::mxchat_get_user_email();

    $history = get_option("mxchat_history_{$session_id}", []);
    $history[] = ['role' => $role, 'content' => $message];
    update_option("mxchat_history_{$session_id}", $history);

    $wpdb->insert($table_name, [
        'user_id' => $user_id,
        'user_identifier' => $user_identifier,
        'user_email' => $user_email,
        'session_id' => $session_id,
        'role' => $role,
        'message' => $message,
        'timestamp' => current_time('mysql', 1)
    ]);
}


public function mxchat_handle_chat_request() {
    global $wpdb;

    // Get and sanitize the user identifier
    $user_id = $this->mxchat_get_user_identifier();
    $user_id = sanitize_key($user_id);

    // Setup rate limiting
    $rate_limit_transient_key = 'mxchat_chat_limit_' . $user_id;
    $chat_count = get_transient($rate_limit_transient_key) ?: 0;

    // Retrieve the session ID from the client's POST data
    $session_id = isset($_POST['session_id']) ? sanitize_text_field($_POST['session_id']) : '';

    if (empty($session_id)) {
        wp_send_json_error('Session ID is missing.');
        wp_die();
    }

    // Check rate limit
    $rate_limit_option = $this->options['rate_limit'] ?? 'unlimited';
    if ($rate_limit_option !== 'unlimited' && $chat_count >= intval($rate_limit_option)) {
        wp_send_json_error(['message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.']);
        wp_die();
    }

    // Increment chat count only if limit is not 'unlimited'
    if ($rate_limit_option !== 'unlimited') {
        set_transient($rate_limit_transient_key, $chat_count + 1, DAY_IN_SECONDS);
    }

    // Validate and sanitize the incoming message
    if (empty($_POST['message'])) {
        wp_send_json_error('No message received');
        wp_die();
    }

    $message = sanitize_text_field($_POST['message']);
    $this->mxchat_save_chat_message($session_id, 'user', $message);

    // Track email capture and WooCommerce flows
    $email_capture_prompt = get_transient('mxchat_email_capture_' . $user_id);
    $interaction_count = get_transient('mxchat_email_interaction_count_' . $user_id) ?: 0;
    $woocommerce_prompt = get_transient('mxchat_woocommerce_prompt_' . $user_id);

    // Handle email capture flow
    if ($email_capture_prompt) {
        if (preg_match('/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/i', $message, $matches)) {
            $email = $matches[0];
            $this->add_email_to_loops($email);
            $response = $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.';

            delete_transient('mxchat_email_capture_' . $user_id);
            delete_transient('mxchat_email_interaction_count_' . $user_id);
            delete_transient('mxchat_woocommerce_prompt_' . $user_id);

            $this->mxchat_save_chat_message($session_id, 'bot', $response);
            wp_send_json(['message' => $response]);
            wp_die();
        } else {
            if ($interaction_count >= 3) {
                delete_transient('mxchat_email_capture_' . $user_id);
                delete_transient('mxchat_email_interaction_count_' . $user_id);
            } else {
                set_transient('mxchat_email_interaction_count_' . $user_id, ++$interaction_count, 5 * MINUTE_IN_SECONDS);
            }
        }
    }


 // Initialize variables for storing intent and fallback response data
    $this->fallbackResponse = [
        'text' => '',
        'html' => '',
        'images' => [],
    ];
    $this->productCardHtml = ''; // Initialize product card HTML
    $intent_info = '';

    // Step 1: Detect intent and handle intent-based responses if applicable
    $intent_matched = $this->mxchat_check_intent_and_invoke_callback($message, $user_id, $session_id);

/*
    // Log the intent match and fallback response
    if (defined('WP_DEBUG') && WP_DEBUG) {
        error_log("Intent matched: " . ($intent_matched ? 'Yes' : 'No'));
        error_log("Fallback response after intent handling: " . print_r($this->fallbackResponse['text'], true));
    }
*/


    // Store any intent-based info from fallbackResponse for use in context
    if (!empty($this->fallbackResponse['text'])) {
        $intent_info = $this->fallbackResponse['text'];
    }

    // Step 2: Check for relevant knowledge database content based on user message embedding
    $user_message_embedding = $this->mxchat_generate_embedding($message, $this->options['api_key']);
    if (!is_array($user_message_embedding)) {
        wp_send_json_error('Error processing your message.');
        wp_die();
    }

    // Fetch relevant content from the knowledge base using the embedding
    $relevant_content = $this->mxchat_find_relevant_content($user_message_embedding);

    // Step 3: Prepare context for AI by combining user query, intent-based info, and knowledge base content
    $context_content = "User asked: '{$message}'\n\n";

    // Append intent-based information if any
    if (!empty($intent_info)) {
        $context_content .= "Relevant intent-based information:\n" . $intent_info . "\n\n";
    }

    // Append relevant knowledge base content if any
    if (!empty($relevant_content)) {
        $context_content .= "Relevant content from knowledge database:\n" . $relevant_content . "\n\n";
    }

    // Step 4: Generate AI response based on context and conversation history
    $conversation_history = $this->mxchat_fetch_conversation_history_for_ai($session_id);
    $this->mxchat_increment_chat_count();

    // Call AI to generate a response with the full context
    $response = $this->mxchat_generate_response(
        $context_content,
        $this->options['api_key'],
        $this->options['xai_api_key'],
        $this->options['claude_api_key'],
        $conversation_history
    );


    // Step 5: Save the response in chat history and prepare to send it back to the user
    $this->mxchat_save_chat_message($session_id, 'bot', $response);

      // Step 4: Then save any intent content
    if (!empty($this->productCardHtml)) {
        $this->mxchat_save_chat_message($session_id, 'bot', $this->productCardHtml);
    }

    if (!empty($this->fallbackResponse['html'])) {
        $this->mxchat_save_chat_message($session_id, 'bot', $this->fallbackResponse['html']);
    }

    $response_data = [
        'text' => $response,
        'html' => !empty($this->productCardHtml) ? $this->productCardHtml : (isset($this->fallbackResponse['html']) ? $this->fallbackResponse['html'] : ''),
        'session_id' => $session_id
    ];


    //error_log("Response Data Sent to Frontend: " . print_r($response_data, true));

    // Send response to the user
    wp_send_json($response_data);
    wp_die();
}

// New function to check intents and invoke the callback function
private function mxchat_check_intent_and_invoke_callback($message, $user_id, $session_id) {
    global $wpdb;

    // Log that we are starting the intent check
    //error_log("Starting intent check for message: " . $message);

    // Embed the user message
    $user_embedding = $this->mxchat_generate_embedding($message, $this->options['api_key']);
    if (!is_array($user_embedding)) {
       // error_log('Error generating embedding for the message.');
        wp_send_json_error('Error generating embedding for the message.');
        return false;
    }

    // Fetch all intents from the database
    $table_name = $wpdb->prefix . 'mxchat_intents';
    $intents = $wpdb->get_results("SELECT * FROM $table_name");

    // Log if no intents are found
    if (empty($intents)) {
        //error_log("No intents found in the database.");
    }

    $highest_similarity = -INF;
    $matched_intent = null;

    // Calculate similarity for each intent
    foreach ($intents as $intent) {
        // Use unserialize with allowed_classes parameter for enhanced security
        $intent_embedding = $intent->embedding_vector
            ? unserialize($intent->embedding_vector, ['allowed_classes' => false])
            : null;

        if (!is_array($intent_embedding)) {
            //error_log("Invalid embedding for intent: " . $intent->intent_label);
            continue; // Skip if the embedding is invalid
        }

        $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $intent_embedding);
        //error_log("Similarity for intent '{$intent->intent_label}': " . $similarity);

        if ($similarity > $highest_similarity) {
            $highest_similarity = $similarity;
            $matched_intent = $intent;
        }
    }

    // Define the similarity threshold for matching
    $similarity_threshold = 0.80;

    // If a match is found above the threshold, call the associated callback
    if ($highest_similarity >= $similarity_threshold && $matched_intent) {
        //error_log("Matched intent: " . $matched_intent->intent_label . " with similarity: " . $highest_similarity);

        // Ensure the callback function exists before calling it
        if (method_exists($this, $matched_intent->callback_function)) {
            // Call the intent handler function
            call_user_func([$this, $matched_intent->callback_function], $message, $user_id, $session_id);
            return true; // Indicate that an intent was matched and handled
        } else {
            //error_log("Callback function not found: " . $matched_intent->callback_function);
        }
    } else {
        //error_log("No intent matched above the threshold.");
    }

    return false; // No intent was matched above the threshold
}

//verified good
public function mxchat_handle_order_history($message, $user_id, $session_id) {
    // Check if WooCommerce is active
    if (!class_exists('WooCommerce')) {
        $this->fallbackResponse['text'] = "Order functionality is currently unavailable.";
        return; // No need to proceed further
    }

    // Determine if the user is asking for the "last order" or "all orders"
    $queryType = (stripos($message, 'last order') !== false || stripos($message, 'recent order') !== false) ? 'last' : 'all';

    // Fetch the relevant order details
    $orderDetails = MxChat_WooCommerce::mxchat_fetch_user_orders_details($queryType);

    // Set the fallback response to order details, which will be used if needed
    $this->fallbackResponse['text'] = $orderDetails ?: "No previous orders found.";
}


//verified good
public function mxchat_handle_product_inquiry( $message, $user_id, $session_id ) {
    // Attempt to retrieve the product ID from the message
    $product_id = MxChat_WooCommerce::mxchat_extract_product_id_from_message( $message );

    // Check if there's a previously discussed product for follow-up queries
    if ( ! $product_id ) {
        $product_id = get_transient( 'mxchat_last_discussed_product_' . $user_id );
    }

    if ( $product_id && class_exists( 'WooCommerce' ) ) {
        $product = wc_get_product( $product_id );
        if ( $product ) {
            // Escape and prepare product data
            $product_name      = esc_html( $product->get_name() );
            $product_price     = $product->get_price_html(); // Assuming this is safe HTML
            $product_image_url = esc_url( wp_get_attachment_url( $product->get_image_id() ) );
            $product_url       = esc_url( get_permalink( $product_id ) );
            $product_id_attr   = esc_attr( $product_id );

            // Generate the product card HTML using HEREDOC syntax
            $product_card_html = <<<HTML
<div class="mxchat-product-card">
    <a href="{$product_url}" target="_blank">
        <img src="{$product_image_url}" alt="{$product_name}" class="mxchat-product-image" />
        <h3 class="mxchat-product-name">{$product_name}</h3>
    </a>
    <div class="mxchat-product-price">{$product_price}</div>
    <button class="mxchat-add-to-cart-button" data-product-id="{$product_id_attr}">Add to Cart</button>
</div>
HTML;

            // Save the last discussed product
            set_transient( 'mxchat_last_discussed_product_' . $user_id, $product_id, HOUR_IN_SECONDS );

            // Save the product card HTML to be appended later
            $this->productCardHtml = $product_card_html;

            // Do not send a response here; allow execution to continue
            return;
        }
    }

    // If no product found, set a fallback response
    $this->fallbackResponse = __( "I couldn't find details for that product. Could you specify the product name?", 'mxchat' );
    return;
}

//verified good
public function mxchat_handle_email_capture($message, $user_id, $session_id) {
    // Log the message safely
    //error_log("Triggered email capture intent for message: " . sanitize_text_field($message));

    // Initiate email capture flow
    $response = esc_html($this->options['triggered_phrase_response'] ?? "Would you like to join our mailing list? Please provide your email below.");

    set_transient('mxchat_email_capture_' . $user_id, true, 5 * MINUTE_IN_SECONDS);
    $this->mxchat_save_chat_message($session_id, 'bot', $response);

    // Respond to the user
    wp_send_json(['message' => $response]);
    wp_die();
}

//very good
public function mxchat_generate_image($message, $user_id, $session_id) {
    // Prepare a prompt for DALL-E
    $prompt = "Create an image of " . sanitize_text_field($message);

    // Use the existing OpenAI API key
    $openai_api_key = sanitize_text_field($this->options['api_key']);

    // Call DALL-E to generate an image
    $image_response = $this->mxchat_generate_dalle_image($prompt, $openai_api_key);

    // Check if the response contains an image URL
    if (isset($image_response['imageUrl'])) {
        $image_url = esc_url_raw($image_response['imageUrl']);

        // Construct the HTML with a CSS class instead of inline styles
        $response_html = '<img src="' . esc_url($image_url) . '" alt="Generated Image" class="mxchat-generated-image" />';

        $response_text = "Here is the image I generated:";
    } else {
        $response_text = "I'm sorry, but I couldn't generate an image based on your request.";
        $response_html = '';
        //error_log("DALL-E image generation error: " . esc_html($image_response['error'] ?? 'Unknown error.'));
    }

    // Save both text and HTML responses
    $this->mxchat_save_chat_message($session_id, 'bot', $response_text . "\n" . $response_html);

    // Prepare the response data
    $response_data = [
        'message' => $response_text,
        'html'    => $response_html,
        'image_url' => $image_url ?? '',
    ];

    // Send the JSON response
    header('Content-Type: application/json; charset=' . get_option('blog_charset'));
    echo json_encode($response_data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    wp_die();
}
private function mxchat_generate_dalle_image($prompt, $api_key, $model = 'dall-e-3', $timeout = 60) {
    $api_url = 'https://api.openai.com/v1/images/generations';
    $body = json_encode([
        'prompt' => sanitize_text_field($prompt),
        'n' => 1,
        'size' => '1024x1024',
        'model' => sanitize_text_field($model),
    ]);

    $args = [
        'body' => $body,
        'headers' => [
            'Content-Type' => 'application/json',
            'Authorization' => 'Bearer ' . sanitize_text_field($api_key),
        ],
        'method' => 'POST',
        'timeout' => absint($timeout),
    ];

    $response = wp_remote_post($api_url, $args);

    if (is_wp_error($response)) {
        //error_log("DALL-E request failed: " . $response->get_error_message());
        return ['error' => "Error generating image: " . $response->get_error_message()];
    }

    $response_body = json_decode(wp_remote_retrieve_body($response), true);

    if (isset($response_body['data'][0]['url'])) {
        return ['imageUrl' => esc_url_raw($response_body['data'][0]['url'])];
    } else {
        //error_log("DALL-E response error: " . wp_remote_retrieve_body($response));
        return ['error' => "Failed to generate image."];
    }
}

//very good
public function mxchat_handle_search_request($message, $user_id, $session_id) {
    // Sanitize user input
    $search_query = preg_replace('/^search the web for\s+/i', '', $message);
    $search_query = preg_replace('/^show me the latest news about\s+/i', '', $message);
    $search_query = preg_replace('/^what\'?s the news in\s+/i', '', $message);
    $search_query = preg_replace('/^news about\s+/i', '', $message);
    $search_query = trim(sanitize_text_field($search_query));

    if (empty($search_query)) {
        $this->fallbackResponse = [
            'text' => __("Please provide a valid search query.", 'mxchat'),
        ];
        return;
    }

    // Check if the query is likely related to news
    $is_news_search = preg_match("/\bnews\b/i", $message);

    // Retrieve settings
    $options = get_option('mxchat_options');

    $api_key = isset($options['brave_api_key']) ? sanitize_text_field($options['brave_api_key']) : '';
    $news_count = isset($options['brave_news_count']) ? intval($options['brave_news_count']) : 3;
    $country = isset($options['brave_country']) ? sanitize_text_field($options['brave_country']) : 'us';
    $language = isset($options['brave_language']) ? sanitize_text_field($options['brave_language']) : 'en';

    if (empty($api_key)) {

        $this->fallbackResponse = [
            'text' => __("API key is not configured. Please set it in the Brave Search Settings.", 'mxchat'),
        ];
        return;
    }

    // Set API endpoint and parameters based on search type
    $api_url = $is_news_search
        ? 'https://api.search.brave.com/res/v1/news/search'
        : 'https://api.search.brave.com/res/v1/web/search';

    $query_args = [
        'q' => urlencode($search_query),
    ];

    if ($is_news_search) {
        $query_args['count'] = $news_count;
        $query_args['country'] = $country;
        $query_args['search_lang'] = $language;
    }

    $api_url = add_query_arg($query_args, $api_url);

    // Implement caching
    $transient_key = 'mxchat_search_' . md5($api_url);
    $body = get_transient($transient_key);

    if (false === $body) {
        $args = [
            'headers' => [
                'Accept' => 'application/json',
                'Accept-Encoding' => 'gzip',
                'Authorization' => 'Bearer ' . $api_key,
                'x-subscription-token' => $api_key,
            ],
            'timeout' => 10,
        ];

        $response = wp_remote_get($api_url, $args);

        if (is_wp_error($response)) {

            $this->fallbackResponse = [
                'text' => __("I'm sorry, I couldn't retrieve any information based on your request.", 'mxchat'),
            ];
            return;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        set_transient($transient_key, $body, HOUR_IN_SECONDS);
    }

  // Process the API response and build a more informative summary
    $text_summary = "";

    if ($is_news_search && isset($body['results']) && is_array($body['results']) && count($body['results']) > 0) {
        $text_summary = "Here are some recent news articles:\n\n";

        foreach ($body['results'] as $news) {
            $title = isset($news['title']) ? esc_html($news['title']) : __('No Title', 'mxchat');
            $url = isset($news['url']) ? esc_url($news['url']) : '#';
            $description = isset($news['description']) ? esc_html(strip_tags($news['description'])) : __('No description available.', 'mxchat');
            $age = isset($news['age']) ? esc_html($news['age']) : '';
            $hostname = isset($news['meta_url']['hostname']) ? esc_html($news['meta_url']['hostname']) : '';
            $thumbnail = isset($news['thumbnail']['src']) ? esc_url($news['thumbnail']['src']) : '';

            // Build text-only news item summary
            $text_summary .= "Title: {$title}\nURL: {$url}\nDescription: {$description}\nPublished: {$age}\n";
            if (!empty($hostname)) {
                $text_summary .= "Source: {$hostname}\n";
            }
            if (!empty($thumbnail)) {
                $text_summary .= "Thumbnail: ![Image]({$thumbnail})\n";
            }
            if (!empty($news['extra_snippets'])) {
                $extra_snippet_text = implode(" ", $news['extra_snippets']);
                $text_summary .= "Additional Info: {$extra_snippet_text}\n";
            }
            $text_summary .= "\n"; // Separate entries
        }
    } elseif (!$is_news_search && isset($body['web']['results']) && is_array($body['web']['results']) && count($body['web']['results']) > 0) {
        $text_summary = "Here are some relevant articles based on your query:\n\n";

        foreach ($body['web']['results'] as $result) {
            $title = isset($result['title']) ? esc_html($result['title']) : __('No Title', 'mxchat');
            $url = isset($result['url']) ? esc_url($result['url']) : '#';
            $description = isset($result['description']) ? esc_html(strip_tags($result['description'])) : __('No description available.', 'mxchat');
            $hostname = isset($result['meta_url']['hostname']) ? esc_html($result['meta_url']['hostname']) : '';
            $thumbnail = isset($result['thumbnail']['src']) ? esc_url($result['thumbnail']['src']) : '';

            // Build text-only web item summary
            $text_summary .= "Title: {$title}\nURL: {$url}\nDescription: {$description}\n";
            if (!empty($hostname)) {
                $text_summary .= "Source: {$hostname}\n";
            }
            if (!empty($thumbnail)) {
                $text_summary .= "Thumbnail: ![Image]({$thumbnail})\n";
            }
            if (!empty($result['extra_snippets'])) {
                $extra_snippet_text = implode(" ", $result['extra_snippets']);
                $text_summary .= "Additional Info: {$extra_snippet_text}\n";
            }
            $text_summary .= "\n"; // Separate entries
        }
    } else {

        $this->fallbackResponse = [
            'text' => __("I'm sorry, I couldn't retrieve any information based on your request.", 'mxchat'),
        ];
        return;
    }

    $this->fallbackResponse = [
        'text' => $text_summary,
    ];


}

//very good
public function mxchat_handle_image_search_request($message, $user_id, $session_id) {

    // Step 1: Interpret the search query for better results
    $refined_search_query = $this->mxchat_interpret_search_query($message);


    // If no query was interpreted, return a fallback message
    if (empty($refined_search_query)) {
        $this->fallbackResponse = [
            'text' => __("I'm sorry, I couldn't interpret your search query. Please specify what you'd like to see images of.", 'mxchat'),
            'html' => "",
        ];
        return;
    }

    // Brave API URL
    $api_url = 'https://api.search.brave.com/res/v1/images/search';

    // Retrieve Brave API settings
    $options = get_option('mxchat_options');
    $api_key = isset($options['brave_api_key']) ? sanitize_text_field($options['brave_api_key']) : '';

    if (empty($api_key)) {
/*
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log("Brave API key is missing.");
        }
*/

        $this->fallbackResponse = [
            'text' => __("API key is not configured. Please set it in the Brave Search Settings.", 'mxchat'),
            'html' => "",
        ];
        return;
    }

    $image_count = isset($options['brave_image_count']) ? intval($options['brave_image_count']) : 4;
    $safe_search = isset($options['brave_safe_search']) ? sanitize_text_field($options['brave_safe_search']) : 'strict';

    // Append query parameters based on settings
    $api_url = add_query_arg([
        'q' => rawurlencode($refined_search_query),
        'count' => $image_count,
        'safesearch' => $safe_search,
    ], $api_url);

/*
    // Log the final API URL for the search
    if (defined('WP_DEBUG') && WP_DEBUG) {
        error_log("Final API URL for Brave Image Search: " . esc_url_raw($api_url));
    }
*/


    // Implement caching
    $transient_key = 'mxchat_image_search_' . md5($refined_search_query);
    $body = get_transient($transient_key);

    if (false === $body) {
        $args = [
            'headers' => [
                'Accept' => 'application/json',
                'Accept-Encoding' => 'gzip',
                'X-Subscription-Token' => $api_key,
            ],
            'timeout' => 10,
        ];

        $response = wp_remote_get($api_url, $args);

        if (is_wp_error($response)) {
/*
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log("Brave Image API request failed: " . $response->get_error_message());
            }
*/

            $this->fallbackResponse = [
                'text' => __("I'm sorry, I couldn't retrieve any images based on your request.", 'mxchat'),
                'html' => "",
            ];
            return;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        set_transient($transient_key, $body, HOUR_IN_SECONDS);
    }

    // Process the API response
    if (isset($body['results']) && is_array($body['results']) && count($body['results']) > 0) {
        $html_output = '<div class="mxchat-image-gallery">';

        foreach ($body['results'] as $image) {
            $image_url = isset($image['url']) ? esc_url($image['url']) : '';
            $thumbnail_url = isset($image['thumbnail']['src']) ? esc_url($image['thumbnail']['src']) : '';
            $title = isset($image['title']) ? esc_html($image['title']) : __('Image', 'mxchat');

            if ($image_url && $thumbnail_url) {
                $html_output .= '<div class="mxchat-image-item">';
                $html_output .= '<strong class="mxchat-image-title">' . $title . '</strong>';
                $html_output .= '<a href="' . $image_url . '" target="_blank" rel="noopener noreferrer" class="mxchat-image-link">';
                $html_output .= '<img src="' . $thumbnail_url . '" alt="' . $title . '" class="mxchat-image-thumbnail">';
                $html_output .= '</a></div>';
            }
        }

        $html_output .= '</div>';

        $this->fallbackResponse = [
            'text' => "",
            'html' => $html_output,
        ];

        // Save response in chat history
        $this->mxchat_save_chat_message($session_id, 'bot', $html_output);

    } else {
/*
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log("Brave Image API response did not contain expected data structure or was empty: " . print_r($body, true));
        }
*/

        $this->fallbackResponse = [
            'text' => __("I'm sorry, I couldn't retrieve any images based on your request.", 'mxchat'),
            'html' => "",
        ];
    }
}
public function mxchat_interpret_search_query($user_query) {
    $system_prompt = "Interpret the user's request to provide only the essential keywords or phrases for image searching. Remove conversational language, politeness, or extra context. Return a concise search query that doesn't lose any of the original meaning.";

    // Retrieve OpenAI API key using 'api_key' as the option key
    $api_key = isset($this->options['api_key']) ? sanitize_text_field($this->options['api_key']) : sanitize_text_field(get_option('mxchat_options')['api_key']);

/*
    // Log the API key check, without exposing the key
    if (defined('WP_DEBUG') && WP_DEBUG) {
        error_log("Retrieved OpenAI API Key: " . ($api_key ? "Present" : "Missing"));
    }
*/


    if (empty($api_key)) {
        //error_log("OpenAI API key is missing.");
        return sanitize_text_field($user_query); // Default to the original query if API key is missing
    }

    $url = 'https://api.openai.com/v1/chat/completions';
    $args = [
        'headers' => [
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type' => 'application/json',
        ],
        'body' => wp_json_encode([
            'model' => 'gpt-3.5-turbo',
            'messages' => [
                ['role' => 'system', 'content' => $system_prompt],
                ['role' => 'user', 'content' => sanitize_text_field($user_query)],
            ],
            'temperature' => 0.2,
            'max_tokens' => 20,
        ]),
        'method' => 'POST',
    ];

    $response = wp_remote_post($url, $args);

    if (is_wp_error($response)) {
        //error_log("OpenAI request failed: " . $response->get_error_message());
        return sanitize_text_field($user_query); // Fallback to the original query if there's an error
    }

    $body = json_decode(wp_remote_retrieve_body($response), true);

    // Check for a valid response and sanitize output
    if (isset($body['choices'][0]['message']['content'])) {
        $interpreted_query = sanitize_text_field(trim($body['choices'][0]['message']['content']));

/*
        // Log the interpreted query for debugging
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log("Interpreted search query: " . $interpreted_query);
        }
*/

        return $interpreted_query;
    } else {
        //error_log("Unexpected API response format: " . print_r($body, true));
        return sanitize_text_field($user_query);
    }
}

//very good
public function mxchat_handle_add_to_cart_intent($message, $user_id, $session_id) {
    // Check if WooCommerce is active before proceeding
    if (!class_exists('WooCommerce')) {
       // error_log("WooCommerce is not active. Add-to-cart intent cannot be processed.");
        return false;
    }

    // Sanitize user ID for transient key
    $sanitized_user_id = sanitize_key($user_id);

    // Retrieve the last discussed product ID from the transient
    $last_product_id = get_transient('mxchat_last_discussed_product_' . $sanitized_user_id);
    if ($last_product_id) {
        // Attempt to add the product to the WooCommerce cart
        $added = WC()->cart->add_to_cart($last_product_id);
        $product = wc_get_product($last_product_id);

        if ($added && $product) {
            // Escape product name for safety
            $product_name = esc_html($product->get_name());

            // Success response if the product is added to the cart
            $response = "The product '{$product_name}' has been added to your cart. To proceed to checkout, please type 'checkout'.";
            set_transient('mxchat_checkout_prompt_' . $sanitized_user_id, true, 5 * MINUTE_IN_SECONDS);

            // Save the response in chat history and send a JSON response
            $this->mxchat_save_chat_message($session_id, 'bot', $response);
            wp_send_json(['message' => $response]);
            wp_die();
        } else {
            // Failure response if the product could not be added
            $response = "Sorry, I couldn't add the product to your cart. Please try again.";
            $this->mxchat_save_chat_message($session_id, 'bot', $response);
            wp_send_json(['message' => $response]);
            wp_die();
        }
    } else {
        // Prompt the user to specify the product name if no product was found
        $response = "I couldn't find the product to add. Please mention the product name again.";
        $this->mxchat_save_chat_message($session_id, 'bot', $response);
        wp_send_json(['message' => $response]);
        wp_die();
    }
}
public function mxchat_handle_checkout_intent($message, $user_id, $session_id) {
    // Ensure WooCommerce is available and perform checkout actions
    if (class_exists('WooCommerce')) {
        // Sanitize user ID for transient key
        $sanitized_user_id = sanitize_key($user_id);

        $checkout_prompt = get_transient('mxchat_checkout_prompt_' . $sanitized_user_id);

        // Check if there is an active checkout prompt and if the cart has items
        if ($checkout_prompt && WC()->cart->get_cart_contents_count() > 0) {
            // Get and escape the checkout URL
            $checkout_url = wc_get_checkout_url();
            $checkout_url = esc_url_raw($checkout_url);

            $response = "Great! Redirecting you to the checkout page...";

            // Clear the checkout prompt transient to avoid duplicate responses
            delete_transient('mxchat_checkout_prompt_' . $sanitized_user_id);

            // Save and send the response with the checkout URL for redirection
            $this->mxchat_save_chat_message($session_id, 'bot', $response);
            wp_send_json(['message' => $response, 'redirect_url' => $checkout_url]);
            wp_die();
        } else {
            // If no active checkout prompt or empty cart, prompt user to add items first
            $response = "It seems like there is no active checkout prompt or no items in your cart. Please add a product to the cart first.";
            $this->mxchat_save_chat_message($session_id, 'bot', $response);
            wp_send_json(['message' => $response]);
            wp_die();
        }
    } else {
        // WooCommerce is not available, send an error message
        $response = "WooCommerce is not active on this site, so checkout is not possible.";
        $this->mxchat_save_chat_message($session_id, 'bot', $response);
        wp_send_json(['message' => $response]);
        wp_die();
    }
}

//very good
public static function mxchat_extract_product_id_from_message($message) {
    // Sanitize the message input
    $message = sanitize_text_field($message);

    // Convert Markdown links to plain URLs
    $message = preg_replace('/\[(.*?)\]\((.*?)\)/', '$2', $message);

    // Match all URLs in the message
    preg_match_all('/https?:\/\/[^\s]+/', $message, $matches);

    if (!empty($matches[0])) {
        foreach ($matches[0] as $url) {
            // Sanitize the URL
            $url = esc_url_raw($url);

            // Try to get the post ID from the URL
            $post_id = url_to_postid($url);

            if ($post_id) {
                // Check if the post type is 'product'
                if (get_post_type($post_id) === 'product') {
                    return $post_id;
                }
            }
        }
    }
    return false;
}


//very good
private function add_email_to_loops($email) {
    // Sanitize the email
    $email = sanitize_email($email);

    // Retrieve and sanitize options
    $api_key = isset($this->options['loops_api_key']) ? sanitize_text_field($this->options['loops_api_key']) : '';
    $mailing_list_id = isset($this->options['loops_mailing_list']) ? sanitize_text_field($this->options['loops_mailing_list']) : '';

    // Check for missing API key or mailing list ID
    if (empty($api_key) || empty($mailing_list_id)) {
        //error_log('Loops API key or mailing list ID is missing.');
        return;
    }

    $data = array(
        'email'        => $email,
        'subscribed'   => true,
        'source'       => 'MxChat AI Chatbot',
        'mailingLists' => array($mailing_list_id => true),
    );

    $url = 'https://app.loops.so/api/v1/contacts/create';
    $args = array(
        'body'    => wp_json_encode($data),
        'headers' => array(
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type'  => 'application/json',
        ),
        'method'  => 'POST',
        'timeout' => 45,
    );

    $response = wp_remote_post($url, $args);

    // Handle errors in the API request
    if (is_wp_error($response)) {
        //error_log('Error adding email to Loops: ' . $response->get_error_message());
        return;
    }

    // Check for non-200 HTTP responses
    $response_code = wp_remote_retrieve_response_code($response);
    if ($response_code != 200) {
        $response_body = wp_remote_retrieve_body($response);
        //error_log('Loops API responded with code ' . $response_code . ': ' . $response_body);
    }
}

private function mxchat_get_user_identifier() {
    return MxChat_User::mxchat_get_user_identifier();
}

private function mxchat_generate_embedding($text, $api_key) {
        $endpoint = 'https://api.openai.com/v1/embeddings';

        $body = wp_json_encode([
            'input' => $text,
            'model' => 'text-embedding-ada-002'
        ]);

        $args = [
            'body' => $body,
            'headers' => [
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . $api_key,
            ],
            'timeout' => 60,
            'redirection' => 5,
            'blocking' => true,
            'httpversion' => '1.0',
            'sslverify' => true,
        ];

        $response = wp_remote_post($endpoint, $args);

        if (is_wp_error($response)) {
            return null;
        }

        $response_body = json_decode(wp_remote_retrieve_body($response), true);

        if (isset($response_body['data'][0]['embedding']) && is_array($response_body['data'][0]['embedding'])) {
            return $response_body['data'][0]['embedding'];
        } else {
            return null;
        }
    }

private function mxchat_find_relevant_content($user_embedding) {
    global $wpdb;
    $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';

    // Define a cache key for embeddings
    $cache_key = 'mxchat_system_prompt_embeddings';

    // Attempt to get the embeddings from the cache
    $embeddings = wp_cache_get($cache_key, 'mxchat_system_prompts');

    if ($embeddings === false) {
        // Cache miss, query the database and cache the results
        $query = "SELECT id, embedding_vector FROM {$system_prompt_table}";
        $embeddings = $wpdb->get_results($query);

        if ($embeddings === null || empty($embeddings)) {
            return null; // Return null to handle no embeddings gracefully
        }

        // Cache the results if successful
        wp_cache_set($cache_key, $embeddings, 'mxchat_system_prompts', 3600); // Cache for 1 hour
    }

    $most_relevant_id = null;
    $highest_similarity = -INF;

    foreach ($embeddings as $embedding) {
        $database_embedding = $embedding->embedding_vector
            ? unserialize($embedding->embedding_vector, ['allowed_classes' => false])
            : null;

        if (is_array($database_embedding) && is_array($user_embedding)) {
            $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $database_embedding);

            if ($similarity > $highest_similarity) {
                $highest_similarity = $similarity;
                $most_relevant_id = $embedding->id;
            }
        }
    }

    if ($most_relevant_id !== null) {
        // Fetch content with product links
        return $this->fetch_content_with_product_links($most_relevant_id);
    }

    return null; // Return null if no relevant content is found
}

private function fetch_content_with_product_links($most_relevant_id) {
    global $wpdb;
    $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';

    // Fetch the article content and associated product URL
    $query = $wpdb->prepare("SELECT article_content, source_url FROM {$system_prompt_table} WHERE id = %d", $most_relevant_id);
    $result = $wpdb->get_row($query);

    if ($result) {
        // Append the product link to the content if available
        $content = $result->article_content;
        if (!empty($result->source_url)) {
            $content .= "\n\nFor more details, check out this product: " . esc_url($result->source_url);
        }
        return $content;
    }

    return null;
}

private function mxchat_generate_response($relevant_content, $api_key, $xai_api_key, $claude_api_key, $conversation_history) {
    if (!$relevant_content) {
        return "I'm sorry, I couldn't find relevant information on that topic.";
    }

    // Check the selected model
    $selected_model = isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5-turbo';

    // Call the appropriate function based on the selected model
    if (strpos($selected_model, 'claude') !== false) {
        return $this->mxchat_generate_response_claude($selected_model, $claude_api_key, $conversation_history, $relevant_content);
    } elseif ($selected_model === 'grok-beta') {
        return $this->mxchat_generate_response_xai($selected_model, $xai_api_key, $conversation_history, $relevant_content);
    } else {
        return $this->mxchat_generate_response_openai($selected_model, $api_key, $conversation_history, $relevant_content);
    }
}

private function mxchat_generate_response_openai($selected_model, $api_key, $conversation_history, $relevant_content) {
    // Get system prompt instructions from options
    $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';

    // Add system prompt to relevant content
    $content_with_instructions = $system_prompt_instructions . " " . $relevant_content;

    // Prepend system instructions to the conversation history
    array_unshift($conversation_history, [
        'role' => 'system',
        'content' => "Here are your instructions: " . $content_with_instructions
    ]);

    // Ensure consistency: Replace 'bot' role with 'assistant' in conversation history
    foreach ($conversation_history as &$message) {
        if ($message['role'] === 'bot') {
            $message['role'] = 'assistant';
        }
    }

    // Build the request body
    $body = json_encode([
        'model' => $selected_model,
        'messages' => $conversation_history,
        'temperature' => 0.8,
        'stream' => false
    ]);

    // Set up the API request
    $args = [
        'body'        => $body,
        'headers'     => [
            'Content-Type' => 'application/json',
            'Authorization' => 'Bearer ' . $api_key,
        ],
        'timeout'     => 60,
        'redirection' => 5,
        'blocking'    => true,
        'httpversion' => '1.0',
        'sslverify'   => true,
    ];

    // Make the API request
    $response = wp_remote_post('https://api.openai.com/v1/chat/completions', $args);

    // Process the response
    if (is_wp_error($response)) {
        return "Sorry, there was an error processing your request.";
    }

    $response_body = json_decode(wp_remote_retrieve_body($response), true);

    if (isset($response_body['choices'][0]['message']['content'])) {
        return trim($response_body['choices'][0]['message']['content']);
    } else {
        return "Sorry, I couldn't process that request.";
    }
}

private function mxchat_generate_response_xai($selected_model, $xai_api_key, $conversation_history, $relevant_content) {
    // Get system prompt instructions from options
    $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';

    // Add system prompt to relevant content
    $content_with_instructions = $system_prompt_instructions . " " . $relevant_content;

    // Prepend system instructions to the conversation history
    array_unshift($conversation_history, [
        'role' => 'system',
        'content' => "Here are your instructions: " . $content_with_instructions
    ]);

    // Ensure consistency: Replace 'bot' role with 'assistant' in conversation history
    foreach ($conversation_history as &$message) {
        if ($message['role'] === 'bot') {
            $message['role'] = 'assistant';
        }
    }

    // Build the request body
    $body = json_encode([
        'model' => $selected_model,
        'messages' => $conversation_history,
        'temperature' => 0.8,
        'stream' => false
    ]);

    // Set up the API request
    $args = [
        'body'        => $body,
        'headers'     => [
            'Content-Type' => 'application/json',
            'Authorization' => 'Bearer ' . $xai_api_key,
        ],
        'timeout'     => 60,
        'redirection' => 5,
        'blocking'    => true,
        'httpversion' => '1.0',
        'sslverify'   => true,
    ];

    // Make the API request
    $response = wp_remote_post('https://api.x.ai/v1/chat/completions', $args);

    // Process the response
    if (is_wp_error($response)) {
        return "Sorry, there was an error processing your request.";
    }

    $response_body = json_decode(wp_remote_retrieve_body($response), true);

    if (isset($response_body['choices'][0]['message']['content'])) {
        return trim($response_body['choices'][0]['message']['content']);
    } else {
        return "Sorry, I couldn't process that request.";
    }
}

private function mxchat_generate_response_claude($selected_model, $claude_api_key, $conversation_history, $relevant_content) {
    // Get system prompt instructions from options for Claude's top-level system parameter
    $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';

    // Ensure consistency: Replace 'bot' role with 'assistant' in conversation history
    foreach ($conversation_history as &$message) {
        if ($message['role'] === 'bot') {
            $message['role'] = 'assistant';
        }
    }

    // Add relevant content as the latest user message in conversation history
    $conversation_history[] = [
        'role' => 'user',
        'content' => $relevant_content
    ];

    // Build the request body with Claude's expected structure, using system instructions as a top-level parameter
    $body = json_encode([
        'model' => $selected_model,
        'max_tokens' => 1000,
        'temperature' => 0.8,
        'system' => $system_prompt_instructions, // Set the system prompt at the top level as required
        'messages' => $conversation_history
    ]);

    // Set up the API request with the necessary headers
    $args = [
        'body'        => $body,
        'headers'     => [
            'Content-Type' => 'application/json',
            'x-api-key' => $claude_api_key,
            'anthropic-version' => '2023-06-01',
        ],
        'timeout'     => 60,
        'redirection' => 5,
        'blocking'    => true,
        'httpversion' => '1.0',
        'sslverify'   => true,
    ];

    // Make the API request
    $response = wp_remote_post('https://api.anthropic.com/v1/messages', $args);

/*
    // Check for errors and log the response for debugging
    if (is_wp_error($response)) {
        error_log("Claude API request error: " . print_r($response->get_error_message(), true));
        return "Sorry, there was an error processing your request.";
    }
*/


    // Decode the response and parse according to the expected Claude response structure
    $response_body = json_decode(wp_remote_retrieve_body($response), true);
    //error_log("Claude API response: " . print_r($response_body, true));

    // Check if the response has the expected 'content' array with 'text' blocks
    if (isset($response_body['content'][0]['text'])) {
        return trim($response_body['content'][0]['text']);
    } else {
        return "Sorry, I couldn't process that request.";
    }
}

public function mxchat_dismiss_pre_chat_message() {
    // Get and sanitize the user identifier
    $user_id = $this->mxchat_get_user_identifier();
    $user_id = sanitize_key($user_id);

    // Set a transient to track that the user has dismissed the pre-chat message
    $transient_key = 'mxchat_pre_chat_message_dismissed_' . $user_id;
    set_transient($transient_key, true, DAY_IN_SECONDS);

    wp_send_json_success();
}

public function mxchat_check_pre_chat_message_status() {
    // Get and sanitize the user identifier
    $user_id = $this->mxchat_get_user_identifier();
    $user_id = sanitize_key($user_id);

    // Check if the transient exists (i.e., if the message was dismissed)
    $transient_key = 'mxchat_pre_chat_message_dismissed_' . $user_id;
    $dismissed = get_transient($transient_key);

    // Log the result to see if it's being set correctly
    //error_log("Check pre-chat message dismissed for $user_id: " . ($dismissed ? 'Yes' : 'No'));

    if ($dismissed) {
        wp_send_json_success(['dismissed' => true]);
    } else {
        wp_send_json_success(['dismissed' => false]);
    }

    wp_die();
}

private function mxchat_calculate_cosine_similarity($vectorA, $vectorB) {
        if (!is_array($vectorA) || !is_array($vectorB) || empty($vectorA) || empty($vectorB)) {
            return 0;
        }

        $dotProduct = array_sum(array_map(function ($a, $b) {
            return $a * $b;
        }, $vectorA, $vectorB));
        $normA = sqrt(array_sum(array_map(function ($a) {
            return $a * $a;
        }, $vectorA)));
        $normB = sqrt(array_sum(array_map(function ($b) {
            return $b * $b;
        }, $vectorB)));

        if ($normA == 0 || $normB == 0) {
            return 0;
        }

        return $dotProduct / ($normA * $normB);
    }

public function mxchat_enqueue_scripts_styles() {
        // Define version numbers for the styles and scripts
        $chat_style_version = '1.3'; // Replace with your actual version
        $chat_script_version = '1.3'; // Replace with your actual version

        // Correct path to the script file
        wp_enqueue_script(
            'mxchat-chat-js', // Handle for the script
            plugin_dir_url(__FILE__) . '../js/chat-script.js', // Correct path using __FILE__
            array('jquery'), // Dependencies
            $chat_script_version, // Version for cache busting
            true // Load script in footer
        );

        // Enqueue the CSS file similarly
        wp_enqueue_style(
            'mxchat-chat-css', // Handle for the style
            plugin_dir_url(__FILE__) . '../css/chat-style.css', // Correct path using __FILE__
            array(), // No dependencies
            $chat_style_version // Version for cache busting
        );

        // Fetch options from the database
        $this->options = get_option('mxchat_options');

        // Prepare settings to pass to JavaScript
        $style_settings = array(
            'ajax_url' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('mxchat_chat_nonce'), // Nonce for security
            'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
            'appendWidgetToBody' => $this->options['append_to_body'] ?? 'off'
        );

        // Localize the script with necessary data
        wp_localize_script('mxchat-chat-js', 'mxchatChat', $style_settings);
    }

public function mxchat_reset_rate_limits() {
        global $wpdb;

        // Define a cache key pattern for rate limits
        $cache_key_pattern = 'mxchat_chat_limit_%';

        // Retrieve all option names matching the pattern
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
        $option_names = $wpdb->get_col("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE 'mxchat_chat_limit_%'");

        // db call ok; no-cache ok
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- db call ok
        $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE 'mxchat_chat_limit_%'");

        // Clear the relevant cache entries
        foreach ($option_names as $option_name) {
            wp_cache_delete($option_name, 'options');
        }

        // Optionally, clear a general cache if you have one
        wp_cache_delete('mxchat_all_chat_limits', 'options');
    }

private function mxchat_fetch_woocommerce_products() {
    // Ensure WooCommerce is active
    if (!class_exists('WooCommerce')) {
        return [];
    }

    $args = array(
        'post_type' => 'product',
        'post_status' => 'publish',
        'posts_per_page' => -1,
    );

    $products = get_posts($args);
    $product_data = [];

    foreach ($products as $product) {
        $product_id = $product->ID;
        $product_obj = wc_get_product($product_id);

        $product_data[] = array(
            'id' => $product_id,
            'name' => $product_obj->get_name(),
            'description' => $product_obj->get_description(),
            'short_description' => $product_obj->get_short_description(),
            'url' => get_permalink($product_id),
            'price' => $product_obj->get_regular_price(),
            'sale_price' => $product_obj->get_sale_price(),
            'stock_status' => $product_obj->get_stock_status(),
            'sku' => $product_obj->get_sku(),
            'in_stock' => $product_obj->is_in_stock(),
            'total_sales' => $product_obj->get_total_sales(),
        );
    }

    return $product_data;
}

}
?>

```
