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/js/chat-script.js
jQuery(document).ready(function($) {
    
    // ====================================
    // GLOBAL VARIABLES & CONFIGURATION
    // ====================================
    const toolbarIconColor = mxchatChat.toolbar_icon_color || '#212121';
    
    // Initialize color settings
    var userMessageBgColor = mxchatChat.user_message_bg_color;
    var userMessageFontColor = mxchatChat.user_message_font_color;
    var botMessageBgColor = mxchatChat.bot_message_bg_color;
    var botMessageFontColor = mxchatChat.bot_message_font_color;
    var liveAgentMessageBgColor = mxchatChat.live_agent_message_bg_color;
    var liveAgentMessageFontColor = mxchatChat.live_agent_message_font_color;
    
    var linkTarget = mxchatChat.link_target_toggle === 'on' ? '_blank' : '_self';
    let lastSeenMessageId = '';
    let notificationCheckInterval;
    let notificationBadge;
    var sessionId = getChatSession();
    let pollingInterval;
    let processedMessageIds = new Set();
    let activePdfFile = null;
    let activeWordFile = null;


    // ====================================
    // SESSION MANAGEMENT
    // ====================================
    
    function getChatSession() {
        var sessionId = getCookie('mxchat_session_id');
        //console.log("Session ID retrieved from cookie: ", sessionId);
    
        if (!sessionId) {
            sessionId = generateSessionId();
            //console.log("Generated new session ID: ", sessionId);
            setChatSession(sessionId);
        }
    
        //console.log("Final session ID: ", sessionId);
        return sessionId;
    }
    
    function setChatSession(sessionId) {
        // Set the cookie with a 24-hour expiration (86400 seconds)
        document.cookie = "mxchat_session_id=" + sessionId + "; path=/; max-age=86400; SameSite=Lax";
    }
    
    function getCookie(name) {
        let value = "; " + document.cookie;
        let parts = value.split("; " + name + "=");
        if (parts.length == 2) return parts.pop().split(";").shift();
    }
    
    function generateSessionId() {
        return 'mxchat_chat_' + Math.random().toString(36).substr(2, 9);
    }

// ====================================
// CONTEXTUAL AWARENESS FUNCTIONALITY
// ====================================

function getPageContext() {
    // Check if contextual awareness is enabled
    if (mxchatChat.contextual_awareness_toggle !== 'on') {
        return null;
    }
    
    // Get page URL
    const pageUrl = window.location.href;
    
    // Get page title
    const pageTitle = document.title || '';
    
    // Get main content from the page
    let pageContent = '';
    
    // Try to get content from common content areas
    const contentSelectors = [
        'main',
        '[role="main"]',
        '.content',
        '.main-content',
        '.post-content',
        '.entry-content',
        '.page-content',
        'article',
        '#content',
        '#main'
    ];
    
    let contentElement = null;
    for (const selector of contentSelectors) {
        contentElement = document.querySelector(selector);
        if (contentElement) {
            break;
        }
    }
    
    // If no specific content area found, use body but exclude header, footer, nav, sidebar
    if (!contentElement) {
        contentElement = document.body;
    }
    
    if (contentElement) {
        // Clone the element to avoid modifying the original
        const clone = contentElement.cloneNode(true);
        
        // Remove unwanted elements
        const unwantedSelectors = [
            'header',
            'footer',
            'nav',
            '.navigation',
            '.sidebar',
            '.widget',
            '.menu',
            'script',
            'style',
            '.comments',
            '#comments',
            '.breadcrumb',
            '.breadcrumbs',
            '#floating-chatbot',
            '#floating-chatbot-button',
            '.mxchat',
            '[class*="chat"]',
            '[id*="chat"]'
        ];
        
        unwantedSelectors.forEach(selector => {
            const elements = clone.querySelectorAll(selector);
            elements.forEach(el => el.remove());
        });
        
        //   Extract MxChat context data attributes before getting text content
        const contextData = [];
        clone.querySelectorAll('[data-mxchat-context]').forEach(el => {
            const contextValue = el.dataset.mxchatContext;
            if (contextValue && contextValue.trim()) {
                contextData.push(contextValue);
            }
        });
        
        // Get text content and clean it up
        pageContent = clone.textContent || clone.innerText || '';
        
        // Add context data to page content if any were found
        if (contextData.length > 0) {
            pageContent += '\n\nAdditional Context:\n' + contextData.join('\n');
        }
        
        // Clean up whitespace and limit length
        pageContent = pageContent
            .replace(/\s+/g, ' ')
            .trim()
            .substring(0, 3000); // Limit to 3000 characters to avoid token limits
    }
    
    // Only return context if we have meaningful content
    if (!pageContent || pageContent.length < 50) {
        return null;
    }
    
    return {
        url: pageUrl,
        title: pageTitle,
        content: pageContent
    };
}

//   Track originating page when chat starts
function trackOriginatingPage() {
    const sessionId = getChatSession();
    const pageUrl = window.location.href;
    const pageTitle = document.title || 'Untitled Page';
    
    // Only track once per session
    const trackingKey = 'mxchat_originating_tracked_' + sessionId;
    if (sessionStorage.getItem(trackingKey)) {
        return;
    }
    
    $.ajax({
        url: mxchatChat.ajax_url,
        type: 'POST',
        data: {
            action: 'mxchat_track_originating_page',
            session_id: sessionId,
            page_url: pageUrl,
            page_title: pageTitle,
            nonce: mxchatChat.nonce
        },
        success: function(response) {
            if (response.success) {
                sessionStorage.setItem(trackingKey, 'true');
            }
        }
    });
}

// ====================================
// CORE CHAT FUNCTIONALITY
// ====================================
// Update your existing sendMessage function
function sendMessage() {
    var message = $('#chat-input').val();
    
    // ADD PROMPT HOOK HERE
    if (typeof customMxChatFilter === 'function') { 
        message = customMxChatFilter(message, "prompt"); 
    }
    
    if (message) {
        appendMessage("user", message);
        $('#chat-input').val('');
        $('#chat-input').css('height', 'auto');

        if (hasQuickQuestions()) {
            collapseQuickQuestions();
        }
        appendThinkingMessage();
        scrollToBottom();

        const currentModel = mxchatChat.model || 'gpt-4o';

        // Check if streaming is enabled AND supported for this model
        if (shouldUseStreaming(currentModel)) {
            callMxChatStream(message, function(response) {
                $('.bot-message.temporary-message').removeClass('temporary-message');
            });
        } else {
            callMxChat(message, function(response) {
                replaceLastMessage("bot", response);
            });
        }
    }
}

// Update your existing sendMessageToChatbot function
function sendMessageToChatbot(message) {
    // ADD PROMPT HOOK HERE
    if (typeof customMxChatFilter === 'function') {
        message = customMxChatFilter(message, "prompt");
    }
    
    var sessionId = getChatSession();

    if (hasQuickQuestions()) {
        collapseQuickQuestions();
    }
    appendThinkingMessage();
    scrollToBottom();

    const currentModel = mxchatChat.model || 'gpt-4o';

    // Check if streaming is enabled AND supported for this model
    if (shouldUseStreaming(currentModel)) {
        callMxChatStream(message, function(response) {
            $('.bot-message.temporary-message').removeClass('temporary-message');
        });
    } else {
        callMxChat(message, function(response) {
            $('.temporary-message').remove();
            replaceLastMessage("bot", response);
        });
    }
}

// Updated shouldUseStreaming function with debugging
function shouldUseStreaming(model) {
    // Check if streaming is enabled in settings (using your toggle naming pattern)
    const streamingEnabled = mxchatChat.enable_streaming_toggle === 'on';
    
    // Check if model supports streaming
    const streamingSupported = isStreamingSupported(model);
    
    
    // Only use streaming if both enabled and supported
    return streamingEnabled && streamingSupported;
}

// Helper function to handle chat mode updates
function handleChatModeUpdates(response, responseText) {
    // Check for explicit chat mode in response (THIS IS THE KEY FIX)
    if (response.chat_mode) {
        updateChatModeIndicator(response.chat_mode);
        return; // Return early since we found explicit mode
    }
    // Check for fallback response chat mode
    else if (response.fallbackResponse && response.fallbackResponse.chat_mode) {
        updateChatModeIndicator(response.fallbackResponse.chat_mode);
        return; // Return early since we found explicit mode
    }
    
    // Only do text-based detection if no explicit mode was provided
    // Check for specific AI chatbot response text
    if (responseText === 'You are now chatting with the AI chatbot.' || 
        responseText.includes('now chatting with the AI') ||
        responseText.includes('switched to AI mode') ||
        responseText.includes('AI chatbot is now')) {
        updateChatModeIndicator('ai');
    }
    // Check for agent transfer messages
    else if (responseText.includes('agent') && 
             (responseText.includes('transfer') || responseText.includes('connected'))) {
        updateChatModeIndicator('agent');
    }
}

//Function to get bot ID from the chatbot wrapper
function getMxChatBotId() {
    const chatbotWrapper = document.getElementById('mxchat-chatbot-wrapper');
    return chatbotWrapper ? chatbotWrapper.getAttribute('data-bot-id') || 'default' : 'default';
}

function callMxChat(message, callback) {
    // Get page context if contextual awareness is enabled
    const pageContext = getPageContext();
    
    //   Get bot ID
    const botId = getMxChatBotId();
    
    // Prepare AJAX data
    const ajaxData = {
        action: 'mxchat_handle_chat_request',
        message: message,
        session_id: getChatSession(),
        nonce: mxchatChat.nonce,
        current_page_url: window.location.href,
        current_page_title: document.title,
        bot_id: botId //   Include bot ID
    };
    
    // Add page context if available
    if (pageContext) {
        ajaxData.page_context = JSON.stringify(pageContext);
    }
    
    // CHECK FOR VISION FLAGS AND ADD THEM
    if (window.mxchatVisionProcessed) {
        ajaxData.vision_processed = true;
        ajaxData.original_user_message = window.mxchatOriginalMessage || message;
        ajaxData.vision_images_count = window.mxchatVisionImagesCount || 0;
        // Clear the flags after use
        window.mxchatVisionProcessed = false;
        window.mxchatOriginalMessage = null;
        window.mxchatVisionImagesCount = 0;
    }
    
    $.ajax({
        url: mxchatChat.ajax_url,
        type: 'POST',
        dataType: 'json',
        data: ajaxData,
        success: function(response) {
            
                // IMMEDIATE CHAT MODE UPDATE - This should be FIRST
    if (response.chat_mode) {
        //console.log("Updating chat mode to:", response.chat_mode);
        updateChatModeIndicator(response.chat_mode);
    }
    
    // Also check in data property if response is wrapped
    if (response.data && response.data.chat_mode) {
        //console.log("Updating chat mode from data to:", response.data.chat_mode);
        updateChatModeIndicator(response.data.chat_mode);
    }
            // Log the full response for debugging
            //console.log("API Response:", response);

            // IMMEDIATE CHAT MODE UPDATE - Handle this first, before any other processing
            if (response.chat_mode) {
                //console.log("Updating chat mode immediately to:", response.chat_mode);
                updateChatModeIndicator(response.chat_mode);
            }

            // First check if this is a successful response by looking for text, html, or message fields
            // This preserves compatibility with your server response format
            if (response.text !== undefined || response.html !== undefined || response.message !== undefined || 
                (response.success === true && response.data && response.data.status === 'waiting_for_agent')) {

                // Handle successful response - this is your original success handling code

                // Handle other responses
                let responseText = response.text || '';
                let responseHtml = response.html || '';
                let responseMessage = response.message || '';

                // Add PDF filename handling
                if (response.data && response.data.filename) {
                    showActivePdf(response.data.filename);
                    activePdfFile = response.data.filename;
                }

                // Add redirect check here
                if (response.redirect_url) {
                    if (responseText) {
                        replaceLastMessage("bot", responseText);
                    }
                    setTimeout(() => {
                        window.location.href = response.redirect_url;
                    }, 1500);
                    return;
                }

                // Check for live agent response
                if (response.success && response.data && response.data.status === 'waiting_for_agent') {
                    updateChatModeIndicator('agent');
                    return;
                }

                // Handle the message and show notification if chat is hidden
                if (responseText || responseHtml || responseMessage) {
                    
                    // ADD RESPONSE HOOKS HERE - BEFORE DISPLAYING
                    if (responseText && typeof customMxChatFilter === 'function') {
                        responseText = customMxChatFilter(responseText, "response");
                    }
                    if (responseMessage && typeof customMxChatFilter === 'function') {
                        responseMessage = customMxChatFilter(responseMessage, "response");
                    }
                    
                    // Update the messages as before
                    if (responseText && responseHtml) {
                        replaceLastMessage("bot", responseText, responseHtml);
                    } else if (responseText) {
                        replaceLastMessage("bot", responseText);
                    } else if (responseHtml) {
                        replaceLastMessage("bot", "", responseHtml);
                    } else if (responseMessage) {
                        replaceLastMessage("bot", responseMessage);
                    }

                    // Check if chat is hidden and show notification
                    if ($('#floating-chatbot').hasClass('hidden')) {
                        const badge = $('#chat-notification-badge');
                        if (badge.length) {
                            badge.show();
                        }
                    }
                } else {
                    ////console.error("Unexpected response format:", response);
                    replaceLastMessage("bot", "I received an empty response. Please try again or contact support if this persists.");
                }

                if (response.message_id) {
                    lastSeenMessageId = response.message_id;
                }

                return;
            }

            // If we got here, it's likely an error response
            // Now we can check for error conditions with our robust error handling

            let errorMessage = "";
            let errorCode = "";

            // Check various possible error locations in the response
            if (response.data && response.data.error_message) {
                errorMessage = response.data.error_message;
                errorCode = response.data.error_code || "";
            } else if (response.error_message) {
                errorMessage = response.error_message;
                errorCode = response.error_code || "";
            } else if (response.message) {
                errorMessage = response.message;
            } else if (typeof response.data === 'string') {
                errorMessage = response.data;
            } else if (!response.success) {
                // Explicit check for success: false without other error info
                errorMessage = "An error occurred. Please try again or contact support.";
            } else {
                // Fallback for any other unexpected response format
                errorMessage = "Unexpected response received. Please try again or contact support.";
            }

            // Log the error with code for debugging
            //console.log("Response data:", response.data);
            //console.error("API Error:", errorMessage, "Code:", errorCode);

            // Format user-friendly error message
            let displayMessage = errorMessage;

            // Customize message for admin users
            if (mxchatChat.is_admin) {
                // For admin users, show more technical details including error code
                displayMessage = errorMessage + (errorCode ? " (Error code: " + errorCode + ")" : "");
            }

            replaceLastMessage("bot", displayMessage);
        },
        error: function(xhr, status, error) {
            //console.error("AJAX Error:", status, error);
            //console.log("Response Text:", xhr.responseText);

            let errorMessage = "An unexpected error occurred.";

            // Try to parse the response if it's JSON
            try {
                const responseJson = JSON.parse(xhr.responseText);
                //console.log("Parsed error response:", responseJson);

                if (responseJson.data && responseJson.data.error_message) {
                    errorMessage = responseJson.data.error_message;
                } else if (responseJson.message) {
                    errorMessage = responseJson.message;
                }
            } catch (e) {
                // Not JSON or parsing failed, use HTTP status based messages
                if (xhr.status === 0) {
                    errorMessage = "Network error: Please check your internet connection.";
                } else if (xhr.status === 403) {
                    errorMessage = "Access denied: Your session may have expired. Please refresh the page.";
                } else if (xhr.status === 404) {
                    errorMessage = "API endpoint not found. Please contact support.";
                } else if (xhr.status === 429) {
                    errorMessage = "Too many requests. Please try again in a moment.";
                } else if (xhr.status >= 500) {
                    errorMessage = "Server error: The server encountered an issue. Please try again later.";
                }
            }

            replaceLastMessage("bot", errorMessage);
        }
    });
}

function callMxChatStream(message, callback) {
    //console.log("Using streaming for message:", message);
    
    const currentModel = mxchatChat.model || 'gpt-4o';
    if (!isStreamingSupported(currentModel)) {
        //console.log("Streaming not supported, falling back to regular call");
        callMxChat(message, callback);
        return;
    }

    // Get page context if contextual awareness is enabled
    const pageContext = getPageContext();
    
    //   Get bot ID
    const botId = getMxChatBotId();

    const formData = new FormData();
    formData.append('action', 'mxchat_stream_chat');
    formData.append('message', message);
    formData.append('session_id', getChatSession());
    formData.append('nonce', mxchatChat.nonce);
    formData.append('current_page_url', window.location.href);
    formData.append('current_page_title', document.title);
    formData.append('bot_id', botId); //   Include bot ID
    
    // Add page context if available
    if (pageContext) {
        formData.append('page_context', JSON.stringify(pageContext));
    }

    // CHECK FOR VISION FLAGS AND ADD THEM
    if (window.mxchatVisionProcessed) {
        formData.append('vision_processed', 'true');
        formData.append('original_user_message', window.mxchatOriginalMessage || message);
        formData.append('vision_images_count', window.mxchatVisionImagesCount || '0');
        // Clear the flags after use
        window.mxchatVisionProcessed = false;
        window.mxchatOriginalMessage = null;
        window.mxchatVisionImagesCount = 0;
    }

    let accumulatedContent = '';
    let testingDataReceived = false;
    let streamingStarted = false;

    fetch(mxchatChat.ajax_url, {
        method: 'POST',
        body: formData,
        credentials: 'same-origin'
    })
    .then(response => {
        //console.log("Streaming response received:", response);
        
        // Store the response for potential fallback handling
        const responseClone = response.clone();
        
        if (!response.ok) {
            // Try to get error details from response
            return responseClone.json().then(errorData => {
                //console.log("Server returned error response:", errorData);
                throw { isServerError: true, data: errorData };
            }).catch(() => {
                throw new Error('Network response was not ok');
            });
        }

    // Check if response is JSON instead of streaming
    const contentType = response.headers.get('content-type');
    if (contentType && contentType.includes('application/json')) {
        //console.log("Received JSON response instead of stream");
        return responseClone.json().then(data => {
            //console.log("Processing JSON response:", data);
            
            // IMMEDIATE CHAT MODE UPDATE for JSON response
            if (data.chat_mode) {
                //console.log("JSON Response: Updating chat mode to:", data.chat_mode);
                updateChatModeIndicator(data.chat_mode);
            }
    
            // Check for testing panel
            if (window.mxchatTestPanelInstance && data.testing_data) {
                //console.log('Testing data found in streaming JSON response:', data.testing_data);
                window.mxchatTestPanelInstance.handleTestingData(data.testing_data);
            }
            
            // Handle the JSON response directly
            handleNonStreamResponse(data, callback);
            return Promise.resolve(); // Prevent further processing
        });
    }

        // Continue with streaming processing
        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let buffer = '';

        function processStream() {
            reader.read().then(({ done, value }) => {
                if (done) {
                    //console.log("Streaming completed, final content:", accumulatedContent);
                    
                    // If streaming completed but no content was received, try to get response as fallback
                    if (!streamingStarted || !accumulatedContent) {
                        //console.log("Stream completed with no content, checking for fallback data");
                        
                        // Try to read the response as JSON
                        responseClone.text().then(text => {
                            try {
                                const data = JSON.parse(text);
                                if (data.text || data.message) {
                                    handleNonStreamResponse(data, callback);
                                } else {
                                    // No valid data, fall back to regular call
                                    $('.bot-message.temporary-message').remove();
                                    callMxChat(message, callback);
                                }
                            } catch (e) {
                                // Could not parse, fall back to regular call
                                $('.bot-message.temporary-message').remove();
                                callMxChat(message, callback);
                            }
                        }).catch(() => {
                            $('.bot-message.temporary-message').remove();
                            callMxChat(message, callback);
                        });
                        return;
                    }
                    
                    if (callback) {
                        callback(accumulatedContent);
                    }
                    return;
                }

                buffer += decoder.decode(value, { stream: true });
                const lines = buffer.split('\n');
                buffer = lines.pop() || '';

                for (const line of lines) {
                    if (line.startsWith('data: ')) {
                        const data = line.substring(6);

                        if (data === '[DONE]') {
                            //console.log("Received [DONE] signal");
                            
                            if (!accumulatedContent) {
                                //console.log("No content received before [DONE]");
                                $('.bot-message.temporary-message').remove();
                                callMxChat(message, callback);
                                return;
                            }
                            
                            if (callback) {
                                callback(accumulatedContent);
                            }
                            return;
                        }

                        try {
                            const json = JSON.parse(data);
                            
                            // IMMEDIATE CHAT MODE UPDATE FOR STREAMING
                            if (json.chat_mode) {
                                //console.log("Stream: Updating chat mode immediately to:", json.chat_mode);
                                updateChatModeIndicator(json.chat_mode);
                            }
                            
                            // Handle testing data
                            if (json.testing_data && !testingDataReceived) {
                                //console.log('Testing data received in stream:', json.testing_data);
                                if (window.mxchatTestPanelInstance) {
                                    window.mxchatTestPanelInstance.handleTestingData(json.testing_data);
                                    testingDataReceived = true;
                                }
                            }
                            // Handle content streaming
                            else if (json.content) {
                                streamingStarted = true;
                                accumulatedContent += json.content;
                                updateStreamingMessage(accumulatedContent);
                            } 
                            // Handle complete response in stream (fallback response)
                            else if (json.text || json.message) {
                                //console.log("Received complete response in stream:", json);
                                handleNonStreamResponse(json, callback);
                                return;
                            }
                            // Handle errors
                            else if (json.error) {
                                console.error("Streaming error:", json.error);
                                
                                // Check if we have a fallback response
                                if (json.fallback || json.text || json.message) {
                                    handleNonStreamResponse(json, callback);
                                } else {
                                    $('.bot-message.temporary-message').remove();
                                    callMxChat(message, callback);
                                }
                                return;
                            }
                        } catch (e) {
                            console.error('Error parsing SSE data:', e, 'Data:', data);
                        }
                    }
                }

                processStream();
            }).catch(streamError => {
                console.error('Error reading stream:', streamError);
                $('.bot-message.temporary-message').remove();
                callMxChat(message, callback);
            });
        }

        processStream();
    })
        .catch(error => {
            //console.log('Streaming failed:', error);
            
            // Check if we have server error data with chat mode
            if (error && error.isServerError && error.data) {
                //console.log('Using server error response data');
                
                // Check for chat mode in error data
                if (error.data.chat_mode) {
                    //console.log("Error Response: Updating chat mode to:", error.data.chat_mode);
                    updateChatModeIndicator(error.data.chat_mode);
                }
                
                handleNonStreamResponse(error.data, callback);
            } else {
                // Only fall back to regular call if we don't have any response data
                //console.log('No response data available, falling back to regular call');
                $('.bot-message.temporary-message').remove();
                callMxChat(message, callback);
            }
        });
}

// Helper function to handle non-streaming responses (updated to include immediate chat mode handling)
function handleNonStreamResponse(data, callback) {
    //console.log("Handling non-stream response:", data);
    
    // IMMEDIATE CHAT MODE UPDATE FOR NON-STREAMING RESPONSES
    if (data.chat_mode) {
        //console.log("Non-Stream: Updating chat mode immediately to:", data.chat_mode);
        updateChatModeIndicator(data.chat_mode);
    }
    
    // Also check in data property if response is wrapped
    if (data.data && data.data.chat_mode) {
        //console.log("Non-Stream: Updating chat mode from data to:", data.data.chat_mode);
        updateChatModeIndicator(data.data.chat_mode);
    }
    
    // Remove temporary message
    $('.bot-message.temporary-message').remove();
    
    // Handle different response formats
    if (data.text || data.html || data.message) {
        
        // Apply response hooks
        if (data.text && typeof customMxChatFilter === 'function') {
            data.text = customMxChatFilter(data.text, "response");
        }
        if (data.message && typeof customMxChatFilter === 'function') {
            data.message = customMxChatFilter(data.message, "response");
        }
        
        // Display the response
        if (data.text && data.html) {
            replaceLastMessage("bot", data.text, data.html);
        } else if (data.text) {
            replaceLastMessage("bot", data.text);
        } else if (data.html) {
            replaceLastMessage("bot", "", data.html);
        } else if (data.message) {
            replaceLastMessage("bot", data.message);
        }
    }
    
    // Handle other response properties
    if (data.data && data.data.filename) {
        showActivePdf(data.data.filename);
        activePdfFile = data.data.filename;
    }
    
    if (data.redirect_url) {
        setTimeout(() => {
            window.location.href = data.redirect_url;
        }, 1500);
    }
    
    if (callback) {
        callback(data.text || data.message || '');
    }
}
// Enhanced updateChatModeIndicator function for immediate DOM updates
function updateChatModeIndicator(mode) {
    //console.log("updateChatModeIndicator called with mode:", mode);
    const indicator = document.getElementById('chat-mode-indicator');
    if (indicator) {
        const oldText = indicator.textContent;
        
        if (mode === 'agent') {
            indicator.textContent = 'Live Agent';
            startPolling();
            //console.log("Set indicator to Live Agent");
        } else {
            // Everything else is AI mode
            const customAiText = indicator.getAttribute('data-ai-text') || 'AI Agent';
            indicator.textContent = customAiText;
            stopPolling();
            //console.log("Set indicator to AI Agent:", customAiText);
        }
        
        // Force immediate DOM update and reflow
        if (oldText !== indicator.textContent) {
            // Force a reflow to ensure the change is visible immediately
            indicator.style.display = 'none';
            indicator.offsetHeight; // Trigger reflow
            indicator.style.display = '';
            
            // Double-check after a brief moment to ensure the change stuck
            setTimeout(() => {
                //console.log("Final indicator text:", indicator.textContent);
                if (mode === 'agent' && indicator.textContent !== 'Live Agent') {
                    console.warn("Mode indicator update failed, forcing update");
                    indicator.textContent = 'Live Agent';
                } else if (mode !== 'agent' && indicator.textContent === 'Live Agent') {
                    console.warn("Mode indicator stuck on Live Agent, forcing AI update");
                    const customAiText = indicator.getAttribute('data-ai-text') || 'AI Agent';
                    indicator.textContent = customAiText;
                }
            }, 50);
        }
    } else {
        console.error("Chat mode indicator element not found!");
    }
}

// Helper function to handle non-streaming responses (updated to include chat mode handling)
function handleNonStreamResponse(data, callback) {
    //console.log("Handling non-stream response:", data);
    
    // Remove temporary message
    $('.bot-message.temporary-message').remove();
    
    // Handle different response formats
    if (data.text || data.html || data.message) {
        
        // Apply response hooks
        if (data.text && typeof customMxChatFilter === 'function') {
            data.text = customMxChatFilter(data.text, "response");
        }
        if (data.message && typeof customMxChatFilter === 'function') {
            data.message = customMxChatFilter(data.message, "response");
        }
        
        //   Handle chat mode updates for streaming responses too
        handleChatModeUpdates(data, data.text || data.message);
        
        // Display the response
        if (data.text && data.html) {
            replaceLastMessage("bot", data.text, data.html);
        } else if (data.text) {
            replaceLastMessage("bot", data.text);
        } else if (data.html) {
            replaceLastMessage("bot", "", data.html);
        } else if (data.message) {
            replaceLastMessage("bot", data.message);
        }
    }
    
    // Handle other response properties
    if (data.data && data.data.filename) {
        showActivePdf(data.data.filename);
        activePdfFile = data.data.filename;
    }
    
    if (data.redirect_url) {
        setTimeout(() => {
            window.location.href = data.redirect_url;
        }, 1500);
    }
    
    if (callback) {
        callback(data.text || data.message || '');
    }
}

// Function to update message during streaming
function updateStreamingMessage(content) {
    // ADD RESPONSE HOOK FOR REAL-TIME STREAMING
    if (typeof customMxChatFilter === 'function') {
        content = customMxChatFilter(content, "response");
    }
    
    const formattedContent = linkify(content);

    // Find the temporary message
    const tempMessage = $('.bot-message.temporary-message').last();

    if (tempMessage.length) {
        // Update existing message
        tempMessage.html(formattedContent);
    } else {
        // Create new temporary message if it doesn't exist
        appendMessage("bot", content, '', [], true);
    }
}

function isStreamingSupported(model) {
    if (!model) return false;

    const modelPrefix = model.split('-')[0].toLowerCase();

    // Support streaming for OpenAI, Claude, Grok, DeepSeek, and OpenRouter models
    const isSupported = modelPrefix === 'gpt' || 
                        modelPrefix === 'o1' || 
                        modelPrefix === 'claude' || 
                        modelPrefix === 'grok' || 
                        modelPrefix === 'deepseek' || 
                        model === 'openrouter';  // Add this line - check full model name for OpenRouter
    
    return isSupported;
}

// Update the event handlers to use the correct function names
$('#send-button').off('click').on('click', function() {
    sendMessage(); // Use the updated sendMessage function
});

// Override enter key handler
$('#chat-input').off('keypress').on('keypress', function(e) {
    if (e.which == 13 && !e.shiftKey) {
        e.preventDefault();
        sendMessage(); // Use the updated sendMessage function
    }
});

    
function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
    try {
        // Determine styles based on sender type
        let messageClass, bgColor, fontColor;

        if (sender === "user") {
            messageClass = "user-message";
            bgColor = userMessageBgColor;
            fontColor = userMessageFontColor;
            // Only sanitize user input
            messageText = sanitizeUserInput(messageText);
        } else if (sender === "agent") {
            messageClass = "agent-message";
            bgColor = liveAgentMessageBgColor;
            fontColor = liveAgentMessageFontColor;
        } else {
            messageClass = "bot-message";
            bgColor = botMessageBgColor;
            fontColor = botMessageFontColor;
        }

        const messageDiv = $('<div>')
            .addClass(messageClass)
            .attr('dir', 'auto')
            .css({
                'background': bgColor,
                'color': fontColor,
                'margin-bottom': '1em'
            });

        // Process the message content based on sender
        let fullMessage;
        if (sender === "user") {
            // For user messages, apply linkify after sanitization
            fullMessage = linkify(messageText);
        } else {
            // For bot/agent messages, preserve HTML
            fullMessage = messageText;
        }

        // Add images if provided
        if (images && images.length > 0) {
            fullMessage += '<div class="image-gallery" dir="auto">';
            images.forEach(img => {
                const safeTitle = sanitizeUserInput(img.title);
                const safeUrl = encodeURI(img.image_url);
                const safeThumbnail = encodeURI(img.thumbnail_url);
                
                fullMessage += `
                    <div style="margin-bottom: 10px;">
                        <strong>${safeTitle}</strong><br>
                        <a href="${safeUrl}" target="_blank">
                            <img src="${safeThumbnail}" alt="${safeTitle}" style="max-width: 100px; height: auto; margin: 5px;" />
                        </a>
                    </div>`;
            });
            fullMessage += '</div>';
        }

        // Append HTML content if provided
        if (messageHtml && sender !== "user") {
            fullMessage += '<br><br>' + messageHtml;
        }

        messageDiv.html(fullMessage);

        if (isTemporary) {
            messageDiv.addClass('temporary-message');
        }

        messageDiv.hide().appendTo('#chat-box').fadeIn(300, function() {
            // FIXED: Use event delegation for link tracking
            if (sender === "bot" || sender === "agent") {
                attachLinkTracking(messageDiv, messageText);
            }
            
            if (sender === "bot") {
                const lastUserMessage = $('#chat-box').find('.user-message').last();
                if (lastUserMessage.length) {
                    scrollElementToTop(lastUserMessage);
                }
            }
        });

        if (messageText.id) {
            lastSeenMessageId = messageText.id;
            hideNotification();
        }
    } catch (error) {
        console.error("Error rendering message:", error);
    }
}

//   Helper function to attach link tracking with proper event handling
function attachLinkTracking(messageDiv, messageText) {
    // Use a slight delay to ensure DOM is ready
    setTimeout(function() {
        const links = messageDiv.find('a[href]').not('[data-tracked]');
        
        links.each(function() {
            const $link = $(this);
            const originalHref = $link.attr('href');
            
            // Mark as tracked to avoid duplicate handlers
            $link.attr('data-tracked', 'true');
            
            // Only track external URLs
            if (originalHref && (originalHref.startsWith('http://') || originalHref.startsWith('https://'))) {
                // Remove any existing click handlers first
                $link.off('click.tracking');
                
                // Add new click handler with namespace
                $link.on('click.tracking', function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    
                    const messageContext = typeof messageText === 'string' 
                        ? messageText.substring(0, 200) 
                        : '';
                    
                    // Track the click
                    $.ajax({
                        url: mxchatChat.ajax_url,
                        type: 'POST',
                        data: {
                            action: 'mxchat_track_url_click',
                            session_id: getChatSession(),
                            url: originalHref,
                            message_context: messageContext,
                            nonce: mxchatChat.nonce
                        },
                        complete: function() {
                            // Always redirect, even if tracking fails
                            if ($link.attr('target') === '_blank' || linkTarget === '_blank') {
                                window.open(originalHref, '_blank');
                            } else {
                                window.location.href = originalHref;
                            }
                        }
                    });
                    
                    return false; // Extra insurance to prevent default
                });
            }
        });
    }, 100); // Small delay to ensure DOM is ready
}

