HEX
Server: Apache
System: Linux d5123.usc1.stableserver.net 5.14.0-570.17.1.el9_6.x86_64 #1 SMP PREEMPT_DYNAMIC Sat May 24 12:53:17 EDT 2025 x86_64
User: d5123 (1001)
PHP: 8.4.21
Disabled: NONE
Upload Files
File: /home/d5123/myboofola_com/wp-content/plugins/mxchat-basic/js/test-panel.js
/**
 * MxChat Test Panel JavaScript
 * Handles the testing interface for admins - always active when panel is open
 */

class MxChatTestPanel {
    constructor() {
        this.panel = null;
        this.tab = null;
        this.isOpen = false;
        this.lastQueryData = null;
        
        this.init();
    }

    init() {
        // Wait for DOM to be ready
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => this.setup());
        } else {
            this.setup();
        }
    }

    setup() {
        this.createElements();
        this.bindEvents();
        this.setupChatInterception();
    }

    createElements() {
        // Create the test tab
        this.tab = document.createElement('div');
        this.tab.className = 'mxchat-test-tab';
        this.tab.innerHTML = 'MXCHAT DEBUGGING';
        this.tab.title = 'Open MxChat Debug Panel';
        document.body.appendChild(this.tab);

        // Create the test panel
        this.panel = document.createElement('div');
        this.panel.className = 'mxchat-test-panel';
        this.panel.innerHTML = this.getPanelHTML();
        document.body.appendChild(this.panel);
    }

    getPanelHTML() {
        return `
            <div class="mxchat-test-header">
                <h3>MxChat Debug Panel</h3>
                <p>Always-on debugging for administrators</p>
                <button class="mxchat-test-close" title="Close panel">&times;</button>
            </div>
            
            <div class="mxchat-test-content">
                <!-- Session Management -->
                <div class="mxchat-test-section">
                    <h4>Quick Actions</h4>
                    <button class="mxchat-test-btn danger" id="clear-chat-session">
                        Clear Chat Session
                    </button>
                </div>
    
                <!-- Query Analysis -->
                <div class="mxchat-test-section">
                    <h4>Last Query Analysis</h4>
                    <div class="mxchat-test-info">
                        <strong>Similarity Threshold:</strong>
                        <span id="similarity-threshold">Loading...</span>
                    </div>
                    <div class="mxchat-test-info">
                        <strong>User Query:</strong>
                        <div id="last-query" class="query-display">Waiting for next query...</div>
                    </div>
                    <div class="mxchat-test-info">
                        <strong>Approved URLs for Citations:</strong>
                        <div class="mxchat-test-results approved-urls-container" id="approved-urls">
                            <div class="no-data-message">No URL data yet</div>
                        </div>
                    </div>
                    <div class="mxchat-test-info">
                        <strong>Document Matches:</strong>
                        <div class="mxchat-test-results similarity-container" id="similarity-scores">
                            <div class="no-data-message">No query data yet</div>
                        </div>
                    </div>
                    <div class="mxchat-test-info">
                        <strong>Actions Triggered:</strong>
                        <div class="mxchat-test-results actions-container" id="action-scores">
                            <div class="no-data-message">No action data yet</div>
                        </div>
                    </div>
                </div>
    
                <!-- System Information -->
                <div class="mxchat-test-section">
                    <h4>System Information</h4>
                    <div class="mxchat-test-info">
                        <strong>System Prompt:</strong>
                        <div class="mxchat-test-results system-prompt-container" id="system-prompt">Loading...</div>
                    </div>
                    <div class="mxchat-test-info">
                        <strong>Knowledge Base:</strong>
                        <span id="kb-status">Checking...</span>
                    </div>
                </div>
    
                <!-- Debug Console -->
                <div class="mxchat-test-section">
                    <h4>Debug Log</h4>
                    <div class="mxchat-test-results debug-console-container" id="debug-console">
                        <div class="debug-entry">Debug panel ready - monitoring chat activity...</div>
                    </div>
                    <button class="mxchat-test-btn secondary" id="clear-debug">
                        Clear Log
                    </button>
                </div>
            </div>
        `;
    }


    bindEvents() {
        // Tab click to toggle panel
        this.tab.addEventListener('click', () => this.togglePanel());

        // Close button
        const closeBtn = this.panel.querySelector('.mxchat-test-close');
        closeBtn.addEventListener('click', () => this.closePanel());

        // Action buttons
        this.bindActionButtons();

        // Keep escape key to close (useful shortcut)
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape' && this.isOpen) {
                this.closePanel();
            }
        });
    }

    bindActionButtons() {
        // Clear chat session
        this.panel.querySelector('#clear-chat-session').addEventListener('click', () => {
            this.clearChatSession();
        });

        // Clear debug console
        this.panel.querySelector('#clear-debug').addEventListener('click', () => {
            this.clearDebugConsole();
        });
    }

    togglePanel() {
        if (this.isOpen) {
            this.closePanel();
        } else {
            this.openPanel();
        }
    }

    openPanel() {
        this.panel.classList.add('open');
        this.isOpen = true;
        this.tab.style.display = 'none';
        this.loadSystemInfo();
        this.log('Debug panel opened - capturing chat data automatically');
    }

    closePanel() {
        this.panel.classList.remove('open');
        this.isOpen = false;
        this.tab.style.display = 'block';
    }

    setupChatInterception() {
        // Set up interception for chat responses to capture testing data
        this.interceptChatResponses();
        this.log('Chat monitoring initialized');
    }

    interceptChatResponses() {
        // Store reference to the test panel instance
        window.mxchatTestPanelInstance = this;
        
        // Intercept jQuery AJAX calls (for regular chat)
        if (window.jQuery) {
            const originalAjax = jQuery.ajax;
            
            jQuery.ajax = function(options) {
                const originalSuccess = options.success;
                
                options.success = function(data, textStatus, jqXHR) {
                    
                    // Check if this is a chat request
                    if (options.data && 
                        (options.data.action === 'mxchat_handle_chat_request' || 
                         options.data.action === 'mxchat_stream_chat')) {
                        
                        const testPanel = window.mxchatTestPanelInstance;
                        
                        // Always try to handle testing data if it exists (no toggle check)
                        if (testPanel && data && data.testing_data) {
                            testPanel.handleTestingData(data.testing_data);
                        } else if (testPanel && data && data.data && data.data.testing_data) {
                            // Check if data is nested
                            testPanel.handleTestingData(data.data.testing_data);
                        }
                    }
                    
                    // Call original success handler
                    if (originalSuccess) {
                        originalSuccess.call(this, data, textStatus, jqXHR);
                    }
                };
                
                return originalAjax.call(this, options);
            };
        }
        
        // Also intercept fetch API calls (for streaming)
        const originalFetch = window.fetch;
        
        window.fetch = (...args) => {
            return originalFetch(...args).then(response => {
                // Check if this is a chat request
                if (args[0].includes('admin-ajax.php') || args[0].includes('mxchat')) {
                    // For streaming responses that return JSON instead of streams
                    const contentType = response.headers.get('content-type');
                    if (contentType && contentType.includes('application/json')) {
                        response.clone().json().then(data => {
                            const testPanel = window.mxchatTestPanelInstance;
                            
                            // Always try to handle testing data if it exists (no toggle check)
                            if (testPanel && data && data.testing_data) {
                                testPanel.handleTestingData(data.testing_data);
                            } else if (testPanel && data && data.data && data.data.testing_data) {
                                // Check nested data
                                testPanel.handleTestingData(data.data.testing_data);
                            }
                        }).catch(() => {
                            // Ignore JSON parsing errors
                        });
                    }
                }
                return response;
            });
        };
        
        this.log('Chat interception active for jQuery and fetch requests');
    }

