// chat-core.js - Универсальное ядро чата
class ChatCore {
    constructor(config) {
        this.config = {
            API_URL: config.API_URL,
            API_TOKEN: config.API_TOKEN,
            API_AUTH_HEADER: config.API_AUTH_HEADER || 'Authorization',
            CSRF_NAME: config.CSRF_NAME || 'csrf_token',
            CSRF_TOKEN: config.CSRF_TOKEN || '',
            ALLOW_FILES: config.ALLOW_FILES || false,
            MAX_FILE_SIZE: config.MAX_FILE_SIZE || 10 * 1024 * 1024,
            MAX_TOTAL_SIZE: config.MAX_TOTAL_SIZE || 50 * 1024 * 1024,
            ALLOWED_FILE_TYPES: config.ALLOWED_FILE_TYPES || [],
            POLLING_INTERVAL: config.POLLING_INTERVAL || 2000,
            FETCH_TIMEOUT: config.FETCH_TIMEOUT || 10000,
            MAX_RETRIES: config.MAX_RETRIES || 3,
            ...config
        };
        
        this.currentTaskId = null;
        this.pollingInterval = null;
        this.isWaitingForResponse = false;
        this.lastStatusID = null;
        this.selectedFiles = [];
        this.fileObjectUrls = [];
        this.conversations = new Map(); // Хранилище диалогов
        this.currentConversationId = null;
        
        // Колбэки для UI
        this.callbacks = {
            onMessage: null,      // (role, content, files) => {}
            onStatusChange: null, // (waiting) => {}
            onConversationListUpdate: null, // (conversations) => {}
            onConversationChange: null, // (conversationId) => {}
            onFileListUpdate: null, // (files) => {}
            onUploadProgress: null, // (percent) => {}
            onConnectionChange: null, // (isOnline) => {}
            onVoiceInput: null,   // (text) => {}
            onError: null         // (error) => {}
        };
    }
    
    // Регистрация колбэков
    setCallbacks(callbacks) {
        Object.assign(this.callbacks, callbacks);
    }
    
    // Создание нового диалога
    createNewConversation(firstMessage = null) {
        const conversationId = this.generateId();
        const conversation = {
            id: conversationId,
            title: null,
            messages: [],
            createdAt: new Date(),
            updatedAt: new Date(),
            taskId: null
        };
        
        this.conversations.set(conversationId, conversation);
        this.switchToConversation(conversationId);
        
        if (firstMessage) {
            this.addMessage('user', firstMessage);
            this.sendMessage(firstMessage);
        }
        
        this.updateConversationsList();
        return conversationId;
    }
    
    // Переключение между диалогами
    switchToConversation(conversationId) {
        if (!this.conversations.has(conversationId)) {
            console.error('Conversation not found:', conversationId);
            return false;
        }
        
        // Останавливаем текущий опрос
        this.stopPolling();
        
        this.currentConversationId = conversationId;
        const conversation = this.conversations.get(conversationId);
        this.currentTaskId = conversation.taskId;
        
        // Очищаем UI и загружаем историю
        if (this.callbacks.onConversationChange) {
            this.callbacks.onConversationChange(conversationId, conversation.messages);
        }
        
        return true;
    }
    
    // Установка заголовка диалога (из первого сообщения)
    setConversationTitle(conversationId, message) {
        const conversation = this.conversations.get(conversationId);
        if (conversation && !conversation.title) {
            const title = message.length > 30 ? message.substring(0, 30) + '...' : message;
            conversation.title = title;
            this.updateConversationsList();
        }
    }
    
    // Добавление сообщения в диалог
    addMessage(role, content, files = null, isHistory = false) {
        const conversation = this.conversations.get(this.currentConversationId);
        if (!conversation) return;
        
        const message = {
            id: Date.now(),
            role: role,
            content: content,
            files: files,
            timestamp: new Date()
        };
        
        conversation.messages.push(message);
        conversation.updatedAt = new Date();
        
        if (!isHistory && this.callbacks.onMessage) {
            this.callbacks.onMessage(role, content, files);
        }
        
        return message;
    }
    
