/**
 * Этот файл содержит React компонент GraphPanelComponent, предназначенный для визуализации
 * графов диалогов. Он использует библиотеку React Flow для интерактивного отображения
 * узлов и рёбер, а также библиотеку Dagre для автоматического расположения элементов графа.
 * Включает кастомные типы узлов для различных сущностей графа.
 */

// ----------------------------------------------- React & React Flow Imports ---------------------------------------------------
import * as React from 'react';
import * as ReactDOM from 'react-dom/client'; // <-- ИСПРАВЛЕНО: для createRoot нужен 'react-dom/client'
import ReactFlow, {
	addEdge,
	useNodesState,
	useEdgesState,
	Background,
	Controls,
	ReactFlowProvider,
	Connection,
	Edge,
	BackgroundVariant
} from 'reactflow';
import { App } from 'obsidian';
import { Dialog } from 'src/utils/Dialog';

// <-- ИСПРАВЛЕНО: Это ключевой момент. Убедитесь, что esbuild настроен на обработку CSS импортов.
// Обычно, при использовании esbuild для React-проектов, вы импортируете CSS прямо в JS/TS файл.
// esbuild должен быть настроен для копирования или встраивания этого CSS.
// Если стили не применяются, проверьте конфигурацию esbuild (например, через плагин esbuild-plugin-css-bundle).
import 'reactflow/dist/style.css'; // Импортируем стили ReactFlow

// Если вы используете какие-либо кастомные ноды, импортируйте их здесь
// import CustomNode from './CustomNode';
// const nodeTypes = { custom: CustomNode };

// ----------------------------------------------- Custom Nodes -----------------------------------------------------------------

import { Handle, Position } from 'reactflow';

/**
 * Интерфейс, описывающий структуру данных, передаваемых в BaseNodeWrapper.
 */
interface BaseNodeWrapperData {
	label: string;
	title?: string;
	title_generated?: boolean;
	isSelected: boolean;
	nodeId: string;
	graphId: string;
	onNodeDelete: (nodeId: string) => Promise<void>;
	app: App;
}

/**
 * Интерфейс, описывающий свойства компонента BaseNodeWrapper.
 */
interface BaseNodeWrapperProps {
	data: BaseNodeWrapperData;
	isConnectable: boolean;
	// children: React.ReactNode; // Содержимое, специфичное для типа узла (например, метка)
}

/**
 * BaseNodeWrapper - Общая обёртка для всех кастомных узлов,
 * предоставляющая хэндлы для соединений и функциональность удаления.
 * @param {BaseNodeWrapperProps} props - Свойства компонента.
 * @returns {React.ReactElement} Элемент React.
 */
const BaseNodeWrapper: React.FC<BaseNodeWrapperProps> = ({ data, isConnectable, children }) => {
	// ----------------------------------------------- Styles --------------------------------------------------------------------
	// Стили для кнопки удаления. Вынесены для переиспользования.
	const deleteButtonStyle: React.CSSProperties = {
		position: 'absolute',
		bottom: '-30px', // Размещаем под узлом
		left: '5px',
		background: 'transparent',
		border: 'none',
		cursor: 'pointer',
		fontSize: '1em',
		color: 'var(--text-muted)', // Используем цвет текста Obsidian
		padding: '0 5px',
		opacity: data.isSelected ? 1 : 0, // Показываем только если узел выделен
		transition: 'opacity 0.2s ease-in-out'
	};

	// ----------------------------------------------- Event Handlers ------------------------------------------------------------
	/**
	 * Обработчик удаления узла.
	 * @param {React.MouseEvent} event - Событие клика.
	 */
	const handleNodeDelete = async (event: React.MouseEvent) => {
		event.stopPropagation(); // Предотвращаем срабатывание onNodeClick на родительском узле

		const confirmed = await Dialog.confirm(data.app, 'Вы уверены, что хотите удалить этот узел?');
		if (confirmed) {
			await data.onNodeDelete(data.nodeId);
		}
	};

    const handleRegenerate = async (event: React.MouseEvent) => {
        event.stopPropagation();
        // Используем EventBus вместо workspace.trigger
        const eventBus = (data.app as any).plugins?.plugins?.['llm-agent-plugin']?.eventBus;
        if (eventBus) {
            eventBus.emit('regenerate-message', { 
                graphId: data.graphId, 
                nodeId: data.nodeId 
            });
        }
    };
	// ----------------------------------------------- Render --------------------------------------------------------------------
	return React.createElement(
		React.Fragment,
		null,
		React.createElement(Handle, {
			type: 'source',
			position: Position.Bottom,
			isConnectable: isConnectable
		}),
		React.createElement(Handle, {
			type: 'target',
			position: Position.Top,
			isConnectable: isConnectable
		}),
		children, // Здесь будет отображаться специфическое содержимое узла
        data.isSelected && React.createElement('div', {
            style: {
                position: 'absolute',
                bottom: '-30px',
                left: '5px',
                display: 'flex',
                gap: '5px'
            }
        },
            React.createElement('button', {
                style: deleteButtonStyle,
                onClick: handleNodeDelete,
                'aria-label': 'Удалить узел',
                className: 'node-delete-button'
            }, '🗑️'),
            React.createElement('button', {
                style: deleteButtonStyle,
                onClick: handleRegenerate,
                'aria-label': 'Регенерировать',
                className: 'node-regenerate-button'
            }, '🔄')
        )
    );
};

