/*
 * ChatView управляет пользовательским интерфейсом чата, отображает историю сообщений,
 * отправляет запросы LLM-агенту и обрабатывает ответы, обновляя состояние чата и графа.
 */

import { ItemView, WorkspaceLeaf } from 'obsidian';
import { ChatPanel } from '../components/ChatPanel';
import LLMAgentPlugin from 'main';

export const CHAT_VIEW_TYPE = 'llm-agent-chat-view';

// ----------------------------------------------- Chat View ---------------------------------------------------------------

export class ChatView extends ItemView {
	plugin: LLMAgentPlugin;
	chatHistory: any[];
	selectedGraphId: string | null;
	currentNode: string | null;
	chatContainer: HTMLDivElement;
	chatPanel: ChatPanel | null;
	availableModels: string[];
	// TODO: сделать аккуратнее - добавлен таймер для обновления заголовков
	titleUpdateTimer: NodeJS.Timeout | null;

	constructor(leaf: WorkspaceLeaf, plugin: LLMAgentPlugin) {
		super(leaf);
		this.plugin = plugin;
		this.chatHistory = [];
		this.selectedGraphId = null;
		this.currentNode = null;
		this.chatPanel = null;
		this.availableModels = [];
		this.titleUpdateTimer = null;
	}

	getViewType(): string {
		return CHAT_VIEW_TYPE;
	}

	getDisplayText(): string {
		return 'LLM Agent Chat';
	}

	getIcon(): string {
		return 'message-circle';
	}

