Source: frontend/js/components/chat.js

/**
 * Enhanced Chat Component with MCP Integration
 * Handles both standard and streaming chat with MCP tool usage indicators
 */

export class ChatManager {
    constructor() {
        this.providers = [];
        this.currentProvider = null;
        this.conversations = new Map(); // Store conversation histories
        this.mcpClient = null; // Will be set when MCP client is available
        
        // Initialize DOM elements
        this.initializeDOMElements();
        
        // Bind event listeners
        this.bindEventListeners();
        
        // Load providers and initialize
        this.loadProviders();
    }

    /**
     * Initialize DOM element references
     */
    initializeDOMElements() {
        // Standard chat elements
        this.standardForm = document.getElementById('chat-form');
        this.standardInput = document.getElementById('chat-input');
        this.standardMessages = document.getElementById('chat-messages');
        this.standardProvider = document.getElementById('chat-provider');
        this.standardTemperature = document.getElementById('chat-temperature');
        this.standardMemory = document.getElementById('chat-memory');
        this.temperatureValue = document.getElementById('temperature-value');
        
        // Streaming chat elements
        this.streamForm = document.getElementById('chat-stream-form');
        this.streamInput = document.getElementById('chat-stream-input');
        this.streamMessages = document.getElementById('chat-stream-messages');
        this.streamProvider = document.getElementById('chat-stream-provider');
        this.streamTemperature = document.getElementById('chat-stream-temperature');
        this.streamMemory = document.getElementById('chat-stream-memory');
        this.streamTemperatureValue = document.getElementById('stream-temperature-value');
    }

    /**
     * Bind event listeners
     */
    bindEventListeners() {
        // Standard chat form
        if (this.standardForm) {
            this.standardForm.addEventListener('submit', (e) => this.handleChatSubmit(e, 'standard'));
        }
        
        // Streaming chat form
        if (this.streamForm) {
            this.streamForm.addEventListener('submit', (e) => this.handleChatSubmit(e, 'stream'));
        }
        
        // Temperature sliders
        if (this.standardTemperature && this.temperatureValue) {
            this.standardTemperature.addEventListener('input', (e) => {
                this.temperatureValue.textContent = e.target.value;
            });
        }
        
        if (this.streamTemperature && this.streamTemperatureValue) {
            this.streamTemperature.addEventListener('input', (e) => {
                this.streamTemperatureValue.textContent = e.target.value;
            });
        }
        
        // Provider selection
        if (this.standardProvider) {
            this.standardProvider.addEventListener('change', (e) => {
                this.handleProviderChange(e.target.value, 'standard');
            });
        }
        
        if (this.streamProvider) {
            this.streamProvider.addEventListener('change', (e) => {
                this.handleProviderChange(e.target.value, 'stream');
            });
        }
        
        // Auto-resize textareas
        [this.standardInput, this.streamInput].forEach(input => {
            if (input) {
                input.addEventListener('input', this.autoResizeTextarea.bind(this));
                input.addEventListener('keydown', this.handleInputKeydown.bind(this));
            }
        });
    }

    /**
     * Load available chat providers from API
     */
    async loadProviders() {
        try {
            console.log('Loading chat providers...');
            const response = await fetch('/api/providers');
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            
            const data = await response.json();
            this.providers = data.providers || [];
            
            console.log(`Loaded ${this.providers.length} providers:`, this.providers);
            this.populateProviderDropdowns();
            
        } catch (error) {
            console.error('Failed to load chat providers:', error);
            this.showError('Failed to load chat providers. Using fallback.');
            this.providers = [{ id: 'fallback', name: 'Fallback Provider', type: 'unknown' }];
            this.populateProviderDropdowns();
        }
    }