// Обновляем CustomNode, чтобы включить кнопку удаления
// Простой компонент для узла типа 'user'
const UserNode: React.FC<any> = ({ data, isConnectable }) => {
	const displayLabel = data.title || data.label || 'Пользователь';
	// ... (titleGenerated больше не нужен в каждом узле, если не используется для рендеринга)

	return React.createElement(
		BaseNodeWrapper,
		{ data: data, isConnectable: isConnectable },
		React.createElement('div', null, displayLabel)
	);
};

// Обновите другие типы узлов (LLMNode, ToolNode, ErrorNode) аналогично
const LLMNode: React.FC<any> = ({ data, isConnectable }) => {
	const displayLabel = data.title || data.label || 'LLM';
	// ...

	return React.createElement(
		BaseNodeWrapper,
		{ data: data, isConnectable: isConnectable },
		React.createElement('div', null, displayLabel)
	);
};

// Простой компонент для узла типа 'tool'
const ToolNode: React.FC<any> = ({ data, isConnectable }) => {
	const displayLabel = data.title || data.label || 'Инструмент';
	// ...

	return React.createElement(
		BaseNodeWrapper,
		{ data: data, isConnectable: isConnectable },
		React.createElement('div', null, displayLabel)
	);
};

// Простой компонент для узла типа 'error'
const ErrorNode: React.FC<any> = ({ data, isConnectable }) => {
	const displayLabel = data.title || data.label || 'Ошибка';
	// ...

	return React.createElement(
		BaseNodeWrapper,
		{ data: data, isConnectable: isConnectable },
		React.createElement('div', null, displayLabel)
	);
};

// Регистрируем кастомные типы узлов
const nodeTypes = {
	user: UserNode,
	llm: LLMNode,
	tool: ToolNode,
	error: ErrorNode
	// Если у тебя есть node.type === 'default', то можешь его тоже определить,
	// либо использовать встроенный 'default' React Flow.
};

// ----------------------------------------------- Dagre Layout -----------------------------------------------------------------
import dagre from 'dagre'; // Импортируем библиотеку dagre

// Типы для Dagre (были уже корректны)
interface DagreNode {
	x: number;
	y: number;
	width: number;
	height: number;
}

// Инициализируем объект Dagre Graph
const dagreGraph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));

// Определяем размеры узлов для корректного расположения.
// Может потребоваться корректировка в зависимости от реального размера узла в CSS.
const nodeWidth = 120;
const nodeHeight = 40;

/**
 * Применяет алгоритм расположения Dagre к узлам и рёбрам графа.
 * @param {Array<any>} nodes - Массив узлов React Flow.
 * @param {Array<any>} edges - Массив рёбер React Flow.
 * @param {'TB' | 'LR'} [direction='TB'] - Направление графа ('TB' - сверху вниз, 'LR' - слева направо).
 * @returns {{nodes: any[], edges: any[]}} Объект с обновлёнными узлами и рёбрами, содержащими позиции.
 */