handleTestingData(testingData) {
    this.log('๐Ÿ“Š Chat data captured from response');
    
    // Update query analysis section
    this.updateLastQuery(testingData.query || 'No query', testingData.top_matches || []);
    
    // NEW: Update approved URLs
    this.updateApprovedUrls(testingData.approved_urls || []);
    
    this.updateTopMatches(testingData.top_matches || [], testingData.similarity_threshold || 0.75, testingData.sources_used || 0, testingData.total_chunks_used || 0);
    
    // NEW: Update action matches
    this.updateActionMatches(testingData.action_matches || []);
    
    // Log additional info
    if (testingData.knowledge_base_type) {
        this.log(`๐Ÿ“š Knowledge Base: ${testingData.knowledge_base_type}`);
    }
    if (testingData.similarity_threshold) {
        this.log(`๐ŸŽฏ Similarity Threshold: ${(testingData.similarity_threshold * 100)}%`);
    }
    
    // NEW: Log approved URLs count
    if (testingData.approved_urls && testingData.approved_urls.length > 0) {
        this.log(`๐Ÿ”— Approved URLs for citations: ${testingData.approved_urls.length}`);
    }
    
    // Show summary in debug console
    if (testingData.top_matches && testingData.top_matches.length > 0) {
        const aboveThreshold = testingData.top_matches.filter(match => match.above_threshold).length;
        const belowThreshold = testingData.top_matches.length - aboveThreshold;
        const highestScore = testingData.top_matches[0].similarity_percentage;
        
        this.log(`โœ… Analysis: ${aboveThreshold} above threshold, ${belowThreshold} below threshold`);
        this.log(`๐Ÿ† Highest similarity: ${highestScore}%`);
    } else {
        this.log('โš ๏ธ No document matches found');
    }
    
    // NEW: Log action summary
    if (testingData.action_matches && testingData.action_matches.length > 0) {
        const triggeredAction = testingData.action_matches.find(action => action.triggered);
        if (triggeredAction) {
            this.log(`๐ŸŽฏ Action Triggered: ${triggeredAction.intent_label} (${triggeredAction.similarity_percentage}%)`);
        } else {
            const highestAction = testingData.action_matches[0];
            this.log(`๐Ÿšซ No actions triggered - Highest: ${highestAction.intent_label} (${highestAction.similarity_percentage}%)`);
        }
    } else {
        this.log('๐Ÿ“ No actions checked');
    }
}

