/**
 * Парсер для обработки ссылок на файлы (@/@@) и промпты (#/##) в тексте сообщений.
 * Поддерживает поиск активных ссылок при вводе и валидацию промптов.
 */

// ----------------------------------------------- Interfaces ---------------------------------------------------------------

export interface ReferenceMatch {
	fullMatch: string; // "@filename" или "@@filename"
	prefix: string; // "@" или "@@"
	name: string; // "filename"
	startIndex: number;
	endIndex: number;
	type: 'file' | 'prompt';
}

export interface FileReference {
	type: 'file' | 'prompt';
	name: string;
	path: string;
	permanent: boolean; // true для @@ и ##
	// TODO: добавить snapshotId при реализации снапшотов
}

// ----------------------------------------------- Reference Parser ---------------------------------------------------------------

export class ReferenceParser {
	/**
	 * Парсит текст и находит все @/@@ и #/## ссылки
	 * @param text - текст для парсинга
	 * @returns массив найденных ссылок
	 */
	static parseReferences(text: string): ReferenceMatch[] {
		const matches: ReferenceMatch[] = [];

		// Регулярное выражение для поиска @/@@ и #/## ссылок
		// Поддерживает имена файлов с пробелами, заключенные в кавычки или без пробелов
		const regex = /(@{1,2}|#{1,2})(?:"([^"]+)"|([^\s@#"]+))/g;

		let match;
		while ((match = regex.exec(text)) !== null) {
			const prefix = match[1];
			const quotedName = match[2];
			const unquotedName = match[3];
			const name = quotedName || unquotedName;

			matches.push({
				fullMatch: match[0],
				prefix: prefix,
				name: name,
				startIndex: match.index,
				endIndex: match.index + match[0].length,
				type: prefix.startsWith('@') ? 'file' : 'prompt'
			});
		}

		return matches;
	}

	/**
	 * Проверяет, является ли позиция курсора внутри незавершённой ссылки
	 * @param text - текст сообщения
	 * @param cursorPosition - позиция курсора
	 * @returns информация о текущей ссылке или null
	 */
	static getCurrentReference(text: string, cursorPosition: number): ReferenceMatch | null {
		// Ищем незавершенные ссылки перед курсором
		const beforeCursor = text.substring(0, cursorPosition);

		// Регулярное выражение для поиска начала ссылки без завершения
		const incompleteRegex = /(@{1,2}|#{1,2})([^\s@#"]*?)$/;
		const match = beforeCursor.match(incompleteRegex);

		if (match) {
			const prefix = match[1];
			const partialName = match[2];
			const startIndex = beforeCursor.length - match[0].length;

			return {
				fullMatch: match[0],
				prefix: prefix,
				name: partialName,
				startIndex: startIndex,
				endIndex: cursorPosition,
				type: prefix.startsWith('@') ? 'file' : 'prompt'
			};
		}

		return null;
	}

	/**
	 * Валидирует промпт на корректность @{...} ссылок
	 * @param promptContent - содержимое промпта
	 * @returns результат валидации
	 */
	static validatePromptReferences(promptContent: string): { valid: boolean; errors: string[] } {
		const errors: string[] = [];

		// Ищем шаблонные ссылки @{...}
		const templateRegex = /@\{([^}]+)\}/g;
		const matches = promptContent.matchAll(templateRegex);

		for (const match of matches) {
			const referenceName = match[1].trim();

			// Проверяем, что имя ссылки не пустое
			if (!referenceName) {
				errors.push(`Пустая ссылка в позиции ${match.index}`);
				continue;
			}

			// Проверяем, что имя содержит только допустимые символы
			if (!/^[a-zA-Z0-9_-]+$/.test(referenceName)) {
				errors.push(`Недопустимые символы в ссылке "${referenceName}"`);
			}
		}

		return {
			valid: errors.length === 0,
			errors: errors
		};
	}

	/**
	 * Извлекает все шаблонные ссылки из промпта, основываясь на заданных префиксах.
	 * По умолчанию ищет ссылки с префиксом @{...}.
	 * @param promptContent - содержимое промпта
	 * @param prefixes - массив префиксов, по которым нужно искать ссылки (например, ['@'], ['#'], ['@', '#']). По умолчанию ['@'].
	 * @returns массив имен ссылок
	 */
	static extractTemplateReferences(promptContent: string, prefixes: Array<'@' | '#'> = ['@']): string[] {
		const references: string[] = [];
		let prefixRegexPart: string;

		if (prefixes.includes('@') && prefixes.includes('#')) {
			prefixRegexPart = '[#@]'; // Ищет как @, так и #
		} else if (prefixes.includes('@')) {
			prefixRegexPart = '@'; // Ищет только @
		} else if (prefixes.includes('#')) {
			prefixRegexPart = '#'; // Ищет только #
		} else {
			// Если пустой массив или неподдерживаемые префиксы, не ищем ничего
			return [];
		}

		// Регулярное выражение для поиска ссылок с динамическим префиксом
		const templateRegex = new RegExp(`${prefixRegexPart}\\{([^}]+)\\}`, 'g');
		const matches = promptContent.matchAll(templateRegex);

		for (const match of matches) {
			const referenceName = match[1].trim();
			if (referenceName && !references.includes(referenceName)) {
				references.push(referenceName);
			}
		}

		return references;
	}
}