    /**
     * Populate provider dropdown menus
     */
    populateProviderDropdowns() {
        const dropdowns = [this.standardProvider, this.streamProvider];
        
        dropdowns.forEach(dropdown => {
            if (!dropdown) return;
            
            // Clear existing options
            dropdown.innerHTML = '';
            
            if (this.providers.length === 0) {
                dropdown.innerHTML = '<option value="" disabled>No providers available</option>';
                return;
            }
            
            // Add default option
            dropdown.innerHTML = '<option value="" disabled selected>Select a provider...</option>';
            
            // Add provider options
            this.providers.forEach(provider => {
                const option = document.createElement('option');
                option.value = provider.id;
                option.textContent = `${provider.name} (${provider.type})`;
                
                // Add MCP indicator if provider has MCP capabilities
                if (provider.capabilities && provider.capabilities.includes('mcp')) {
                    option.textContent += ' 🔗';
                    option.title = 'MCP-enabled provider';
                }
                
                dropdown.appendChild(option);
            });
            
            // Select first available provider by default
            if (this.providers.length > 0) {
                dropdown.value = this.providers[0].id;
                this.currentProvider = this.providers[0];
            }
        });
    }

    /**
     * Handle provider selection change
     */
    handleProviderChange(providerId, mode) {
        this.currentProvider = this.providers.find(p => p.id === providerId);
        console.log(`Selected provider for ${mode}:`, this.currentProvider);
        
        // Update MCP integration status
        this.updateMCPStatus();
    }

    /**
     * Update MCP integration status
     */
    updateMCPStatus() {
        // Check if current provider supports MCP tools
        const hasMCPSupport = this.currentProvider && 
                            this.currentProvider.capabilities && 
                            this.currentProvider.capabilities.includes('mcp');
        
        // Get MCP client if available
        if (window.mcpClient) {
            this.mcpClient = window.mcpClient;
        }
        
        console.log('MCP Status:', {
            provider: this.currentProvider?.name,
            mcpSupport: hasMCPSupport,
            mcpConnected: this.mcpClient?.connected || false
        });
    }

    /**
     * Handle chat form submission
     */
    async handleChatSubmit(event, mode) {
        event.preventDefault();
        
        const isStream = mode === 'stream';
        const form = isStream ? this.streamForm : this.standardForm;
        const input = isStream ? this.streamInput : this.standardInput;
        const messages = isStream ? this.streamMessages : this.standardMessages;
        const provider = isStream ? this.streamProvider : this.standardProvider;
        const temperature = isStream ? this.streamTemperature : this.standardTemperature;
        const useMemory = isStream ? this.streamMemory : this.standardMemory;
        
        const prompt = input.value.trim();
        if (!prompt) return;
        
        const selectedProviderId = provider.value;
        if (!selectedProviderId) {
            this.showError('Please select a provider first.');
            return;
        }
        
        // Clear input and disable form
        input.value = '';
        this.setFormState(form, false);
        
        // Add user message to chat
        this.addMessage(messages, 'user', prompt);
        
        // Prepare request data
        const requestData = {
            prompt,
            providerId: selectedProviderId,
            temperature: parseFloat(temperature.value),
            useMemory: useMemory.checked,
            useSearchInterjection: false, // Can be made configurable
            conversationId: this.getConversationId(mode)
        };
        
        try {
            if (isStream) {
                await this.handleStreamingChat(messages, requestData);
            } else {
                await this.handleStandardChat(messages, requestData);
            }
        } catch (error) {
            console.error('Chat error:', error);
            this.addMessage(messages, 'assistant', `Error: ${error.message}`, { error: true });
        } finally {
            this.setFormState(form, true);
        }
    }