    // Отправка сообщения
    async sendMessage(text, files = null) {
        if ((!text && (!files || files.length === 0)) || !this.currentTaskId) {
            return false;
        }
        
        // Валидация файлов
        if (files && files.length > 0) {
            const validation = this.validateFiles(files);
            if (!validation.valid) {
                if (this.callbacks.onError) {
                    this.callbacks.onError(validation.errors.join('\n'));
                }
                return false;
            }
        }
        
        // Добавляем сообщение пользователя
        const messageFiles = files ? files.map(f => ({
            name: f.name,
            size: f.size,
            type: f.type,
            objectUrl: URL.createObjectURL(f)
        })) : null;
        
        this.addMessage('user', text, messageFiles);
        
        // Устанавливаем заголовок диалога из первого сообщения
        const conversation = this.conversations.get(this.currentConversationId);
        if (conversation && !conversation.title && text) {
            this.setConversationTitle(this.currentConversationId, text);
        }
        
        // Очищаем выбранные файлы
        if (files) {
            this.selectedFiles = [];
            if (this.callbacks.onFileListUpdate) {
                this.callbacks.onFileListUpdate([]);
            }
        }
        
        this.setWaitingState(true);
        
        // Отправляем на сервер
        const formData = new FormData();
        formData.append('role', 'user');
        formData.append('content', text || '');
        formData.append(this.config.CSRF_NAME, this.config.CSRF_TOKEN);
        
        if (files) {
            files.forEach(file => {
                formData.append('files[]', file);
            });
        }
        
        return this.sendWithProgress(formData);
    }
    
    // Отправка с прогрессом
    sendWithProgress(formData) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            
            xhr.upload.onprogress = (e) => {
                if (e.lengthComputable && this.callbacks.onUploadProgress) {
                    const percent = (e.loaded / e.total) * 100;
                    this.callbacks.onUploadProgress(percent);
                }
            };
            
            xhr.onload = () => {
                if (this.callbacks.onUploadProgress) {
                    this.callbacks.onUploadProgress(0);
                }
                
                if (xhr.status >= 200 && xhr.status < 300) {
                    const data = JSON.parse(xhr.responseText);
                    if (data.status === 'accepted') {
                        this.startPolling(this.currentTaskId);
                        resolve(data);
                    }
                } else {
                    const error = new Error(`HTTP error! status: ${xhr.status}`);
                    this.setWaitingState(false);
                    reject(error);
                }
            };
            
            xhr.onerror = () => {
                if (this.callbacks.onUploadProgress) {
                    this.callbacks.onUploadProgress(0);
                }
                const error = new Error('Network error');
                this.setWaitingState(false);
                reject(error);
            };
            