function replaceLastMessage(sender, responseText, responseHtml = '', images = []) {
    var messageClass = sender === "user" ? "user-message" : sender === "agent" ? "agent-message" : "bot-message";
    var lastMessageDiv = $('#chat-box').find('.bot-message.temporary-message, .agent-message.temporary-message').last();

    // Determine styles
    let bgColor, fontColor;
    if (sender === "user") {
        bgColor = userMessageBgColor;
        fontColor = userMessageFontColor;
    } else if (sender === "agent") {
        bgColor = liveAgentMessageBgColor;
        fontColor = liveAgentMessageFontColor;
    } else {
        bgColor = botMessageBgColor;
        fontColor = botMessageFontColor;
    }

    var fullMessage = linkify(responseText);
    if (responseHtml) {
        fullMessage += '<br><br>' + responseHtml;
    }

    if (images.length > 0) {
        fullMessage += '<div class="image-gallery" dir="auto">';
        images.forEach(img => {
            fullMessage += `
                <div style="margin-bottom: 10px;">
                    <strong>${img.title}</strong><br>
                    <a href="${img.image_url}" target="_blank">
                        <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
                    </a>
                </div>`;
        });
        fullMessage += '</div>';
    }

    if (lastMessageDiv.length) {
        lastMessageDiv.fadeOut(200, function() {
            $(this)
                .html(fullMessage)
                .removeClass('bot-message user-message')
                .addClass(messageClass)
                .attr('dir', 'auto')
                .css({
                    'background-color': bgColor,
                    'color': fontColor,
                })
                .removeClass('temporary-message')
                .fadeIn(200, function() {
                    // FIXED: Use the helper function for link tracking
                    if (sender === "bot" || sender === "agent") {
                        attachLinkTracking($(this), responseText);
                    }
                    
                    if (sender === "bot" || sender === "agent") {
                        const lastUserMessage = $('#chat-box').find('.user-message').last();
                        if (lastUserMessage.length) {
                            scrollElementToTop(lastUserMessage);
                        }
                        // Show notification if chat is hidden
                        if ($('#floating-chatbot').hasClass('hidden')) {
                            showNotification();
                        }
                    }
                });
        });
    } else {
        appendMessage(sender, responseText, responseHtml, images);
    }
}


    function appendThinkingMessage() {
        // Remove any existing thinking dots first
        $('.thinking-dots').remove();

        // Retrieve the bot message font color and background color
        var botMessageFontColor = mxchatChat.bot_message_font_color;
        var botMessageBgColor = mxchatChat.bot_message_bg_color;


        var thinkingHtml = '<div class="thinking-dots-container">' +
                           '<div class="thinking-dots">' +
                           '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
                           '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
                           '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
                           '</div>' +
                           '</div>';

        // Append the thinking dots to the chat container (or within the temporary message div)
        $("#chat-box").append('<div class="bot-message temporary-message" style="background-color: ' + botMessageBgColor + ';">' + thinkingHtml + '</div>');
        scrollToBottom();
    }
    
    function removeThinkingDots() {
        $('.thinking-dots').closest('.temporary-message').remove();
    }

    // ====================================
    // TEXT FORMATTING & PROCESSING
    // ====================================
    
    function linkify(inputText) {
    //console.log('=== LINKIFY DEBUG START ===');
    //console.log('Input to linkify:', inputText);
    //console.log('Input type:', typeof inputText);
    //console.log('Input length:', inputText ? inputText.length : 'null/undefined');
    
    if (!inputText) {
        //console.log('Empty input, returning empty string');
        return '';
    }
    
    // Process markdown headers FIRST (before paragraph wrapping)
    //console.log('--- Processing Headers ---');
    let processedText = formatMarkdownHeaders(inputText);
    //console.log('After headers:', processedText);
    //console.log('Headers changed:', processedText !== inputText);
    
    // Process text styling (bold, italic, strikethrough)
    //console.log('--- Processing Text Styling ---');
    //const beforeStyling = processedText;
    processedText = formatTextStyling(processedText);
    //console.log('After styling:', processedText);
    //console.log('Styling changed:', processedText !== beforeStyling);
    
    // Process code blocks BEFORE processing links to avoid conflicts
    //console.log('--- Processing Code Blocks ---');
    const beforeCodeBlocks = processedText;
    processedText = formatCodeBlocks(processedText);
    //console.log('After code blocks:', processedText);
    //console.log('Code blocks changed:', processedText !== beforeCodeBlocks);
    
    // NOW convert to paragraphs (this should be AFTER markdown processing)
    //console.log('--- Converting to Paragraphs ---');
    const beforeParagraphs = processedText;
    processedText = convertNewlinesToBreaks(processedText);
    //console.log('After paragraph conversion:', processedText);
    //console.log('Paragraphs changed:', processedText !== beforeParagraphs);
    
    // Process markdown links
    //console.log('--- Processing Markdown Links ---');
    const beforeMarkdownLinks = processedText;
    const markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g;
    processedText = processedText.replace(markdownLinkPattern, (match, text, url) => {
        //console.log('Found markdown link:', match, 'text:', text, 'url:', url);
        const safeUrl = encodeURI(url);
        const safeText = sanitizeUserInput(text);
        return `<a href="${safeUrl}" target="${linkTarget}">${safeText}</a>`;
    });
    //console.log('After markdown links:', processedText);
    //console.log('Markdown links changed:', processedText !== beforeMarkdownLinks);

    // Process phone numbers (tel:)
    //console.log('--- Processing Phone Numbers ---');
    const beforePhone = processedText;
    const phonePattern = /\[([^\]]+)\]\((tel:[\d+\-\s()]+)\)/g;
    processedText = processedText.replace(phonePattern, (match, text, phone) => {
        //console.log('Found phone link:', match, 'text:', text, 'phone:', phone);
        const safePhone = encodeURI(phone);
        const safeText = sanitizeUserInput(text);
        return `<a href="${safePhone}">${safeText}</a>`;
    });
    //console.log('After phone numbers:', processedText);
    //console.log('Phone numbers changed:', processedText !== beforePhone);

    // Process mailto links
    //console.log('--- Processing Mailto Links ---');
    const beforeMailto = processedText;
    const mailtoPattern = /\[([^\]]+)\]\((mailto:[^\)]+)\)/g;
    processedText = processedText.replace(mailtoPattern, (match, text, mailto) => {
        console.log('Found mailto link:', match, 'text:', text, 'mailto:', mailto);
        const safeMailto = encodeURI(mailto);
        const safeText = sanitizeUserInput(text);
        return `<a href="${safeMailto}">${safeText}</a>`;
    });
    //console.log('After mailto links:', processedText);
    //console.log('Mailto links changed:', processedText !== beforeMailto);

    // Process standalone URLs (avoid already processed links)
   //console.log('--- Processing Standalone URLs ---');
    const beforeStandaloneUrls = processedText;
    const urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)(?![^<]*<\/a>)/gim;
    processedText = processedText.replace(urlPattern, (match, prefix, url) => {
        //console.log('Found standalone URL:', match, 'prefix:', prefix, 'url:', url);
        const safeUrl = encodeURI(url);
        return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`;
    });
    //console.log('After standalone URLs:', processedText);
    //console.log('Standalone URLs changed:', processedText !== beforeStandaloneUrls);
    
    // Process www. URLs (avoid already processed links)
    //console.log('--- Processing WWW URLs ---');
    const beforeWwwUrls = processedText;
    const wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
    processedText = processedText.replace(wwwPattern, (match, prefix, url) => {
        //console.log('Found www URL:', match, 'prefix:', prefix, 'url:', url);
        const safeUrl = encodeURI(`http://${url}`);
        return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`;
    });
    //console.log('After www URLs:', processedText);
    //console.log('WWW URLs changed:', processedText !== beforeWwwUrls);

    //console.log('=== FINAL RESULT ===');
    //console.log('Final processed text:', processedText);
    //console.log('Total transformation:', inputText !== processedText);
    //console.log('=== LINKIFY DEBUG END ===');
    
    return processedText;
}
     
    function formatMarkdownHeaders(text) {
        // Handle h1 to h6 headers
        return text.replace(/^(#{1,6})\s+(.+)$/gm, function(match, hashes, content) {
            const level = hashes.length;
            return `<h${level} class="chat-heading chat-heading-${level}">${content.trim()}</h${level}>`;
        });
    }
    
    function formatTextStyling(text) {
        // Handle bold text (**text**)
        text = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
        
        // Handle italic text (*text* or _text_) - avoid conflicts with bold
        text = text.replace(/(?<!\*)\*([^*\n]+)\*(?!\*)/g, '<em>$1</em>');
        text = text.replace(/(?<!_)_([^_\n]+)_(?!_)/g, '<em>$1</em>');
        
        // Handle strikethrough (~~text~~)
        text = text.replace(/~~(.*?)~~/g, '<del>$1</del>');
        
        return text;
    }
    
    function formatBoldText(text) {
        // This function is kept for compatibility but now uses formatTextStyling
        return formatTextStyling(text);
    }
    
function convertNewlinesToBreaks(text) {
    // Split the text into paragraphs (marked by double newlines or multiple <br> tags)
    const paragraphs = text.split(/(?:\n\n|\<br\>\s*\<br\>)/g);
    
    // Filter out empty paragraphs and wrap each paragraph in <p> tags
    return paragraphs
        .map(para => para.trim())
        .filter(para => para.length > 0) // Remove empty paragraphs
        .map(para => `<p>${para}</p>`)
        .join('');
}
    function formatCodeBlocks(text) {
        // Handle fenced code blocks with language specification (```language)
        text = text.replace(/```(\w+)?\n?([\s\S]*?)```/g, (match, language, code) => {
            const lang = language || 'text';
            const escapedCode = escapeHtml(code.trim());
            return `<div class="mxchat-code-block-container">
                <div class="mxchat-code-header">
                    <span class="mxchat-code-language">${lang}</span>
                    <button class="mxchat-copy-button" aria-label="Copy to clipboard">Copy</button>
                </div>
                <pre class="mxchat-code-block"><code class="language-${lang}">${escapedCode}</code></pre>
            </div>`;
        });

        // Handle inline code with single backticks
        text = text.replace(/`([^`\n]+)`/g, '<code class="mxchat-inline-code">$1</code>');

        // Handle raw PHP tags (legacy support)
        text = text.replace(/(<\?php[\s\S]*?\?>)/g, (match) => {
            const escapedCode = escapeHtml(match);
            return `<div class="mxchat-code-block-container">
                <div class="mxchat-code-header">
                    <span class="mxchat-code-language">php</span>
                    <button class="mxchat-copy-button" aria-label="Copy to clipboard">Copy</button>
                </div>
                <pre class="mxchat-code-block"><code class="language-php">${escapedCode}</code></pre>
            </div>`;
        });

        return text;
    }
    
    function sanitizeUserInput(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    function escapeHtml(unsafe) {
        // Skip escaping if it's already escaped or contains HTML code block markup
        if (unsafe.includes('&lt;') || unsafe.includes('&gt;') || 
            unsafe.includes('<pre><code') || unsafe.includes('</code></pre>')) {
            return unsafe;
        }
        
        return unsafe
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&#039;");
    }
    
    function decodeHTMLEntities(text) {
        var textArea = document.createElement('textarea');
        textArea.innerHTML = text;
        return textArea.value;
    }

    // ====================================
    // UI & SCROLLING CONTROLS
    // ====================================
    
    function scrollToBottom(instant = false) {
        var chatBox = $('#chat-box');
        if (instant) {
            // Instantly set the scroll position to the bottom
            chatBox.scrollTop(chatBox.prop("scrollHeight"));
        } else {
            // Use requestAnimationFrame for smoother scrolling if needed
            let start = null;
            const scrollHeight = chatBox.prop("scrollHeight");
            const initialScroll = chatBox.scrollTop();
            const distance = scrollHeight - initialScroll;
            const duration = 500; // Duration in ms
    
            function smoothScroll(timestamp) {
                if (!start) start = timestamp;
                const progress = timestamp - start;
                const currentScroll = initialScroll + (distance * (progress / duration));
                chatBox.scrollTop(currentScroll);
    
                if (progress < duration) {
                    requestAnimationFrame(smoothScroll);
                } else {
                    chatBox.scrollTop(scrollHeight); // Ensure it's exactly at the bottom
                }
            }
    
            requestAnimationFrame(smoothScroll);
        }
    }
    
    function scrollElementToTop(element) {
        var chatBox = $('#chat-box');
        var elementTop = element.position().top + chatBox.scrollTop();
        chatBox.animate({ scrollTop: elementTop }, 500);
    }
    
    function showChatWidget() {
        // First ensure display is set
        $('#floating-chatbot-button').css('display', 'flex');
        // Then handle the fade
        $('#floating-chatbot-button').fadeTo(500, 1);
        // Force visibility
        $('#floating-chatbot-button').removeClass('hidden');
        //console.log('Showing widget');
    }
    
    function hideChatWidget() {
        $('#floating-chatbot-button').css('display', 'none');
        $('#floating-chatbot-button').addClass('hidden');
        //console.log('Hiding widget');
    }
    
    function disableScroll() {
        if (isMobile()) {
            $('body').css('overflow', 'hidden');
        }
    }
    
    function enableScroll() {
        if (isMobile()) {
            $('body').css('overflow', '');
        }
    }
    
    function isMobile() {
        // This can be a simple check, or more sophisticated detection of mobile devices
        return window.innerWidth <= 768; // Example threshold for mobile devices
    }
    
    function setFullHeight() {
        var vh = $(window).innerHeight() * 0.01;
        $(':root').css('--vh', vh + 'px');
    }


    // ====================================
    // NOTIFICATION SYSTEM
    // ====================================
    
    function createNotificationBadge() {
        //console.log("Creating notification badge...");
        const chatButton = document.getElementById('floating-chatbot-button');
        //console.log("Chat button found:", !!chatButton);
        
        if (!chatButton) return;
    
        // Remove any existing badge first
        const existingBadge = chatButton.querySelector('.chat-notification-badge');
        if (existingBadge) {
            //console.log("Removing existing badge");
            existingBadge.remove();
        }
    
        notificationBadge = document.createElement('div');
        notificationBadge.className = 'chat-notification-badge';
        notificationBadge.style.cssText = `
            display: none;
            position: absolute;
            top: -5px;
            right: -5px;
            background-color: red;
            color: white;
            border-radius: 50%;
            padding: 4px 8px;
            font-size: 12px;
            font-weight: bold;
            z-index: 10001;
        `;
        chatButton.style.position = 'relative';
        chatButton.appendChild(notificationBadge);
        
    }
    
    function showNotification() {
        const badge = document.getElementById('chat-notification-badge');
        if (badge && $('#floating-chatbot').hasClass('hidden')) {
            badge.style.display = 'block';
            badge.textContent = '1';
        }
    }
    
    function hideNotification() {
        const badge = document.getElementById('chat-notification-badge');
        if (badge) {
            badge.style.display = 'none';
        }
    }
    
    function startNotificationChecking() {
        const chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
        if (!chatPersistenceEnabled) return;
    
        createNotificationBadge();
        notificationCheckInterval = setInterval(checkForNewMessages, 30000); // Check every 30 seconds
    }
    
    function stopNotificationChecking() {
        if (notificationCheckInterval) {
            clearInterval(notificationCheckInterval);
        }
    }
    
    function checkForNewMessages() {
        const sessionId = getChatSession();
        const chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
        
        if (!chatPersistenceEnabled) return;
    
        $.ajax({
            url: mxchatChat.ajax_url,
            type: 'POST',
            data: {
                action: 'mxchat_check_new_messages',
                session_id: sessionId,
                last_seen_id: lastSeenMessageId,
                nonce: mxchatChat.nonce
            },
            success: function(response) {
                if (response.success && response.data.hasNewMessages) {
                    showNotification();
                }
            }
        });
    }


    // ====================================
    // LIVE AGENT FUNCTIONALITY
    // ====================================
    
function updateChatModeIndicator(mode) {
    //console.log("updateChatModeIndicator called with mode:", mode);
    const indicator = document.getElementById('chat-mode-indicator');
    if (indicator) {
        const oldText = indicator.textContent;
        
        if (mode === 'agent') {
            indicator.textContent = 'Live Agent';
            startPolling();
            //console.log("Set indicator to Live Agent");
        } else {
            // Everything else is AI mode
            const customAiText = indicator.getAttribute('data-ai-text') || 'AI Agent';
            indicator.textContent = customAiText;
            stopPolling();
            //console.log("Set indicator to AI Agent:", customAiText);
        }
        
        // Force immediate DOM update
        if (oldText !== indicator.textContent) {
            // Force a reflow to ensure the change is visible immediately
            indicator.offsetHeight;
            
            // Double-check after a brief moment
            setTimeout(() => {
                //console.log("Final indicator text:", indicator.textContent);
            }, 50);
        }
    } else {
        console.error("Chat mode indicator element not found!");
    }
}

    function startPolling() {
        // Clear any existing interval first
        stopPolling();
        // Start new polling interval
        pollingInterval = setInterval(checkForAgentMessages, 5000);
        //console.log("Started agent message polling");
    }
    
    function stopPolling() {
        if (pollingInterval) {
            clearInterval(pollingInterval);
            pollingInterval = null;
            //console.log("Stopped agent message polling");
        }
    }
    
function checkForAgentMessages() {
    const sessionId = getChatSession();
    $.ajax({
        url: mxchatChat.ajax_url,
        type: 'POST',
        dataType: 'json',
        data: {
            action: 'mxchat_fetch_new_messages',
            session_id: sessionId,
            last_seen_id: lastSeenMessageId,
            persistence_enabled: 'true', // Add this too
            nonce: mxchatChat.nonce
        },
        success: function (response) {
            if (response.success && response.data?.new_messages) {
                let hasNewMessage = false;
                
                response.data.new_messages.forEach(function (message) {
                    if (message.role === "agent" && !processedMessageIds.has(message.id)) {
                        hasNewMessage = true;
                        // CHANGE THIS LINE:
                        appendMessage("agent", message.content); // Instead of replaceLastMessage
                        lastSeenMessageId = message.id;
                        processedMessageIds.add(message.id);
                    }
                });

                if (hasNewMessage && $('#floating-chatbot').hasClass('hidden')) {
                    showNotification();
                }
                
                scrollToBottom(true);
            }
        },
        error: function (xhr, status, error) {
            //console.error("Polling error:", xhr, status, error);
        }
    });
}

    // ====================================
    // CHAT HISTORY & PERSISTENCE
    // ====================================
    
    function loadChatHistory() {
    var sessionId = getChatSession();
    var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';

    if (chatPersistenceEnabled && sessionId) {
        $.ajax({
            url: mxchatChat.ajax_url,
            type: 'POST',
            dataType: 'json',
            data: {
                action: 'mxchat_fetch_conversation_history',
                session_id: sessionId
            },
            success: function(response) {
                // Check if the response indicates success
                if (response.success) {
                    // Handle case where conversation data exists and is an array
                    if (response.data && Array.isArray(response.data.conversation)) {
                        var $chatBox = $('#chat-box');
                        var $fragment = $(document.createDocumentFragment());
                        let highestMessageId = lastSeenMessageId;

                        // Update chat mode if provided
                        if (response.data.chat_mode) {
                            updateChatModeIndicator(response.data.chat_mode);
                        }

                        // Only process if there are actual messages
                        if (response.data.conversation.length > 0) {
                            $.each(response.data.conversation, function(index, message) {
                                // Skip agent messages if persistence is off
                                if (!chatPersistenceEnabled && message.role === 'agent') {
                                    return;
                                }

                                var messageClass, messageBgColor, messageFontColor;

                                switch (message.role) {
                                    case 'user':
                                        messageClass = 'user-message';
                                        messageBgColor = userMessageBgColor;
                                        messageFontColor = userMessageFontColor;
                                        break;
                                    case 'agent':
                                        messageClass = 'agent-message';
                                        messageBgColor = liveAgentMessageBgColor;
                                        messageFontColor = liveAgentMessageFontColor;
                                        break;
                                    default:
                                        messageClass = 'bot-message';
                                        messageBgColor = botMessageBgColor;
                                        messageFontColor = botMessageFontColor;
                                        break;
                                }

                                var messageElement = $('<div>').addClass(messageClass)
                                    .css({
                                        'background': messageBgColor,
                                        'color': messageFontColor
                                    });

                                var content = message.content;
                                content = content.replace(/\\'/g, "'").replace(/\\"/g, '"');
                                content = decodeHTMLEntities(content);

                                if (content.includes("mxchat-product-card") || content.includes("mxchat-image-gallery")) {
                                    messageElement.html(content);
                                } else {
                                    var formattedContent = linkify(content);
                                    messageElement.html(formattedContent);
                                }

                                $fragment.append(messageElement);

                                // Track message IDs
                                if (message.id) {
                                    highestMessageId = Math.max(highestMessageId, message.id);
                                    processedMessageIds.add(message.id);
                                }
                            });

                            // Only append messages and scroll if we have content
                            $chatBox.append($fragment);
                            scrollToBottom(true);

                            // Collapse quick questions if we have conversation history
                            if (hasQuickQuestions()) {
                                collapseQuickQuestions();
                            }

                            // Update lastSeenMessageId after history loads
                            lastSeenMessageId = highestMessageId;

                            // Only update chat mode if persistence is enabled and we have messages
                            if (chatPersistenceEnabled) {
                                var lastMessage = response.data.conversation[response.data.conversation.length - 1];
                                if (lastMessage.role === 'agent') {
                                    updateChatModeIndicator('agent');
                                }
                            }
                        } else {
                            // No conversation history - this is normal for new chats
                            //console.log("No conversation history found for this session - starting fresh chat.");
                        }
                    } else {
                        // Response successful but no conversation data - this is also normal
                        //console.log("No conversation data in response - starting fresh chat.");
                    }
                } else {
                    // Only show error if the response explicitly indicates an error
                    console.warn("Failed to load chat history:", response.message || "Unknown error");
                    // Don't show error message to user for failed history loads
                    // Just start with a fresh chat instead
                }
            },
            error: function(xhr, status, error) {
                // Only log AJAX errors, don't show them to the user
                console.error("AJAX error loading chat history:", status, error);
                // Start with fresh chat - don't show error to user
            }
        });
    } else {
        //console.log("Chat persistence is disabled or no session ID found. Starting fresh chat.");
    }
}


    // ====================================
    // FILE UPLOAD FUNCTIONALITY
    // ====================================
    
    function addSafeEventListener(elementId, eventType, handler) {
        const element = document.getElementById(elementId);
        if (element) {
            element.addEventListener(eventType, handler);
        }
    }
    
    function showActivePdf(filename) {
        const container = document.getElementById('active-pdf-container');
        const nameElement = document.getElementById('active-pdf-name');
        
        if (!container || !nameElement) {
            //console.error('PDF container elements not found');
            return;
        }
    
        nameElement.textContent = filename;
        container.style.display = 'flex';
    }
    
    function showActiveWord(filename) {
        const container = document.getElementById('active-word-container');
        const nameElement = document.getElementById('active-word-name');
        
        if (!container || !nameElement) {
            //console.error('Word document container elements not found');
            return;
        }
    
        nameElement.textContent = filename;
        container.style.display = 'flex';
    }
    
    function removeActivePdf() {
        const container = document.getElementById('active-pdf-container');
        const nameElement = document.getElementById('active-pdf-name');
        
        if (!container || !nameElement || !activePdfFile) return;
    
        fetch(mxchatChat.ajax_url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                'action': 'mxchat_remove_pdf',
                'session_id': sessionId,
                'nonce': mxchatChat.nonce
            })
        })
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                container.style.display = 'none';
                nameElement.textContent = '';
                activePdfFile = null;
                appendMessage('bot', 'PDF removed.');
            }
        })
        .catch(error => {
            //console.error('Error removing PDF:', error);
        });
    }
    
    function removeActiveWord() {
        const container = document.getElementById('active-word-container');
        const nameElement = document.getElementById('active-word-name');
        
        if (!container || !nameElement || !activeWordFile) return;
    
        fetch(mxchatChat.ajax_url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                'action': 'mxchat_remove_word',
                'session_id': sessionId,
                'nonce': mxchatChat.nonce
            })
        })
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                container.style.display = 'none';
                nameElement.textContent = '';
                activeWordFile = null;
                appendMessage('bot', 'Word document removed.');
            }
        })
        .catch(error => {
            //console.error('Error removing Word document:', error);
        });
    }
    
    // ====================================
    // CONSENT & COMPLIANCE (GDPR)
    // ====================================
    
    function initializeChatVisibility() {
        //console.log('Initializing chat visibility');
        const complianzEnabled = mxchatChat.complianz_toggle === 'on' || 
                                mxchatChat.complianz_toggle === '1' || 
                                mxchatChat.complianz_toggle === 1;
    
        if (complianzEnabled && typeof cmplz_has_consent === "function" && typeof complianz !== 'undefined') {
            // Initial check
            checkConsentAndShowChat();
    
            // Listen for consent changes
            $(document).on('cmplz_status_change', function(event) {
                //console.log('Status change detected');
                checkConsentAndShowChat();
            });
        } else {
            // If Complianz is not enabled, always show
            $('#floating-chatbot-button')
                .css('display', 'flex')
                .removeClass('hidden no-consent')
                .fadeTo(500, 1);
                
            // Also check pre-chat message when Complianz is not enabled
            checkPreChatDismissal();
        }
    }

    
    function checkConsentAndShowChat() {
        var consentStatus = cmplz_has_consent('marketing');
        var consentType = complianz.consenttype;
        
        //console.log('Checking consent:', {status: consentStatus,type: consentType});
    
        let $widget = $('#floating-chatbot-button');
        let $chatbot = $('#floating-chatbot');
        let $preChat = $('#pre-chat-message');
        
        if (consentStatus === true) {
            //console.log('Consent granted - showing widget');
            $widget
                .removeClass('no-consent')
                .css('display', 'flex')
                .removeClass('hidden')
                .fadeTo(500, 1);
            $chatbot.removeClass('no-consent');
            
            // Show pre-chat message if not dismissed
            checkPreChatDismissal();
        } else {
            //console.log('No consent - hiding widget');
            $widget
                .addClass('no-consent')
                .fadeTo(500, 0, function() {
                    $(this)
                        .css('display', 'none')
                        .addClass('hidden');
                });
            $chatbot.addClass('no-consent');
            
            // Hide pre-chat message when no consent
            $preChat.hide();
        }
    }


    // ====================================
    // PRE-CHAT MESSAGE HANDLING
    // ====================================
    
    function checkPreChatDismissal() {
        $.ajax({
            url: mxchatChat.ajax_url,
            type: 'POST',
            data: {
                action: 'mxchat_check_pre_chat_message_status',
                _ajax_nonce: mxchatChat.nonce
            },
            success: function(response) {
                if (response.success && !response.data.dismissed) {
                    $('#pre-chat-message').fadeIn(250);
                } else {
                    $('#pre-chat-message').hide();
                }
            },
            error: function() {
                //console.error('Failed to check pre-chat message dismissal status.');
            }
        });
    }
    
    function handlePreChatDismissal() {
        $('#pre-chat-message').fadeOut(200);
        $.ajax({
            url: mxchatChat.ajax_url,
            type: 'POST',
            data: {
                action: 'mxchat_dismiss_pre_chat_message',
                _ajax_nonce: mxchatChat.nonce
            },
            success: function() {
                $('#pre-chat-message').hide();
            },
            error: function() {
                //console.error('Failed to dismiss pre-chat message.');
            }
        });
    }


    // ====================================
    // UTILITY FUNCTIONS
    // ====================================
    
    function copyToClipboard(text) {
        var tempInput = $('<input>');
        $('body').append(tempInput);
        tempInput.val(text).select();
        document.execCommand('copy');
        tempInput.remove();
    }

    
    function isImageHtml(str) {
        return str.startsWith('<img') && str.endsWith('>');
    }


    // ====================================
    // EVENT HANDLERS & INITIALIZATION
    // ====================================

$(document).on('click', '.mxchat-popular-question', function () {
    var question = $(this).text();
    
    // Append the question as if the user typed it
    appendMessage("user", question);
    
    // Only collapse if there are questions
    if (hasQuickQuestions()) {
        collapseQuickQuestions();
    }
    
    // Send the question to the server
    sendMessageToChatbot(question);
});

$(document).on('click', '.questions-toggle-btn', function(e) {
    e.preventDefault();
    e.stopPropagation();
    expandQuickQuestions();
});

$(document).on('click', '.questions-collapse-btn', function(e) {
    e.preventDefault();
    e.stopPropagation();
    collapseQuickQuestions();
});
    
    // Chatbot visibility toggle handlers
    $(document).on('click', '#floating-chatbot-button', function() {
        var chatbot = $('#floating-chatbot');
        if (chatbot.hasClass('hidden')) {
            chatbot.removeClass('hidden').addClass('visible');
            $(this).addClass('hidden');
            $('#chat-notification-badge').hide(); // Hide notification when opening chat
            disableScroll();
            $('#pre-chat-message').fadeOut(250);
        } else {
            chatbot.removeClass('visible').addClass('hidden');
            $(this).removeClass('hidden');
            enableScroll();
            checkPreChatDismissal();
        }
    });
    
    $(document).on('click', '#exit-chat-button', function() {
        $('#floating-chatbot').addClass('hidden').removeClass('visible');
        $('#floating-chatbot-button').removeClass('hidden');
        enableScroll();
    });
    
    $(document).on('click', '.close-pre-chat-message', function(e) {
        e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
        $('#pre-chat-message').fadeOut(200, function() {
            $(this).remove();
        });
    });
    

    // PDF upload button handlers
    if (document.getElementById('pdf-upload-btn')) {
        document.getElementById('pdf-upload-btn').addEventListener('click', function() {
            document.getElementById('pdf-upload').click();
        });
    }
    
    // Word upload button handlers
    if (document.getElementById('word-upload-btn')) {
        document.getElementById('word-upload-btn').addEventListener('click', function() {
            document.getElementById('word-upload').click();
        });
    }
    
    // PDF file input change handler
    addSafeEventListener('pdf-upload', 'change', async function(e) {
        const file = e.target.files[0];
    
        if (!file || file.type !== 'application/pdf') {
            alert('Please select a valid PDF file.');
            return;
        }
    
        if (!sessionId) {
            //console.error('No session ID found');
            alert('Error: No session ID found');
            return;
        }
    
        if (!mxchatChat || !mxchatChat.ajax_url || !mxchatChat.nonce) {
            //console.error('mxchatChat not properly configured:', mxchatChat);
            alert('Error: Ajax configuration missing');
            return;
        }
    
        // Disable buttons and show loading state
        const uploadBtn = document.getElementById('pdf-upload-btn');
        const sendBtn = document.getElementById('send-button');
        const originalBtnContent = uploadBtn.innerHTML;
    
        try {
            const formData = new FormData();
            formData.append('action', 'mxchat_upload_pdf');
            formData.append('pdf_file', file);
            formData.append('session_id', sessionId);
            formData.append('nonce', mxchatChat.nonce);
    
            uploadBtn.disabled = true;
            sendBtn.disabled = true;
            uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
                <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
            </svg>`;
    
            const response = await fetch(mxchatChat.ajax_url, {
                method: 'POST',
                body: formData
            });
    
            const data = await response.json();
    
            if (data.success) {
                // Hide popular questions if they exist
                const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
                if (hasQuickQuestions()) {
                    collapseQuickQuestions();
                }
                
                // Show the active PDF name
                showActivePdf(data.data.filename);
                
                appendMessage('bot', data.data.message);
                scrollToBottom();
                activePdfFile = data.data.filename;
            } else {
                //console.error('Upload failed:', data.data);
                alert('Failed to upload PDF. Please try again.');
            }
        } catch (error) {
            //console.error('Upload error:', error);
            alert('Error uploading file. Please try again.');
        } finally {
            uploadBtn.disabled = false;
            sendBtn.disabled = false;
            uploadBtn.innerHTML = originalBtnContent;
            this.value = ''; // Reset file input
        }
    });
    
    // Word file input change handler
    addSafeEventListener('word-upload', 'change', async function(e) {
        const file = e.target.files[0];
    
        if (!file || file.type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
            alert('Please select a valid Word document (.docx).');
            return;
        }
    
        if (!sessionId) {
            //console.error('No session ID found');
            alert('Error: No session ID found');
            return;
        }
    
        // Disable buttons and show loading state
        const uploadBtn = document.getElementById('word-upload-btn');
        const sendBtn = document.getElementById('send-button');
        const originalBtnContent = uploadBtn.innerHTML;
    
        try {
            const formData = new FormData();
            formData.append('action', 'mxchat_upload_word');
            formData.append('word_file', file);
            formData.append('session_id', sessionId);
            formData.append('nonce', mxchatChat.nonce);
    
            uploadBtn.disabled = true;
            sendBtn.disabled = true;
            uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
                <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
            </svg>`;
    
            const response = await fetch(mxchatChat.ajax_url, {
                method: 'POST',
                body: formData
            });
    
            const data = await response.json();
    
            if (data.success) {
                // Hide popular questions if they exist
                const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
                if (hasQuickQuestions()) {
                    collapseQuickQuestions();
                }
    
                // Show the active Word document name
                showActiveWord(data.data.filename);
                
                appendMessage('bot', data.data.message);
                scrollToBottom();
                activeWordFile = data.data.filename;
            } else {
                //console.error('Upload failed:', data.data);
                alert('Failed to upload Word document. Please try again.');
            }
        } catch (error) {
            //console.error('Upload error:', error);
            alert('Error uploading file. Please try again.');
        } finally {
            uploadBtn.disabled = false;
            sendBtn.disabled = false;
            uploadBtn.innerHTML = originalBtnContent;
            this.value = ''; // Reset file input
        }
    });
    
    // Remove button click handlers
    document.getElementById('remove-pdf-btn')?.addEventListener('click', function(e) {
        e.preventDefault();
        e.stopPropagation();
        removeActivePdf();
    });
    
    document.getElementById('remove-word-btn')?.addEventListener('click', function(e) {
        e.preventDefault();
        e.stopPropagation();
        removeActiveWord();
    });
    
    // Window resize handlers
    $(window).on('resize orientationchange', function() {
        setFullHeight();
    });


    // ====================================
    // TOOLBAR & STYLING SETUP
    // ====================================
    
    // Apply toolbar settings
    if (mxchatChat.chat_toolbar_toggle === 'on') {
        $('.chat-toolbar').show();
    } else {
        $('.chat-toolbar').hide();
    }
    
    // Apply toolbar icon colors
    const toolbarElements = [
        '#mxchat-chatbot .toolbar-btn svg',
        '#mxchat-chatbot .active-pdf-name',
        '#mxchat-chatbot .active-word-name',
        '#mxchat-chatbot .remove-pdf-btn svg',
        '#mxchat-chatbot .remove-word-btn svg',
        '#mxchat-chatbot .toolbar-perplexity svg'
    ];
    
    toolbarElements.forEach(selector => {
        $(selector).css({
            'fill': toolbarIconColor,
            'stroke': toolbarIconColor,
            'color': toolbarIconColor
        });
    });


// ====================================
//  EMAIL COLLECTION SETUP - FIXED VERSION
// ====================================
// Only run email collection setup if it's enabled
if (mxchatChat && mxchatChat.email_collection_enabled === 'on') {
    //console.log('Email collection is enabled, setting up handlers...');
    
    // Email collection form setup and handlers
    const emailForm = document.getElementById('email-collection-form');
    const emailBlocker = document.getElementById('email-blocker');
    const chatbotWrapper = document.getElementById('chat-container');

    if (emailForm && emailBlocker && chatbotWrapper) {
        
        // Add loading state management
        let isSubmitting = false;
        
        // Optimized UI transition functions
        function showEmailForm() {
            emailBlocker.style.display = 'flex';
            chatbotWrapper.style.display = 'none';
        }

        function showChatContainer() {
            // Show chat immediately without delay
            emailBlocker.style.display = 'none';
            chatbotWrapper.style.display = 'flex';
            
            // Load chat history only after showing chat container
            if (typeof loadChatHistory === 'function') {
                loadChatHistory();
            }
        }

        // Enhanced email validation
        function isValidEmail(email) {
            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            return emailRegex.test(email.trim()) && email.length <= 254; // RFC 5321 limit
        }

        // Enhanced name validation
        function isValidName(name) {
            return name && name.trim().length >= 2 && name.trim().length <= 100;
        }

        // Show loading state with spinner
        function setSubmissionState(loading) {
            const submitButton = document.getElementById('email-submit-button');
            const emailInput = document.getElementById('user-email');
            const nameInput = document.getElementById('user-name');
            
            if (loading) {
                isSubmitting = true;
                if (submitButton) submitButton.disabled = true;
                if (emailInput) emailInput.disabled = true;
                if (nameInput) nameInput.disabled = true;
                
                // Store original content and add spinner
                if (submitButton && !submitButton.getAttribute('data-original-html')) {
                    submitButton.setAttribute('data-original-html', submitButton.innerHTML);
                    
                    // Add loading spinner while keeping original text
                    const originalText = submitButton.textContent;
                    submitButton.innerHTML = `
                        <svg class="email-spinner" style="width: 16px; height: 16px; margin-right: 8px; animation: spin 1s linear infinite;" viewBox="0 0 24 24">
                            <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none" stroke-dasharray="31.416" stroke-dashoffset="31.416">
                                <animate attributeName="stroke-dasharray" dur="2s" values="0 31.416;15.708 15.708;0 31.416" repeatCount="indefinite"/>
                                <animate attributeName="stroke-dashoffset" dur="2s" values="0;-15.708;-31.416" repeatCount="indefinite"/>
                            </circle>
                        </svg>
                        ${originalText}
                    `;
                    
                    submitButton.style.opacity = '0.8';
                }
            } else {
                isSubmitting = false;
                if (submitButton) submitButton.disabled = false;
                if (emailInput) emailInput.disabled = false;
                if (nameInput) nameInput.disabled = false;
                
                // Restore original content
                if (submitButton) {
                    const originalHtml = submitButton.getAttribute('data-original-html');
                    if (originalHtml) {
                        submitButton.innerHTML = originalHtml;
                    }
                    submitButton.style.opacity = '1';
                }
            }
        }

        // Error display functions
        function showEmailError(message) {
            clearEmailError();
            
            const errorDiv = document.createElement('div');
            errorDiv.className = 'email-error';
            errorDiv.style.cssText = `
                color: #e74c3c;
                font-size: 12px;
                margin-top: 8px;
                padding: 4px 0;
                animation: fadeInError 0.3s ease;
            `;
            errorDiv.textContent = message;
            
            // Add CSS animation if not already present
            if (!document.getElementById('email-error-styles')) {
                const style = document.createElement('style');
                style.id = 'email-error-styles';
                style.textContent = `
                    @keyframes fadeInError {
                        from { opacity: 0; transform: translateY(-5px); }
                        to { opacity: 1; transform: translateY(0); }
                    }
                    .email-input-shake {
                        animation: shake 0.5s ease-in-out;
                    }
                    @keyframes shake {
                        0%, 100% { transform: translateX(0); }
                        25% { transform: translateX(-5px); }
                        75% { transform: translateX(5px); }
                    }
                    @keyframes spin {
                        from { transform: rotate(0deg); }
                        to { transform: rotate(360deg); }
                    }
                    .email-spinner {
                        display: inline-block;
                        vertical-align: middle;
                    }
                `;
                document.head.appendChild(style);
            }
            
            emailForm.appendChild(errorDiv);
            
            // Add shake animation to inputs
            const emailInput = document.getElementById('user-email');
            const nameInput = document.getElementById('user-name');
            
            if (emailInput) {
                emailInput.classList.add('email-input-shake');
                setTimeout(() => {
                    emailInput.classList.remove('email-input-shake');
                }, 500);
            }
            
            if (nameInput) {
                nameInput.classList.add('email-input-shake');
                setTimeout(() => {
                    nameInput.classList.remove('email-input-shake');
                }, 500);
            }
        }

        function clearEmailError() {
            const existingErrors = emailForm.querySelectorAll('.email-error');
            existingErrors.forEach(error => error.remove());
        }

        // MAIN FORM SUBMIT HANDLER - This is the critical fix
        //console.log('Setting up form submit handler...');
        
        // Remove any existing event listeners first
        emailForm.removeEventListener('submit', handleFormSubmit);
        
        // Add the form submit handler
        emailForm.addEventListener('submit', handleFormSubmit);
        
        function handleFormSubmit(event) {
            //console.log('Form submit handler triggered!');
            event.preventDefault();
            event.stopPropagation();
            
            // Prevent double submission
            if (isSubmitting) {
                //console.log('Already submitting, ignoring...');
                return false;
            }

            const userEmail = document.getElementById('user-email').value.trim();
            const nameInput = document.getElementById('user-name');
            const userName = nameInput ? nameInput.value.trim() : '';
            const sessionId = getChatSession();

            //console.log('Form data:', { userEmail, userName, sessionId });

            // Validate email before submission
            if (!userEmail) {
                showEmailError('Please enter your email address.');
                return false;
            }

            if (!isValidEmail(userEmail)) {
                showEmailError('Please enter a valid email address.');
                return false;
            }

            // Validate name if field exists
            if (nameInput && !isValidName(userName)) {
                showEmailError('Please enter a valid name (2-100 characters).');
                return false;
            }

            // Clear any existing errors
            clearEmailError();
            setSubmissionState(true);

            //console.log('Sending AJAX request...');

            // Prepare form data with optional name
            const formData = new URLSearchParams({
                action: 'mxchat_handle_save_email_and_response',
                email: userEmail,
                session_id: sessionId,
                nonce: mxchatChat.nonce,
            });

            // Add name to form data if provided
            if (userName) {
                formData.append('name', userName);
            }

            fetch(mxchatChat.ajax_url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: formData
            })
            .then((response) => {
                //console.log('Response received:', response);
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                return response.json();
            })
            .then((data) => {
                //console.log('Response data:', data);
                setSubmissionState(false);
                
                if (data.success) {
                    // Show chat immediately
                    showChatContainer();

                    // Handle bot response if provided
                    if (data.message && typeof appendMessage === 'function') {
                        setTimeout(() => {
                            appendMessage('bot', data.message);
                            if (typeof scrollToBottom === 'function') {
                                scrollToBottom();
                            }
                        }, 100);
                    }
                } else {
                    showEmailError(data.message || 'Failed to save email. Please try again.');
                }
            })
            .catch((error) => {
                console.error('Email submission error:', error);
                setSubmissionState(false);
                showEmailError('An error occurred. Please try again.');
            });
            
            return false; // Extra prevention
        }

        // Real-time email validation
        const emailInput = document.getElementById('user-email');
        if (emailInput) {
            let validationTimeout;
            
            emailInput.addEventListener('input', function() {
                // Clear previous validation timeout
                if (validationTimeout) {
                    clearTimeout(validationTimeout);
                }
                
                // Debounce validation
                validationTimeout = setTimeout(() => {
                    const email = this.value.trim();
                    clearEmailError();
                    
                    if (email && !isValidEmail(email)) {
                        showEmailError('Please enter a valid email address.');
                    }
                }, 500);
            });

            // Handle Enter key
            emailInput.addEventListener('keypress', function(e) {
                if (e.key === 'Enter' && !isSubmitting) {
                    e.preventDefault();
                    emailForm.dispatchEvent(new Event('submit'));
                }
            });
        }

        // Real-time name validation
        const nameInput = document.getElementById('user-name');
        if (nameInput) {
            let nameValidationTimeout;
            
            nameInput.addEventListener('input', function() {
                // Clear previous validation timeout
                if (nameValidationTimeout) {
                    clearTimeout(nameValidationTimeout);
                }
                
                // Debounce validation
                nameValidationTimeout = setTimeout(() => {
                    const name = this.value.trim();
                    clearEmailError();
                    
                    if (name && !isValidName(name)) {
                        showEmailError('Name must be between 2 and 100 characters.');
                    }
                }, 500);
            });

            // Handle Enter key
            nameInput.addEventListener('keypress', function(e) {
                if (e.key === 'Enter' && !isSubmitting) {
                    e.preventDefault();
                    emailForm.dispatchEvent(new Event('submit'));
                }
            });
        }

        // Initial state check
        if (mxchatChat.skip_email_check && mxchatChat.initial_email_state) {
            //console.log('Using server-provided email state:', mxchatChat.initial_email_state);
            
            const emailState = mxchatChat.initial_email_state;
            if (emailState.show_email_form) {
                showEmailForm();
            } else {
                showChatContainer();
            }
        } else {
            // Check email status via AJAX
            setTimeout(checkSessionAndEmail, 100);
        }

        // Check if email exists for the current session
        function checkSessionAndEmail() {
            const sessionId = getChatSession();
            
            fetch(mxchatChat.ajax_url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: new URLSearchParams({
                    action: 'mxchat_check_email_provided',
                    session_id: sessionId,
                    nonce: mxchatChat.nonce,
                })
            })
            .then((response) => {
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                return response.json();
            })
            .then((data) => {
                if (data.success) {
                    if (data.data.logged_in || data.data.email) {
                        showChatContainer();
                    } else {
                        showEmailForm();
                    }
                } else {
                    // On error, default to showing email form
                    showEmailForm();
                }
            })
            .catch((error) => {
                console.warn('Email check failed, defaulting to email form:', error);
                showEmailForm();
            });
        }
        
    } else {
        console.error('Email collection is enabled but essential elements are missing:', {
            emailForm: !!emailForm,
            emailBlocker: !!emailBlocker,
            chatbotWrapper: !!chatbotWrapper
        });
    }
} else {
    //console.log('Email collection is disabled');
}

    // Open chatbot when pre-chat message is clicked
    $(document).on('click', '#pre-chat-message', function() {
        var chatbot = $('#floating-chatbot');
        if (chatbot.hasClass('hidden')) {
            chatbot.removeClass('hidden').addClass('visible');
            $('#floating-chatbot-button').addClass('hidden');
            $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
            disableScroll(); // Disable scroll when chatbot opens
        }
    });

    var closeButton = document.querySelector('.close-pre-chat-message');
    if (closeButton) {
        closeButton.addEventListener('click', function() {
            $('#pre-chat-message').fadeOut(200); // Hide the message

            // Send an AJAX request to set the transient flag for 24 hours
            $.ajax({
                url: mxchatChat.ajax_url,
                type: 'POST',
                data: {
                    action: 'mxchat_dismiss_pre_chat_message',
                    _ajax_nonce: mxchatChat.nonce
                },
                success: function() {
                    //console.log('Pre-chat message dismissed for 24 hours.');

                    // Ensure the message is hidden after dismissal
                    $('#pre-chat-message').hide();
                },
                error: function() {
                    ////console.error('Failed to dismiss pre-chat message.');
                }
            });
        });
    }
    
    
function hasQuickQuestions() {
    const questionButtons = document.querySelectorAll('#mxchat-popular-questions .mxchat-popular-question');
    return questionButtons.length > 0;
}

function collapseQuickQuestions() {
    const questionsContainer = document.getElementById('mxchat-popular-questions');
    if (questionsContainer && hasQuickQuestions()) {
        questionsContainer.classList.add('collapsed');
        questionsContainer.classList.add('has-been-collapsed');
        try {
            sessionStorage.setItem('mxchat_questions_collapsed', 'true');
            sessionStorage.setItem('mxchat_questions_has_been_collapsed', 'true');
        } catch (e) {
            // Ignore if sessionStorage is not available
        }
    }
}

function expandQuickQuestions() {
    const questionsContainer = document.getElementById('mxchat-popular-questions');
    if (questionsContainer && hasQuickQuestions()) {
        questionsContainer.classList.remove('collapsed');
        try {
            sessionStorage.setItem('mxchat_questions_collapsed', 'false');
        } catch (e) {
            // Ignore if sessionStorage is not available
        }
    }
}

function checkQuickQuestionsState() {
    if (!hasQuickQuestions()) {
        return; // Don't do anything if no questions exist
    }
    
    try {
        const isCollapsed = sessionStorage.getItem('mxchat_questions_collapsed');
        const hasBeenCollapsed = sessionStorage.getItem('mxchat_questions_has_been_collapsed');
        
        const questionsContainer = document.getElementById('mxchat-popular-questions');
        if (questionsContainer) {
            if (hasBeenCollapsed === 'true') {
                questionsContainer.classList.add('has-been-collapsed');
            }
            if (isCollapsed === 'true') {
                questionsContainer.classList.add('collapsed');
            }
        }
    } catch (e) {
        // Ignore if sessionStorage is not available
    }
}

//   Global delegation for dynamically added links as fallback
$(document).on('click', '#chat-box a[href]:not([data-tracked])', function(e) {
    const $link = $(this);
    const messageDiv = $link.closest('.bot-message, .agent-message');
    
    // Only process bot/agent message links
    if (messageDiv.length > 0) {
        const originalHref = $link.attr('href');
        
        if (originalHref && (originalHref.startsWith('http://') || originalHref.startsWith('https://'))) {
            e.preventDefault();
            e.stopPropagation();
            
            // Mark as tracked
            $link.attr('data-tracked', 'true');
            
            // Get message context from the message div
            const messageText = messageDiv.text().substring(0, 200);
            
            $.ajax({
                url: mxchatChat.ajax_url,
                type: 'POST',
                data: {
                    action: 'mxchat_track_url_click',
                    session_id: getChatSession(),
                    url: originalHref,
                    message_context: messageText,
                    nonce: mxchatChat.nonce
                },
                complete: function() {
                    if ($link.attr('target') === '_blank' || linkTarget === '_blank') {
                        window.open(originalHref, '_blank');
                    } else {
                        window.location.href = originalHref;
                    }
                }
            });
            
            return false;
        }
    }
});

    
    
    
// ====================================
// MAIN INITIALIZATION
// ====================================

if ($('#floating-chatbot').hasClass('hidden')) {
    $('#floating-chatbot-button').removeClass('hidden');
}
// Initialize when document is ready
setFullHeight();
initializeChatVisibility();
loadChatHistory();
trackOriginatingPage();

// Make functions globally available for add-ons
window.hasQuickQuestions = hasQuickQuestions;
window.collapseQuickQuestions = collapseQuickQuestions;
window.appendMessage = appendMessage;
window.appendThinkingMessage = appendThinkingMessage;
window.scrollToBottom = scrollToBottom;
window.scrollElementToTop = scrollElementToTop;
window.replaceLastMessage = replaceLastMessage;
window.callMxChat = callMxChat;
window.callMxChatStream = callMxChatStream;
window.shouldUseStreaming = shouldUseStreaming;
window.getChatSession = getChatSession;
window.getPageContext = getPageContext;
window.updateStreamingMessage = updateStreamingMessage;

}); // End of jQuery ready


// ====================================
// GLOBAL EVENT LISTENERS (Outside jQuery)
// ====================================

// Event listener for copy button (code blocks)
document.addEventListener("click", (e) => {
    if (e.target.classList.contains("mxchat-copy-button")) {
        const copyButton = e.target;
        const codeBlock = copyButton
            .closest(".mxchat-code-block-container")
            .querySelector(".mxchat-code-block code");

        if (codeBlock) {
            // Preserve formatting using innerText
            navigator.clipboard.writeText(codeBlock.innerText).then(() => {
                copyButton.textContent = "Copied!";
                copyButton.setAttribute("aria-label", "Copied to clipboard");

                setTimeout(() => {
                    copyButton.textContent = "Copy";
                    copyButton.setAttribute("aria-label", "Copy to clipboard");
                }, 2000);
            });
        }
    }
});