/**
 * Этот файл содержит 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, // <-- ИСПРАВЛЕНО: импортируем прямо ReactFlowProvider без переименования
	Connection,
	Edge,
	BackgroundVariant // <-- ИСПРАВЛЕНО: используем BackgroundVariant для типизации
} from 'reactflow';

// <-- ИСПРАВЛЕНО: Это ключевой момент. Убедитесь, что 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';

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

	return React.createElement(
		React.Fragment,
		//             className: `custom-node user-node ${titleGenerated ? 'title-generated' : 'title-pending'}`,
		null, // Используем React.Fragment для возврата нескольких элементов без лишнего div
		React.createElement(Handle, {
			type: 'source',
			position: Position.Bottom,
			isConnectable: isConnectable
		}),
		React.createElement(Handle, {
			type: 'target',
			position: Position.Top,
			isConnectable: isConnectable
		}),
		displayLabel
	);
};

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

	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
		}),
		displayLabel
	);
};

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

	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
		}),
		displayLabel
	);
};

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

	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
		}),
		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 - Обработчик клика по узлу.
 */
interface GraphPanelProps {
	graphData: { nodes: any[]; edges: any[] };
	onNodeClick: (nodeId: string) => void;
}

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

	// Используем 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
				},
				position: { x: 0, y: 0 }
			}));

			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]); // Зависимости: изменения в узлах и рёбрах графа

	// ----------------------------------------------- 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) => {
			onNodeClick(node.id);
		},
		[onNodeClick]
	);

	// ----------------------------------------------- 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, // Обработчик клика по узлу
					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 })
			)
		)
	);
};