updateApprovedUrls(approvedUrls) {
    const urlsEl = this.panel.querySelector('#approved-urls');
    
    // Safety check: ensure approvedUrls is an array
    if (!approvedUrls || !Array.isArray(approvedUrls) || approvedUrls.length === 0) {
        urlsEl.innerHTML = '<div class="no-data-message">No approved URLs (AI cannot cite links)</div>';
        return;
    }
    
    let html = `<div class="urls-header">
        <strong>${approvedUrls.length} URL${approvedUrls.length !== 1 ? 's' : ''} approved for AI citations</strong>
    </div>`;
    
    approvedUrls.forEach((url, index) => {
        // Extract domain for display
        let displayUrl = url;
        try {
            const urlObj = new URL(url);
            displayUrl = urlObj.hostname + urlObj.pathname;
        } catch (e) {
            // Keep original if URL parsing fails
        }
        
        html += `
            <div class="url-card">
                <div class="url-line">
                    <span class="url-icon">๐Ÿ”—</span>
                    <a href="${url}" target="_blank" rel="noopener noreferrer" class="url-link" title="${url}">
                        ${displayUrl}
                    </a>
                </div>
            </div>
        `;
    });
    
    html += `<div class="urls-note">
        โ„น๏ธ The AI can only cite these URLs. Any other URLs will be automatically removed from responses.
    </div>`;
    
    urlsEl.innerHTML = html;
}