            xhr.open('POST', this.config.API_URL + `${this.currentTaskId}/`);
            xhr.setRequestHeader(this.config.API_AUTH_HEADER, this.config.API_TOKEN);
            xhr.send(formData);
        });
    }
    
    // Опрос сервера
    startPolling(taskId) {
        this.stopPolling();
        
        this.pollingInterval = setInterval(() => {
            if (!taskId) {
                this.stopPolling();
                return;
            }
            
            this.fetchWithRetry(this.config.API_URL + `${taskId}/`, { method: 'GET' })
                .then(async response => {
                    if (!response.ok) {
                        throw new Error(`HTTP error! status: ${response.status}`);
                    }
                    return response.json();
                })
                .then(data => {
                    if (!data.status) return;
                    
                    const status = data.status;
                    const message = data.message;
                    const changed = data.changed;
                    
                    if (changed !== this.lastStatusID) {
                        this.handleStatusUpdate(status, message);
                        this.lastStatusID = changed;
                    }
                })
                .catch(error => {
                    console.error('Polling error:', error);
                    if (this.callbacks.onError) {
                        this.callbacks.onError('Connection error: ' + error.message);
                    }
                });
        }, this.config.POLLING_INTERVAL);
    }
    
    // Обработка статусов
    handleStatusUpdate(status, message) {
        switch (status) {
            case 'assistant':
            case 'result':
                this.addMessage('assistant', message);
                this.setWaitingState(false);
                this.stopPolling();
                break;
                
            case 'error':
                this.addMessage('error', message);
                this.setWaitingState(false);
                this.stopPolling();
                break;
                
            default:
                // Для промежуточных статусов показываем индикацию
                if (this.callbacks.onStatusChange) {
                    this.callbacks.onStatusChange(status);
                }
                break;
        }
    }
    
    // Валидация файлов
    validateFiles(files) {
        const errors = [];
        let totalSize = 0;
        
        for (let file of files) {
            if (file.size > this.config.MAX_FILE_SIZE) {
                errors.push(`File "${file.name}" exceeds max size`);
            }
            
            if (this.config.ALLOWED_FILE_TYPES.length > 0 && 
                !this.config.ALLOWED_FILE_TYPES.includes(file.type) && 
                file.type !== '') {
                errors.push(`File type not allowed: ${file.name}`);
            }
            
            totalSize += file.size;
        }
        
        if (totalSize > this.config.MAX_TOTAL_SIZE) {
            errors.push(`Total size exceeds limit`);
        }
        
        return {
            valid: errors.length === 0,
            errors,
            totalSize
        };
    }
    
    // Fetch с ретраями
    async fetchWithRetry(url, options, retryCount = 0) {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), this.config.FETCH_TIMEOUT);
        
        try {
            const response = await fetch(url, {
                ...options,
                signal: controller.signal,
                headers: {
                    [this.config.API_AUTH_HEADER]: this.config.API_TOKEN,
                    ...options.headers
                }
            });
            clearTimeout(timeoutId);
            return response;
        } catch (error) {
            clearTimeout(timeoutId);
            
            if (error.name === 'AbortError') {
                throw new Error('Request timeout');
            }
            
            if (retryCount < this.config.MAX_RETRIES) {
                const delay = Math.min(1000 * Math.pow(2, retryCount), 10000);
                await new Promise(r => setTimeout(r, delay));
                return this.fetchWithRetry(url, options, retryCount + 1);
            }
            
            throw error;
        }
    }
    
    // Управление состоянием ожидания
    setWaitingState(waiting) {
        this.isWaitingForResponse = waiting;
        if (this.callbacks.onStatusChange) {
            this.callbacks.onStatusChange(waiting);
        }
    }
    
    // Остановка опроса
    stopPolling() {
        if (this.pollingInterval) {
            clearInterval(this.pollingInterval);
            this.pollingInterval = null;
        }
    }
    
    // Создание ID
    generateId() {
        return 'conv_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
    }
    
    // Обновление списка диалогов
    updateConversationsList() {
        const conversations = Array.from(this.conversations.values())
            .sort((a, b) => b.updatedAt - a.updatedAt);
        
        if (this.callbacks.onConversationListUpdate) {
            this.callbacks.onConversationListUpdate(conversations);
        }
    }
    
    // Загрузка диалогов с сервера
    async loadConversations(conversationsData) {
        for (const conv of conversationsData) {
            const conversation = {
                id: conv.id,
                title: conv.title,
                messages: conv.messages || [],
                createdAt: new Date(conv.createdAt),
                updatedAt: new Date(conv.updatedAt),
                taskId: conv.taskId
            };
            this.conversations.set(conv.id, conversation);
        }
        this.updateConversationsList();
        
        if (conversationsData.length > 0) {
            this.switchToConversation(conversationsData[0].id);
        }
    }
    
    // Создание задачи на сервере
    async createTask() {
        const taskId = this.generateId();
        this.currentTaskId = taskId;
        
        const conversation = this.conversations.get(this.currentConversationId);
        if (conversation) {
            conversation.taskId = taskId;
        }
        
        return taskId;
    }
    
    // Очистка ресурсов
    destroy() {
        this.stopPolling();
        this.fileObjectUrls.forEach(url => URL.revokeObjectURL(url));
        this.fileObjectUrls = [];
    }
}

// Экспорт для использования
if (typeof module !== 'undefined' && module.exports) {
    module.exports = ChatCore;
}