    /**
     * Handle standard (non-streaming) chat
     */
    async handleStandardChat(messages, requestData) {
        // Add loading message
        const loadingId = this.addMessage(messages, 'assistant', 'Thinking...', { loading: true });
        
        try {
            const response = await fetch('/api/chat', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(requestData)
            });
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            
            const data = await response.json();
            
            // Remove loading message
            this.removeMessage(messages, loadingId);
            
            // Add response with MCP tool indicators if available
            const messageOptions = {
                searchResults: data.searchResults,
                mcpTools: data.mcpToolsUsed,
                conversationId: data.conversationId
            };
            
            this.addMessage(messages, 'assistant', data.response, messageOptions);
            
        } catch (error) {
            this.removeMessage(messages, loadingId);
            throw error;
        }
    }

    /**
     * Handle streaming chat with Server-Sent Events
     */
    async handleStreamingChat(messages, requestData) {
        // Add streaming message container
        const streamingId = this.addMessage(messages, 'assistant', '', { streaming: true });
        const messageElement = document.querySelector(`[data-message-id="${streamingId}"] .message-content`);
        
        try {
            const response = await fetch('/api/chat/stream', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(requestData)
            });
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            
            const reader = response.body.getReader();
            const decoder = new TextDecoder();
            let buffer = '';
            let fullResponse = '';
            
            while (true) {
                const { done, value } = await reader.read();
                
                if (done) break;
                
                buffer += decoder.decode(value, { stream: true });
                const lines = buffer.split('\n');
                buffer = lines.pop(); // Keep incomplete line in buffer
                
                for (const line of lines) {
                    if (line.startsWith('data: ')) {
                        try {
                            const data = JSON.parse(line.slice(6));
                            
                            if (data.info) {
                                // Show progress information
                                this.updateStreamingStatus(messageElement, data.info);
                            } else if (data.searchResults) {
                                // Handle search results
                                this.showSearchResults(messageElement, data.searchResults);
                            } else if (data.token) {
                                // Append streaming token
                                fullResponse += data.token;
                                messageElement.textContent = fullResponse;
                            } else if (data.response) {
                                // Final response
                                fullResponse = data.response;
                                messageElement.textContent = fullResponse;
                            }
                            
                            // Scroll to bottom
                            messages.scrollTop = messages.scrollHeight;
                            
                        } catch (e) {
                            console.warn('Failed to parse SSE data:', line);
                        }
                    }
                }
            }
            
            // Mark streaming as complete
            this.markStreamingComplete(streamingId);
            
        } catch (error) {
            this.removeMessage(messages, streamingId);
            throw error;
        }
    }

    /**
     * Add a message to the chat
     */
    addMessage(container, role, content, options = {}) {
        const messageId = `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
        const messageDiv = document.createElement('div');
        messageDiv.className = `chat-message ${role}`;
        messageDiv.setAttribute('data-message-id', messageId);
        
        let messageHTML = `<div class="message-content">${this.formatMessageContent(content)}</div>`;
        
        // Add MCP tool indicators
        if (options.mcpTools && options.mcpTools.length > 0) {
            messageHTML += this.createMCPToolIndicators(options.mcpTools);
        }
        
        // Add search results
        if (options.searchResults && options.searchResults.length > 0) {
            messageHTML += this.createSearchResultsDisplay(options.searchResults);
        }
        
        // Add loading indicator
        if (options.loading) {
            messageHTML += '<div class="message-loading"><div class="loading-dots">●●●</div></div>';
        }
        
        // Add streaming indicator
        if (options.streaming) {
            messageHTML += '<div class="message-streaming">▋</div>';
        }
        
        // Add error styling
        if (options.error) {
            messageDiv.classList.add('error');
        }
        
        messageDiv.innerHTML = messageHTML;
        container.appendChild(messageDiv);
        
        // Scroll to bottom
        container.scrollTop = container.scrollHeight;
        
        return messageId;
    }

    /**
     * Create MCP tool usage indicators
     */
    createMCPToolIndicators(tools) {
        if (!tools || tools.length === 0) return '';
        
        const toolsHTML = tools.map(tool => `
            <div class="mcp-tool-indicator" title="${tool.description || tool.name}">
                🔧 ${tool.name}
                ${tool.duration ? `<span class="tool-duration">(${tool.duration}ms)</span>` : ''}
            </div>
        `).join('');
        
        return `<div class="mcp-tools-used">
            <div class="tools-header">🔗 MCP Tools Used:</div>
            ${toolsHTML}
        </div>`;
    }

    /**
     * Create search results display
     */
    createSearchResultsDisplay(results) {
        if (!results || results.length === 0) return '';
        
        const resultsHTML = results.map(result => `
            <div class="search-result-item">
                <div class="result-title">${result.title || 'Untitled'}</div>
                <div class="result-content">${result.content}</div>
                <div class="result-score">Relevance: ${(result.score * 100).toFixed(1)}%</div>
            </div>
        `).join('');
        
        return `<div class="search-results-used">
            <div class="search-header">🔍 Relevant Context Found:</div>
            ${resultsHTML}
        </div>`;
    }

    /**
     * Format message content
     */
    formatMessageContent(content) {
        if (!content) return '';
        
        // Debug logging for non-string content
        if (typeof content !== 'string') {
            console.debug('formatMessageContent received non-string content:', typeof content, content);
        }
        
        // Handle different content types
        let contentStr;
        if (typeof content === 'string') {
            contentStr = content;
        } else if (typeof content === 'object') {
            // If it's an object, try to extract text content
            if (content.text) {
                contentStr = content.text;
            } else if (content.content) {
                contentStr = content.content;
            } else if (content.message) {
                contentStr = content.message;
            } else if (content.response) {
                contentStr = content.response;
            } else if (content.data && typeof content.data === 'string') {
                contentStr = content.data;
            } else {
                // Fallback to JSON representation for objects
                try {
                    contentStr = JSON.stringify(content, null, 2);
                } catch (e) {
                    contentStr = String(content);
                }
            }
        } else {
            contentStr = String(content);
        }
        
        // Basic HTML escaping
        const escaped = contentStr
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;');
        
        // Convert line breaks
        return escaped.replace(/\n/g, '<br>');
    }

    /**
     * Update streaming status
     */
    updateStreamingStatus(messageElement, status) {
        const statusElement = messageElement.parentElement.querySelector('.message-streaming');
        if (statusElement) {
            statusElement.textContent = `${status} ▋`;
        }
    }

    /**
     * Mark streaming as complete
     */
    markStreamingComplete(messageId) {
        const messageElement = document.querySelector(`[data-message-id="${messageId}"]`);
        if (messageElement) {
            const streamingElement = messageElement.querySelector('.message-streaming');
            if (streamingElement) {
                streamingElement.remove();
            }
        }
    }

    /**
     * Remove a message
     */
    removeMessage(container, messageId) {
        const messageElement = container.querySelector(`[data-message-id="${messageId}"]`);
        if (messageElement) {
            messageElement.remove();
        }
    }

    /**
     * Set form state (enabled/disabled)
     */
    setFormState(form, enabled) {
        const inputs = form.querySelectorAll('input, select, textarea, button');
        inputs.forEach(input => {
            input.disabled = !enabled;
        });
    }

    /**
     * Get or create conversation ID
     */
    getConversationId(mode) {
        const key = `conversation-${mode}`;
        if (!this.conversations.has(key)) {
            this.conversations.set(key, `conv-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`);
        }
        return this.conversations.get(key);
    }

    /**
     * Auto-resize textarea
     */
    autoResizeTextarea(event) {
        const textarea = event.target;
        textarea.style.height = 'auto';
        textarea.style.height = Math.min(textarea.scrollHeight, 200) + 'px';
    }

    /**
     * Handle input keydown (Ctrl+Enter to submit)
     */
    handleInputKeydown(event) {
        if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
            event.preventDefault();
            const form = event.target.closest('form');
            if (form) {
                form.dispatchEvent(new Event('submit', { bubbles: true }));
            }
        }
    }

    /**
     * Show error message
     */
    showError(message) {
        console.error('Chat Error:', message);
        // Could be enhanced with toast notifications
        // Temporarily disabled alert to prevent modal dialogs during debugging
        // alert(message);
    }
}

/**
 * Initialize chat functionality
 */
export function initChatForms() {
    console.log('Initializing enhanced chat with MCP integration...');
    
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            window.chatManager = new ChatManager();
        });
    } else {
        window.chatManager = new ChatManager();
    }
}

/**
 * Load chat providers (legacy function for compatibility)
 */
export function loadChatProviders() {
    // This is now handled by ChatManager constructor
    if (window.chatManager) {
        window.chatManager.loadProviders();
    }
}