updateActionMatches(actionMatches) {
    const actionsEl = this.panel.querySelector('#action-scores');
    
    if (!actionMatches || actionMatches.length === 0) {
        actionsEl.innerHTML = '<div class="no-data-message">No actions checked</div>';
        return;
    }
    
    let html = `<div class="actions-header">
        <strong>Top ${actionMatches.length} actions checked</strong>
    </div>`;
    
    actionMatches.forEach((action, index) => {
        const isTriggered = action.triggered;
        const isAboveThreshold = action.above_threshold;
        const statusIcon = isTriggered ? '๐ŸŽฏ' : (isAboveThreshold ? 'โš ๏ธ' : 'โŒ');
        
        // Determine the correct label based on status
        let statusLabel;
        if (isTriggered) {
            statusLabel = 'TRIGGERED';
        } else if (isAboveThreshold) {
            statusLabel = 'Above threshold';
        } else {
            statusLabel = 'Below threshold';
        }
        
        // Determine card styling
        const cardClass = isTriggered ? 'action-triggered' : (isAboveThreshold ? 'action-above-threshold' : 'action-below-threshold');
        
        html += `
            <div class="action-card ${cardClass}">
                <div class="action-line">
                    <span class="action-icon">${statusIcon}</span>
                    <span class="action-name">${action.intent_label}</span>
                    <span class="action-score">${action.similarity_percentage}%</span>
                </div>
                <div class="action-details">
                    <span class="action-status">${statusLabel}</span>
                    <span class="action-threshold">Threshold: ${action.threshold_percentage}%</span>
                </div>
            </div>
        `;
    });
    
    actionsEl.innerHTML = html;
}

    updateTopMatches(topMatches, threshold, sourcesUsed = 0, totalChunksUsed = 0) {
        const scoresEl = this.panel.querySelector('#similarity-scores');

        if (!topMatches || topMatches.length === 0) {
            scoresEl.innerHTML = '<div class="no-data-message">No similarity data available</div>';
            return;
        }

        // Group matches by source URL
        const groupedByUrl = {};
        topMatches.forEach((match) => {
            const url = match.source_display || 'Unknown';
            if (!groupedByUrl[url]) {
                groupedByUrl[url] = {
                    url: url,
                    isUrl: url.startsWith('http'),
                    bestScore: 0,
                    usedForContext: false,
                    totalChunks: match.total_chunks || 1,
                    matchedChunks: [],
                    isChunked: match.is_chunk || false
                };
            }

            // Track best score
            if (match.similarity_percentage > groupedByUrl[url].bestScore) {
                groupedByUrl[url].bestScore = match.similarity_percentage;
            }

            // Track if any chunk was used for context
            if (match.used_for_context) {
                groupedByUrl[url].usedForContext = true;
            }

            // Add chunk info
            groupedByUrl[url].matchedChunks.push({
                chunkIndex: match.chunk_index,
                score: match.similarity_percentage,
                usedForContext: match.used_for_context,
                aboveThreshold: match.above_threshold
            });
        });

        // Convert to array and sort by best score
        const urlGroups = Object.values(groupedByUrl).sort((a, b) => b.bestScore - a.bestScore);

        // Use backend counts if available, otherwise fall back to frontend calculation
        const usedUrlCount = sourcesUsed > 0 ? sourcesUsed : urlGroups.filter(g => g.usedForContext).length;
        const chunksInfo = totalChunksUsed > 0 ? `${totalChunksUsed} chunks sent to AI` : `${topMatches.length} chunk matches`;

        let html = `<div class="matches-header">
            <strong>${usedUrlCount} source${usedUrlCount === 1 ? '' : 's'} used for AI context</strong>
            <span class="matches-subheader">(${chunksInfo})</span>
        </div>`;

        urlGroups.forEach((group, groupIndex) => {
            const cardClass = group.usedForContext ? 'above-threshold' : 'below-threshold';
            const statusIcon = group.usedForContext ? 'โœ“' : 'โœ—';
            const contextLabel = group.usedForContext ? 'Used for AI context' : 'Not used';

            // Build chunk summary
            let chunkSummary = '';
            if (group.isChunked && group.totalChunks > 1) {
                const usedChunkCount = group.matchedChunks.filter(c => c.usedForContext).length;
                chunkSummary = `<span class="chunk-summary">${usedChunkCount}/${group.totalChunks} chunks matched</span>`;
            }

            // Check if this entry has multiple matched chunks to show expand toggle
            const hasMultipleChunks = group.matchedChunks.length > 1;
            const expandToggle = hasMultipleChunks
                ? `<span class="chunk-expand-toggle" data-group="${groupIndex}">โ–ถ Show chunks</span>`
                : '';

            html += `
                <div class="match-card ${cardClass}">
                    <div class="match-header">
                        <div class="match-title">
                            <span class="status-icon">${statusIcon}</span>
                            <span class="similarity-score">${group.bestScore}%</span>
                            ${chunkSummary}
                        </div>
                        <span class="context-label">${contextLabel}</span>
                    </div>
                    <div class="match-source">
                        ${group.isUrl ?
                            `<span class="source-icon link-icon">๐Ÿ”—</span> ${group.url}` :
                            `<span class="source-icon doc-icon">๐Ÿ“„</span> ${group.url}`
                        }
                    </div>
                    ${expandToggle}
                    ${hasMultipleChunks ? this.renderChunkDetails(group.matchedChunks, groupIndex) : ''}
                </div>
            `;
        });

        scoresEl.innerHTML = html;

        // Add click handlers for expand toggles
        scoresEl.querySelectorAll('.chunk-expand-toggle').forEach(toggle => {
            toggle.addEventListener('click', (e) => {
                const groupId = e.target.dataset.group;
                const details = scoresEl.querySelector(`.chunk-details[data-group="${groupId}"]`);
                if (details) {
                    const isExpanded = details.classList.toggle('expanded');
                    e.target.textContent = isExpanded ? 'โ–ผ Hide chunks' : 'โ–ถ Show chunks';
                }
            });
        });
    }

    renderChunkDetails(chunks, groupIndex) {
        // Sort chunks by chunk index
        const sortedChunks = [...chunks].sort((a, b) => (a.chunkIndex || 0) - (b.chunkIndex || 0));

        let html = `<div class="chunk-details" data-group="${groupIndex}">`;

        sortedChunks.forEach(chunk => {
            const chunkNum = (chunk.chunkIndex !== null && chunk.chunkIndex !== undefined)
                ? chunk.chunkIndex + 1
                : '?';
            const statusClass = chunk.usedForContext ? 'chunk-used' : 'chunk-not-used';
            const statusIcon = chunk.usedForContext ? 'โœ“' : 'โ—‹';

            html += `
                <div class="chunk-detail-row ${statusClass}">
                    <span class="chunk-detail-icon">${statusIcon}</span>
                    <span class="chunk-detail-num">Chunk ${chunkNum}</span>
                    <span class="chunk-detail-score">${chunk.score}%</span>
                </div>
            `;
        });

        html += '</div>';
        return html;
    }

    updateLastQuery(query, topMatches) {
        const queryEl = this.panel.querySelector('#last-query');
        queryEl.textContent = query;
        
        this.lastQueryData = { 
            query, 
            topMatches, 
            timestamp: new Date() 
        };
    }

    clearChatSession() {
        // Determine the active bot ID from MxChatInstances
        let botId = 'default';
        if (typeof MxChatInstances !== 'undefined' && typeof MxChatInstances.getAllBotIds === 'function') {
            const botIds = MxChatInstances.getAllBotIds();
            if (botIds.length > 0) {
                botId = botIds[0];
            }
        }

        // Get current session ID using the correct bot-suffixed cookie name
        const cookieName = 'mxchat_session_id_' + botId;
        const sessionId = this.getCookie(cookieName) || this.getCookie('mxchat_session_id') || this.getCurrentSessionId();

        if (!sessionId) {
            this.log('No active session found');
            return;
        }

        this.log('Current session ID: ' + sessionId);
        this.log('Starting fresh session...');

        // Use MxChatInstances.resetChatSession to properly reset in-memory state + cookie
        if (typeof MxChatInstances !== 'undefined' && typeof MxChatInstances.resetChatSession === 'function') {
            MxChatInstances.resetChatSession(botId);
        }

        // Get the new session ID that was just set by resetChatSession
        let newSessionId = '';
        if (typeof MxChatInstances !== 'undefined' && typeof MxChatInstances.getChatSession === 'function') {
            newSessionId = MxChatInstances.getChatSession(botId);
        }

        // Fallback if MxChatInstances wasn't available
        if (!newSessionId) {
            newSessionId = 'mxchat_chat_' + Math.random().toString(36).substr(2, 9);
            this.clearMxChatCookie(botId);
            this.setChatSession(newSessionId, botId);
        }

        this.log('New session ID: ' + newSessionId);

        // Call backend to clear old session data
        fetch(mxchatTestData.ajaxUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                action: 'mxchat_start_fresh_session',
                nonce: mxchatTestData.nonce,
                old_session_id: sessionId,
                new_session_id: newSessionId
            })
        })
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                this.log('Backend session cleared: ' + data.data.message);

                // Update the session ID everywhere in the DOM
                this.updateSessionIdEverywhere(newSessionId);

                // Clear the chat UI
                this.clearChatUI();

                // Show popular questions again
                const popularQuestions = document.querySelector('#mxchat-popular-questions');
                if (popularQuestions) {
                    popularQuestions.style.display = 'block';
                }

                // Clear testing data displays
                this.updateLastQuery('New session started', []);
                this.updateTopMatches([], 0);
                this.updateApprovedUrls([]);

                this.log('Fresh session started successfully');

            } else {
                this.log('Error clearing session: ' + (data.data?.message || 'Unknown error'));
            }
        })
        .catch(error => {
            console.error('Error clearing chat session:', error);
            this.log('Connection error when clearing session');
        });
    }

    // Helper function to get cookie (same as your existing one)
    getCookie(name) {
        let value = "; " + document.cookie;
        let parts = value.split("; " + name + "=");
        if (parts.length == 2) return parts.pop().split(";").shift();
    }

    // Helper function to set session cookie (matches chat-script.js format)
    setChatSession(sessionId, botId) {
        botId = botId || 'default';
        document.cookie = 'mxchat_session_id_' + botId + '=' + sessionId + '; path=/; max-age=86400; SameSite=Lax';
    }

    // Helper function to clear the MxChat session cookie
    clearMxChatCookie(botId) {
        botId = botId || 'default';
        // Clear both bot-suffixed and legacy cookie formats
        document.cookie = 'mxchat_session_id_' + botId + '=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax';
        document.cookie = 'mxchat_session_id=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax';
        this.log('Session cookie cleared');
    }

    updateSessionIdEverywhere(newSessionId) {
        // Update global session ID variable if it exists
        if (window.mxchatSessionId) {
            window.mxchatSessionId = newSessionId;
        }
        
        // Update session ID in chat input data attribute
        const chatInput = document.querySelector('#chat-input');
        if (chatInput) {
            chatInput.dataset.sessionId = newSessionId;
        }
        
        // Update any hidden session ID fields
        const sessionInputs = document.querySelectorAll('input[name="session_id"]');
        sessionInputs.forEach(input => {
            input.value = newSessionId;
        });
        
        // Update any data attributes that store session ID
        const elementsWithSessionId = document.querySelectorAll('[data-session-id]');
        elementsWithSessionId.forEach(element => {
            element.dataset.sessionId = newSessionId;
        });
        
        // Update URL parameter if it exists
        if (window.location.search.includes('session_id=')) {
            const url = new URL(window.location);
            url.searchParams.set('session_id', newSessionId);
            window.history.replaceState({}, '', url);
        }
        
        this.log('๐Ÿ”„ Session ID updated everywhere in DOM');
    }

    clearChatUI() {
        const chatBox = document.querySelector('#chat-box');
        if (chatBox) {
            // Remove all messages (both user and bot)
            const allMessages = chatBox.querySelectorAll('.bot-message, .user-message');
            allMessages.forEach(msg => {
                // Keep the first bot message if it's a welcome message
                if (msg === chatBox.querySelector('.bot-message') && 
                    msg.textContent.toLowerCase().includes('welcome')) {
                    return; // Keep welcome message
                }
                msg.remove();
            });
            this.log('๐Ÿงน Chat UI cleared');
        }
        
        // Clear chat input
        const chatInput = document.querySelector('#chat-input');
        if (chatInput) {
            chatInput.value = '';
        }
    }

    getCurrentSessionId() {
        // Try MxChatInstances first (most reliable โ€” matches chat-script.js)
        if (typeof MxChatInstances !== 'undefined' && typeof MxChatInstances.getChatSession === 'function') {
            const botIds = typeof MxChatInstances.getAllBotIds === 'function' ? MxChatInstances.getAllBotIds() : ['default'];
            const botId = botIds.length > 0 ? botIds[0] : 'default';
            const instanceSession = MxChatInstances.getChatSession(botId);
            if (instanceSession) {
                return instanceSession;
            }
        }

        // Try bot-suffixed cookie, then legacy cookie
        const botCookieId = this.getCookie('mxchat_session_id_default');
        if (botCookieId) {
            return botCookieId;
        }
        const cookieSessionId = this.getCookie('mxchat_session_id');
        if (cookieSessionId) {
            return cookieSessionId;
        }
        
        // Try to get session ID from various DOM sources
        const chatInput = document.querySelector('#chat-input');
        if (chatInput && chatInput.dataset.sessionId) {
            return chatInput.dataset.sessionId;
        }
        
        // Try to get from URL parameters
        const urlParams = new URLSearchParams(window.location.search);
        const sessionFromUrl = urlParams.get('session_id');
        if (sessionFromUrl) {
            return sessionFromUrl;
        }
        
        // Try to get from global variables
        if (window.chatSessionId) {
            return window.chatSessionId;
        }
        
        // Generate a temporary session ID if none found
        return 'mxchat_chat_' + Math.random().toString(36).substr(2, 9);
    }
    
    loadSystemInfo() {
        // Load system information from backend
        this.updateSimilarityThreshold();
        this.updateSystemPrompt();
        this.updateKnowledgeBaseStatus();
    }

    updateSimilarityThreshold() {
        fetch(mxchatTestData.ajaxUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                action: 'mxchat_get_similarity_threshold',
                nonce: mxchatTestData.nonce
            })
        })
        .then(response => response.json())
        .then(data => {
            const thresholdEl = this.panel.querySelector('#similarity-threshold');
            if (data.success) {
                thresholdEl.innerHTML = `<code>${data.data.threshold_percentage}</code>`;
            } else {
                thresholdEl.innerHTML = '<span class="error-text">Error loading threshold</span>';
            }
        })
        .catch(error => {
            console.error('Error fetching similarity threshold:', error);
            const thresholdEl = this.panel.querySelector('#similarity-threshold');
            thresholdEl.innerHTML = '<span class="error-text">Connection error</span>';
        });
    }

    updateSystemPrompt() {
    const promptEl = this.panel.querySelector('#system-prompt');
    promptEl.textContent = 'Loading system prompt...';
    
    fetch(mxchatTestData.ajaxUrl, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
            action: 'mxchat_get_system_info',
            nonce: mxchatTestData.nonce
        })
    })
    .then(response => response.json())
    .then(data => {
        if (data.success) {
            promptEl.textContent = data.data.system_prompt || 'No system prompt configured';
            
            // Enhanced model display with OpenRouter support
            if (data.data.is_openrouter) {
                this.log(`๐Ÿค– Model: OpenRouter`);
                this.log(`   โ””โ”€ Using: ${data.data.openrouter_model}`);
            } else {
                this.log(`๐Ÿค– Model: ${data.data.selected_model}`);
            }
            
            // Enhanced API status with OpenRouter
            const apiStatus = data.data.api_status;
            const configuredApis = Object.keys(apiStatus).filter(key => apiStatus[key]);
            
            if (configuredApis.length > 0) {
                this.log(`๐Ÿ”‘ Configured APIs: ${configuredApis.map(api => {
                    // Capitalize and format API names
                    if (api === 'openai') return 'OpenAI';
                    if (api === 'xai') return 'X.AI';
                    if (api === 'openrouter') return 'OpenRouter';
                    return api.charAt(0).toUpperCase() + api.slice(1);
                }).join(', ')}`);
            } else {
                this.log(`โš ๏ธ No API keys configured`);
            }
            
            // Specific warning for OpenRouter if selected but no key
            if (data.data.is_openrouter && !apiStatus.openrouter) {
                this.log(`โŒ WARNING: OpenRouter selected but no API key configured!`);
            }
        } else {
            promptEl.textContent = 'Error loading system prompt';
        }
    })
    .catch(error => {
        console.error('Error fetching system info:', error);
        promptEl.textContent = 'Connection error';
    });
}

    updateKnowledgeBaseStatus() {
        const statusEl = this.panel.querySelector('#kb-status');
        statusEl.innerHTML = 'Checking...';
        
        fetch(mxchatTestData.ajaxUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                action: 'mxchat_get_kb_status',
                nonce: mxchatTestData.nonce
            })
        })
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                const kbData = data.data;
                statusEl.innerHTML = `<span class="success-text">โœ“ ${kbData.status}</span> (${kbData.type} - ${kbData.documents})`;
            } else {
                statusEl.innerHTML = '<span class="error-text">Error loading KB status</span>';
            }
        })
        .catch(error => {
            console.error('Error fetching KB status:', error);
            statusEl.innerHTML = '<span class="error-text">Connection error</span>';
        });
    }

    clearDebugConsole() {
        const console = this.panel.querySelector('#debug-console');
        console.innerHTML = '<div class="debug-entry">Debug console cleared...</div>';
    }

    log(message) {
        const console = this.panel.querySelector('#debug-console');
        const timestamp = new Date().toLocaleTimeString();
        const logEntry = document.createElement('div');
        logEntry.className = 'debug-entry';
        logEntry.innerHTML = `<span class="debug-timestamp">[${timestamp}]</span> ${message}`;
        console.appendChild(logEntry);
        console.scrollTop = console.scrollHeight;
        
        // Keep only last 50 entries to prevent memory issues
        const entries = console.querySelectorAll('.debug-entry');
        if (entries.length > 50) {
            entries[0].remove();
        }
    }
}

// Initialize the test panel when the script loads
document.addEventListener('DOMContentLoaded', function() {
    // Only initialize if user is admin and testing is enabled
    if (window.mxchatTestingEnabled) {
        window.mxchatTestPanel = new MxChatTestPanel();
    }
});

// Global function to enable testing mode programmatically
window.enableMxChatTesting = function() {
    if (!window.mxchatTestPanel) {
        window.mxchatTestPanel = new MxChatTestPanel();
    }
};