	async onOpen(): Promise<void> {
		const container = this.containerEl.children[1] as HTMLElement;
		container.empty();

		// Создаем только контейнер для чата (без истории)
		this.chatContainer = container.createDiv() as HTMLDivElement;
		this.chatContainer.addClass('llm-agent-chat-container');

		// Загружаем доступные модели
		await this.fetchAvailableModels();

		// Инициализируем только чат панель с передачей plugin
		this.chatPanel = new ChatPanel(this.chatContainer, {
			chatHistory: this.chatHistory,
			onSendMessage: this.handleSendMessage.bind(this),
			availableModels: this.availableModels,
			selectedModel: this.plugin.settings.defaultModel || 'gemini-2.5-flash',
			plugin: this.plugin // Добавляем передачу plugin
		});

		// Остальной код без изменений...
		this.plugin.eventBus.on('node-selected', this.handleNodeSelected.bind(this));
		this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this));
		this.plugin.eventBus.on('graph-selected', this.handleGraphSelected.bind(this));
	}
	async onClose(): Promise<void> {
		if (this.chatPanel) {
			this.chatPanel.destroy();
			this.chatPanel = null;
		}
		this.plugin.eventBus.off('node-selected', this.handleNodeSelected);
		this.plugin.eventBus.off('new-chat', this.handleNewChat);
		this.plugin.eventBus.off('graph-selected', this.handleGraphSelected);
	}

	// ----------------------------------------------- API Methods ---------------------------------------------------------------

	// TODO: проблемное место
	/**
	 * Загружает историю сообщений от корня до указанного узла графа.
	 * Обновляет `chatHistory` и отображение в `chatPanel`.
	 * @param {string} nodeId - ID конечного узла для загрузки истории.
	 * @returns {Promise<void>}
	 */
	async fetchMessagesFromRootToNode(nodeId: string | null): Promise<void> {
		try {
			// Убедимся, что selectedGraphId установлен
			if (!this.selectedGraphId) {
				console.warn('selectedGraphId не установлен. Невозможно загрузить сообщения.');
				return;
			}

			const url = nodeId
				? `${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/${nodeId}`
				: `${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/last`;
			const response = await fetch(url);

			if (!response.ok) {
				const errorText = await response.text();
				throw new Error(`Ошибка загрузки сообщений: ${response.status} - ${errorText}`);
			}
			const data = await response.json();
			// ИСПРАВЛЕНО: Убедиться, что type передается, если это возможно
			this.chatHistory =
				data.map((msg: any) => ({
					...msg,
					type: msg.type || msg.role // Используем type, если есть, иначе role как fallback
				})) || [];
			if (this.chatPanel) {
				await this.chatPanel.updateHistory(this.chatHistory);
			}
		} catch (error) {
			console.error('Ошибка загрузки сообщений:', error);
			// Optionally: show a notice to the user
			// new Notice(`Ошибка загрузки сообщений: ${error.message}`);
		}
	}

	/**
	 * Загружает список доступных моделей с бэкенда.
	 * @returns {Promise<void>}
	 */
	async fetchAvailableModels(): Promise<void> {
		try {
			const response = await fetch(`${this.plugin.settings.apiBaseUrl}/models`);
			if (!response.ok) {
				throw new Error(`Ошибка загрузки моделей: ${response.status}`);
			}
			this.availableModels = await response.json();
		} catch (error) {
			console.error('Ошибка загрузки доступных моделей:', error);
			// Fallback к базовым моделям
			this.availableModels = ['gemini-2.5-flash', 'gpt-4o-mini', 'claude-3-5-sonnet-20241022'];
		}
	}

	// ----------------------------------------------- Event Handlers ---------------------------------------------------------------

	async handleSendMessage(message: string, selectedModel?: string): Promise<void> {
		try {
			const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat`, {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json'
				},
				body: JSON.stringify({
					message: message,
					graph_id: this.selectedGraphId,
					parent_node_id: this.currentNode,
					system_prompt: this.plugin.settings.systemPrompt,
					model: selectedModel || 'gemini-2.5-flash'
				})
			});

			if (!response.ok) {
				// Пытаемся прочитать ошибку из тела ответа, если это возможно
				const errorData = await response.json().catch(() => ({ message: response.statusText }));
				throw new Error(`HTTP ошибка при отправке сообщения! Статус: ${response.status} - ${errorData.message}`);
			}

			const result = await response.json();

			// Обновляем graph_id и current_node_id всегда
			this.selectedGraphId = result.graph_id;
			const newCurrentNodeId = result.graph_visualization_data.current_node_id;
			this.currentNode = newCurrentNodeId;

			// Сначала обновим GraphView, чтобы он запросил последнюю версию графа
			// Это предотвратит состояние гонки, когда разные запросы возвращают частичные графы.
			// Вместо того, чтобы отправлять частичные данные, мы запрашиваем полный граф у бэкенда.
			this.plugin.eventBus.emit('graph-selected', { graphId: this.selectedGraphId, currentNode: this.currentNode });

			// TODO: сделать аккуратнее - устанавливаем таймер на 30 секунд для обновления заголовков
			this.titleUpdateTimer = setTimeout(() => {
				console.log('Обновление графа для получения сгенерированных заголовков...');
				if (this.selectedGraphId && this.currentNode) {
					this.plugin.eventBus.emit('graph-selected', {
						graphId: this.selectedGraphId,
						currentNode: this.currentNode
					});
				}
				this.titleUpdateTimer = null;
			}, 20000); // 20 секунд, эмпирически

			// Затем обновим ChatView, загрузив историю до нового current_node_id
			if (newCurrentNodeId) {
				await this.fetchMessagesFromRootToNode(newCurrentNodeId);
			} else if (this.selectedGraphId) {
				// Если newCurrentNodeId по какой-то причине не вернулся, но graph_id есть,
				// пытаемся загрузить историю по последнему известному узлу или просто весь граф (чтобы отобразить пустую историю, если граф новый).
				// В этом случае, если current_node_id отсутствует, historyPanel должна очиститься.
				this.currentNode = null; // Сбрасываем, если no current_node_id
				this.fetchMessagesFromRootToNode(''); // Передаем пустую строку или null, чтобы очистить историю
			}
		} catch (error) {
			console.error('Ошибка отправки сообщения:', error);
		}
	}

	/**
	 * Обрабатывает выбор узла на графе. Загружает историю сообщений до выбранного узла
	 * и обновляет панель чата.
	 * @param {{ nodeId: string }} data - Объект, содержащий ID выбранного узла.
	 */
	handleNodeSelected(data: { nodeId: string }): void {
		this.currentNode = data.nodeId;
		if (this.selectedGraphId) {
			this.fetchMessagesFromRootToNode(data.nodeId);
		}
	}

	/**
	 * Обрабатывает событие начала нового чата. Сбрасывает текущее состояние чата
	 * и очищает отображение истории.
	 */
	async handleNewChat(): Promise<void> {
		this.chatHistory = [];
		this.currentNode = null;
		this.selectedGraphId = null;
		if (this.chatPanel) {
			await this.chatPanel.updateHistory(this.chatHistory);
		}
	}

	/**
	 * Обрабатывает выбор графа из `GraphView`. Устанавливает выбранный граф и обновляет
	 * историю чата, загружая сообщения, относящиеся к этому графу.
	 * @param {{ graphId: string; currentNode: string | null }} data - Объект, содержащий ID выбранного графа и текущий узел.
	 * @returns {Promise<void>}
	 */
	async handleGraphSelected(data: { graphId: string; currentNode: string | null }): Promise<void> {
		this.selectedGraphId = data.graphId;
		this.currentNode = data.currentNode; // Может быть пустым, но это не проблема
		await this.fetchMessagesFromRootToNode(data.currentNode);
	}
}
