/**
 * Расширяет ссылки на файлы и промпты, заменяя их содержимым перед отправкой на сервер.
 * Обрабатывает как одинарные (@/#), так и постоянные (@@/##) ссылки.
 */

import { TFile } from 'obsidian';
import LLMAgentPlugin from 'main';
import { FileReference, ReferenceParser } from './ReferenceParser';
import { TemplateEngine } from './TemplateEngine';

// ----------------------------------------------- Reference Expander ---------------------------------------------------------------

export class ReferenceExpander {
    private plugin: LLMAgentPlugin;
    private templateEngine: TemplateEngine;

    constructor(plugin: LLMAgentPlugin, templateEngine: TemplateEngine) {
        this.plugin = plugin;
        this.templateEngine = templateEngine;
    }

    // ----------------------------------------------- Public Methods ---------------------------------------------------------------

    /**
     * Заменяет все ссылки на содержимое файлов (перед отправкой на сервер)
     * @param text - текст с ссылками
     * @param references - массив ссылок для обработки
     * @returns текст с развернутыми ссылками
     */
    async expandReferences(text: string, references: FileReference[]): Promise<string> {
        if (references.length === 0) {
            return text;
        }

        let expandedText = text;
        
        // Обрабатываем ссылки от конца к началу, чтобы не сбивать индексы
        const sortedReferences = [...references].sort((a, b) => {
            const aMatches = ReferenceParser.parseReferences(text).filter(m => m.name === a.name);
            const bMatches = ReferenceParser.parseReferences(text).filter(m => m.name === b.name);
            const aIndex = aMatches.length > 0 ? aMatches[aMatches.length - 1].startIndex : 0;
            const bIndex = bMatches.length > 0 ? bMatches[bMatches.length - 1].startIndex : 0;
            return bIndex - aIndex;
        });

        for (const reference of sortedReferences) {
            expandedText = await this.expandSingleReference(expandedText, reference);
        }

        return expandedText;
    }

    /**
     * Обрабатывает историю сообщений, раскрывая постоянные ссылки (@@/##)
     * @param messages - массив сообщений
     * @returns обработанные сообщения
     */
    async expandMessageHistory(messages: any[]): Promise<any[]> {
        const expandedMessages = [];
        
        for (const message of messages) {
            if (message.role === 'user') {
                const expandedContent = await this.expandPermanentReferences(message.content);
                expandedMessages.push({
                    ...message,
                    content: expandedContent
                });
            } else {
                expandedMessages.push(message);
            }
        }
        
        return expandedMessages;
    }

    // TODO: При реализации снапшотов - сохранять содержимое файлов
    // async expandReferencesWithSnapshots(text: string, references: FileReference[], messageId: string): Promise<{expandedText: string, snapshots: Record<string, string>}> {
    //     const snapshots: Record<string, string> = {};
    //     let expandedText = text;
    //     
    //     for (const reference of references) {
    //         const content = await this.loadFileContent(reference);
    //         if (content !== null) {
    //             snapshots[reference.path] = content;
    //             expandedText = await this.expandSingleReference(expandedText, reference, content);
    //         }
    //     }
    //     
    //     return { expandedText, snapshots };
    // }

    // ----------------------------------------------- Private Methods ---------------------------------------------------------------

    /**
     * Раскрывает одну ссылку в тексте
     * @param text - текст для обработки
     * @param reference - ссылка для раскрытия
     * @param preloadedContent - предзагруженное содержимое (для снапшотов)
     * @returns текст с раскрытой ссылкой
     */
    private async expandSingleReference(text: string, reference: FileReference, preloadedContent?: string): Promise<string> {
        const content = preloadedContent || await this.loadFileContent(reference);
        
        if (content === null) {
            console.warn(`Не удалось загрузить содержимое для ссылки: ${reference.name}`);
            return text;
        }

        // Определяем паттерн для замены
        const prefix = reference.permanent 
            ? (reference.type === 'file' ? '@@' : '##')
            : (reference.type === 'file' ? '@' : '#');
        
        // Создаем регулярное выражение для поиска ссылки
        const escapedName = this.escapeRegExp(reference.name);
        const pattern = new RegExp(`${this.escapeRegExp(prefix)}(?:"${escapedName}"|${escapedName})(?=\\s|$|[^\\w-])`, 'g');
        
        // Форматируем содержимое для вставки
        const formattedContent = this.formatContentForInsertion(content, reference);
        
        return text.replace(pattern, formattedContent);
    }