const getLayoutedElements = (nodes: any[], edges: any[], direction: 'TB' | 'LR' = 'TB') => {
	const isHorizontal = direction === 'LR';
	// Компактные настройки для расположения в 2 ряда
	dagreGraph.setGraph({
		rankdir: direction,
		ranksep: 40, // Уменьшенное расстояние между рангами (рядами)
		nodesep: 10, // Уменьшенное расстояние между узлами в одном ранге
		edgesep: 20, // Расстояние между рёбрами
		marginx: 20, // Отступы по X
		marginy: 20 // Отступы по Y
	});

	// Добавляем узлы в граф Dagre с динамическими размерами
	nodes.forEach((node) => {
		// Пытаемся найти элемент узла в DOM для получения реальных размеров
		const nodeElement = document.querySelector(`[data-id="${node.id}"]`) as HTMLElement;
		const dimensions = getNodeDimensions(nodeElement);

		dagreGraph.setNode(node.id, {
			width: Math.min(dimensions.width + 10, 140), // Ограничиваем максимальную ширину
			height: Math.min(dimensions.height + 10, 50) // Ограничиваем максимальную высоту
		});
	});

	// Добавляем рёбра в граф Dagre
	edges.forEach((edge) => {
		// Dagre ожидает id, source, target
		dagreGraph.setEdge(edge.source, edge.target);
	});

	// Выполняем расчёт расположения графа
	dagre.layout(dagreGraph);

	// Обновляем позиции узлов React Flow на основе расчётов Dagre
	const newNodes = nodes.map((node) => {
		const nodeWithPosition: DagreNode = dagreGraph.node(node.id);
		const nodeDimensions = dagreGraph.node(node.id);

		return {
			...node,
			targetPosition: isHorizontal ? 'left' : 'top',
			sourcePosition: isHorizontal ? 'right' : 'bottom',
			position: {
				x: nodeWithPosition.x - nodeDimensions.width / 2,
				y: nodeWithPosition.y - nodeDimensions.height / 2
			}
		};
	});

	return { nodes: newNodes, edges };
};

// Функция для получения реальных размеров узла из DOM
const getNodeDimensions = (nodeElement: HTMLElement | null): { width: number; height: number } => {
	if (!nodeElement) {
		return { width: 120, height: 40 }; // Fallback размеры
	}

	const rect = nodeElement.getBoundingClientRect();
	return {
		width: Math.max(Math.min(rect.width, 130), 100), // Ограничиваем ширину: мин 100px, макс 130px
		height: Math.max(Math.min(rect.height, 45), 30) // Ограничиваем высоту: мин 30px, макс 45px
	};
};

/**
 * @typedef {object} GraphPanelProps
 * @property {{ nodes: any[]; edges: any[]; }} graphData - Данные графа для отображения (узлы и ребра).
 * @property {(nodeId: string) => void} onNodeClick - Обработчик клика по узлу.
 * @property {(nodeId: string, isSelected: boolean) => void} onNodeSelectionChange - Обработчик изменения выделения узла.
 * @property {(nodeId: string) => Promise<void>} onNodeDelete - Обработчик удаления узла.
 * @property {Set<string>} selectedNodeIds - Набор ID выделенных узлов.
 */
interface GraphPanelProps {
	graphData: { nodes: any[]; edges: any[] };
	onNodeClick: (nodeId: string) => void;
	onNodeSelectionChange: (nodeId: string, isSelected: boolean) => void;
	onNodeDelete: (nodeId: string) => Promise<void>;
	selectedNodeIds: Set<string>;
	app: App;
}

/**
 * GraphPanelComponent - React компонент для отображения графа диалогов с использованием React Flow и Dagre.
 * @param {GraphPanelProps} props - Свойства компонента.
 * @returns {React.ReactElement} Элемент React.
 */