    /**
     * Раскрывает только постоянные ссылки (@@/##) в тексте
     * @param text - текст для обработки
     * @returns текст с раскрытыми постоянными ссылками
     */
    private async expandPermanentReferences(text: string): Promise<string> {
        const matches = ReferenceParser.parseReferences(text);
        const permanentMatches = matches.filter(match => match.prefix.length === 2); // @@ или ##
        
        if (permanentMatches.length === 0) {
            return text;
        }

        let expandedText = text;
        
        // Обрабатываем ссылки от конца к началу
        const sortedMatches = [...permanentMatches].sort((a, b) => b.startIndex - a.startIndex);
        
        for (const match of sortedMatches) {
            const reference: FileReference = {
                type: match.type,
                name: match.name,
                path: await this.resolveFilePath(match.name, match.type),
                permanent: true
            };
            
            const content = await this.loadFileContent(reference);
            if (content !== null) {
                const formattedContent = this.formatContentForInsertion(content, reference);
                expandedText = expandedText.substring(0, match.startIndex) + 
                              formattedContent + 
                              expandedText.substring(match.endIndex);
            }
        }
        
        return expandedText;
    }

    /**
     * Загружает содержимое файла
     * @param reference - ссылка на файл
     * @returns содержимое файла или null если не найден
     */
    private async loadFileContent(reference: FileReference): Promise<string | null> {
        try {
            const file = this.findFile(reference.path, reference.type);
            if (!file) {
                return null;
            }

            const content = await this.plugin.app.vault.read(file);
            
            // Для промптов обрабатываем шаблоны
            if (reference.type === 'prompt') {
                return await this.templateEngine.expandPromptTemplate(content);
            }
            
            return content;
        } catch (error) {
            console.error(`Ошибка загрузки файла ${reference.path}:`, error);
            return null;
        }
    }

    /**
     * Находит файл по пути
     * @param path - путь к файлу
     * @param type - тип файла
     * @returns файл или null
     */
    private findFile(path: string, type: 'file' | 'prompt'): TFile | null {
        let file = this.plugin.app.vault.getAbstractFileByPath(path);
        
        if (!file && type === 'prompt') {
            // Попробуем найти в папке промптов
            const promptsFolder = this.plugin.settings.promptsFolder;
            if (promptsFolder) {
                const fullPath = `${promptsFolder}/${path}`;
                file = this.plugin.app.vault.getAbstractFileByPath(fullPath);
            }
        }
        
        return (file instanceof TFile) ? file : null;
    }

    /**
     * Разрешает полный путь к файлу по имени
     * @param name - имя файла
     * @param type - тип файла
     * @returns полный путь к файлу
     */
    private async resolveFilePath(name: string, type: 'file' | 'prompt'): Promise<string> {
        if (type === 'prompt') {
            const promptsFolder = this.plugin.settings.promptsFolder;
            return promptsFolder ? `${promptsFolder}/${name}.md` : `${name}.md`;
        } else {
            // Ищем файл в хранилище
            const files = this.plugin.app.vault.getMarkdownFiles();
            const matchingFile = files.find(f => f.basename === name);
            return matchingFile ? matchingFile.path : `${name}.md`;
        }
    }

    /**
     * Форматирует содержимое для вставки в сообщение
     * @param content - содержимое файла
     * @param reference - информация о ссылке
     * @returns отформатированное содержимое
     */
    private formatContentForInsertion(content: string, reference: FileReference): string {
        // Добавляем заголовок с информацией о файле
        const header = `\n\n--- Содержимое ${reference.type === 'prompt' ? 'промпта' : 'файла'}: ${reference.name} ---\n`;
        const footer = `\n--- Конец ${reference.type === 'prompt' ? 'промпта' : 'файла'}: ${reference.name} ---\n\n`;
        
        return header + content.trim() + footer;
    }

    /**
     * Экранирует специальные символы для регулярного выражения
     * @param string - строка для экранирования
     * @returns экранированная строка
     */
    private escapeRegExp(string: string): string {
        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }
}