export const GraphPanelComponent: React.FC<GraphPanelProps> = ({
	graphData,
	onNodeClick,
	onNodeSelectionChange,
	onNodeDelete,
	selectedNodeIds,
	app
}) => {
	// ----------------------------------------------- React Hooks ---------------------------------------------------------------
	// Инициализируем состояния для узлов и рёбер React Flow
	const [nodes, setNodes, onNodesChange] = useNodesState([]);
	const [edges, setEdges, onEdgesChange] = useEdgesState([]); // onEdgesState переименовано на onEdgesChange

	// Используем useEffect для применения Dagre layout при изменении данных графа
	React.useEffect(() => {
		// Проверяем, есть ли данные для построения графа
		if (graphData.nodes.length > 0 || graphData.edges.length > 0) {
			console.log('Applying Dagre layout with data:', graphData);

			const initialNodesForLayout = graphData.nodes.map((node) => ({
				id: node.id,
				type: node.type || 'default',
				data: {
					label: node.data?.label || `Node ${node.id}`,
					title: node.data?.title,
					title_generated: node.data?.title_generated || false,
                    onNodeDelete: onNodeDelete, 
                    isSelected: selectedNodeIds.has(node.id),
                    graphId: (node.data && node.data.message && node.data.message.graph_id) ? node.data.message.graph_id : 'unknown',
                    nodeId: node.id,
					app: app
				},
				position: { x: 0, y: 0 },
                className: selectedNodeIds.has(node.id) ? 'selected-node' : '',
			}));

			const initialEdgesForLayout = graphData.edges.map((edge) => ({
				id: edge.id,
				source: edge.source,
				target: edge.target,
				type: edge.type || 'default'
			}));

			// Сначала устанавливаем узлы без layout
			setNodes(initialNodesForLayout);
			setEdges(initialEdgesForLayout);

			// Применяем layout с задержкой, чтобы DOM успел отрендерить узлы
			setTimeout(() => {
				const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(initialNodesForLayout, initialEdgesForLayout);
				setNodes(layoutedNodes);
				setEdges(layoutedEdges);
			}, 100); // Задержка 100ms для рендеринга DOM
		} else {
			// Если данных нет, очищаем граф
			setNodes([]);
			setEdges([]);
		}
	}, [graphData.nodes, graphData.edges, onNodeDelete, selectedNodeIds]); // Зависимости: изменения в узлах и рёбрах графа, колбэк удаления, выделенные узлы

	// ----------------------------------------------- Event Handlers ------------------------------------------------------------
	/**
	 * Обработчик добавления нового соединения между узлами (если разрешено).
	 * @param {Connection | Edge} params - Параметры нового соединения.
	 */
	const onConnect = React.useCallback((params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)), [setEdges]);

	/**
	 * Обработчик клика по узлу.
	 * @param {React.MouseEvent} event - Объект события клика.
	 * @param {any} node - Данные узла, по которому был клик.
	 */
	const nodeClickHandler = React.useCallback(
		(event: React.MouseEvent, node: any) => {
			if (event.shiftKey) {
				// Если зажат Shift, переключаем выделение
				onNodeSelectionChange(node.id, !selectedNodeIds.has(node.id));
			} else {
				// Иначе, это обычный клик по узлу
				onNodeClick(node.id);
			}
		},
		[onNodeClick, onNodeSelectionChange, selectedNodeIds]
	);

	// Добавляем обработчик для ReactFlow, чтобы отслеживать клики по пустому пространству
	const onPaneClick = React.useCallback(
		(event: React.MouseEvent) => {
			// Проверяем, был ли клик по пустому пространству (не по узлу или ребру)
			const target = event.target as HTMLElement;
			if (!target.closest('.react-flow__node') && !target.closest('.react-flow__edge')) {
				// Если клик был не по узлу и не по ребру, сбрасываем выделение
				if (selectedNodeIds.size > 0) {
					// Вызываем колбэк для сброса всех выделений
					selectedNodeIds.forEach((nodeId) => onNodeSelectionChange(nodeId, false));
				}
			}
		},
		[selectedNodeIds, onNodeSelectionChange]
	);

	// Обработчик для клавиатуры (Escape)
	React.useEffect(() => {
		const handleKeyDown = (event: KeyboardEvent) => {
			if (event.key === 'Escape') {
				if (selectedNodeIds.size > 0) {
					selectedNodeIds.forEach((nodeId) => onNodeSelectionChange(nodeId, false));
				}
			}
		};
		document.addEventListener('keydown', handleKeyDown);
		return () => {
			document.removeEventListener('keydown', handleKeyDown);
		};
	}, [selectedNodeIds, onNodeSelectionChange]);

	// ----------------------------------------------- Render --------------------------------------------------------------------
	return React.createElement(
		'div',
		{ className: 'graph-panel', style: { width: '100%', height: '100%' } },
		// Оборачиваем ReactFlowProvider вокруг ReactFlow для доступа к контексту React Flow
		React.createElement(
			ReactFlowProvider,
			null,
			React.createElement(
				ReactFlow,
				{
					nodes,
					edges,
					onNodesChange, // Обработчик изменения узлов
					onEdgesChange, // Обработчик изменения рёбер
					onConnect, // Обработчик подключения
					onNodeClick: nodeClickHandler, // Обработчик клика по узлу
					onPaneClick: onPaneClick, // Обработчик клика по панели
					fitView: true, // Автоматически центрирует и масштабирует граф при загрузке
					attributionPosition: 'bottom-right', // Позиция атрибуции React Flow
					nodeTypes,
					// Дополнительные параметры для лучшего отображения
					zoomOnScroll: true,
					panOnDrag: true,
					minZoom: 0.1,
					maxZoom: 2
				},
				// Компоненты Controls и Background являются дочерними для ReactFlow
				React.createElement(Controls, null),
				React.createElement(Background, { variant: BackgroundVariant.Dots, gap: 12, size: 1 })
			)
		)
	);
};
