/**
 * Node Component
 *
 * Generic draggable/resizable node with render prop for custom content.
 * Uses gesture system v2: tap/double-tap/long-press/context-menu
 * route through useNodeGestures; drag mechanics via useNodeDrag.
 */

import React from 'react';
import { useAtomValue, useSetAtom } from 'jotai';
import type { NodeId, UINodeState, NodeRenderProps, UINodeProperties } from '../core/types';
import { graphAtom, draggingNodeIdAtom } from '../core/graph-store';
import { nodePositionAtomFamily } from '../core/graph-position';
import { dropTargetNodeIdAtom } from '../core/graph-mutations';
import { handleNodePointerDownSelectionAtom, selectedNodeIdsAtom, focusedNodeIdAtom } from '../core/selection-store';
import { isFilterActiveAtom, searchResultsAtom, highlightedSearchNodeIdAtom } from '../core/search-store';
import { useNodeSelection } from '../hooks/useNodeSelection';
import { useNodeDrag } from '../hooks/useNodeDrag';
import { useNodeResize, type UseNodeResizeOptions } from '../hooks/useNodeResize';
import { useSplitGesture } from '../hooks/useSplitGesture';
import { ResizeHandle } from './ResizeHandle';
import { useCanvasStyle } from '../providers/CanvasStyleProvider';
import { useInputContext } from '../gestures/GestureProvider';
import { useNodeGestures } from '../gestures/useNodeGestures';

// Re-export for convenience
export type { NodeRenderProps };

export interface NodeProps {
	/** Node data */
	nodeData: UINodeState;
	/** Render function for custom node content */
	renderContent: (props: NodeRenderProps) => React.ReactNode;
	/** Callback to persist node position/size changes */
	onPersist?: (nodeId: NodeId, graphId: string, uiProperties: UINodeProperties) => Promise<void>;
	/** Callback when persist fails */
	onPersistError?: (nodeId: NodeId, error: Error) => void;
	/** Optional wrapper component around the node */
	wrapper?: React.ComponentType<{ children: React.ReactNode; nodeData: UINodeState }>;
}

/**
 * Generic draggable/resizable node component
 */
export function Node({
	nodeData,
	renderContent,
	onPersist,
	onPersistError,
	wrapper: Wrapper,
}: NodeProps) {
	const { id, zIndex } = nodeData;

	const graph = useAtomValue(graphAtom);
	const setHandleNodePointerDown = useSetAtom(handleNodePointerDownSelectionAtom);
	const { isSelected } = useNodeSelection(id);
	const selectedNodeIds = useAtomValue(selectedNodeIdsAtom);
	const activeDraggingNodeId = useAtomValue(draggingNodeIdAtom);
	const focusedNodeId = useAtomValue(focusedNodeIdAtom);
	const isFocused = focusedNodeId === id && !isSelected;
	const position = useAtomValue(nodePositionAtomFamily(id));
	const canvasStyles = useCanvasStyle();
	const isFilterActive = useAtomValue(isFilterActiveAtom);
	const searchResults = useAtomValue(searchResultsAtom);
	const highlightedSearchNodeId = useAtomValue(highlightedSearchNodeIdAtom);
	const isDimmedByFilter = isFilterActive && !searchResults.has(id);
	const isSearchHighlighted = highlightedSearchNodeId === id;
	const dropTargetId = useAtomValue(dropTargetNodeIdAtom);
	const isDropTarget = dropTargetId === id;

	// Gesture system v2: shared context
	const { system, onAction } = useInputContext();

	// Node-level gesture recognition (tap, double-tap, long-press, right-click)
	const gestureHandlers = useNodeGestures({
		nodeId: id,
		mappingIndex: system.mappingIndex,
		onAction,
		heldKeys: system.heldKeys,
	});

	// Drag mechanics (always move-node — gesture resolution removed)
	const { bind, updateNodePositions } = useNodeDrag(id, {
		heldKeys: system.heldKeys,
		onPersist,
		onPersistError,
	});

	const resizeOptions: UseNodeResizeOptions = {
		onPersist,
		onPersistError,
	};

	const { localWidth, localHeight, isResizing, createResizeStart, handleResizeMove, handleResizeEnd } = useNodeResize({
		id,
		nodeData,
		updateNodePositions,
		options: resizeOptions,
	});

	const nodeWidth = localWidth;
	const nodeHeight = localHeight;

	// Two-finger split gesture
	const splitGesture = useSplitGesture(id);

	const isVisuallyDragging = activeDraggingNodeId !== null && selectedNodeIds.has(id);

	const bindGestures = bind();

	// Intercept pointer down to handle selection + feed gestures
	const handlePointerDown = (e: React.PointerEvent) => {
		const target = e.target as HTMLElement;
		if (
			target.closest('[data-no-drag="true"]') ||
			target.tagName === 'INPUT' ||
			target.tagName === 'TEXTAREA' ||
			target.tagName === 'SELECT'
		) {
			return;
		}

		// Feed the v2 gesture recognizer
		gestureHandlers.onPointerDown(e);

		// Forward to @use-gesture drag handler
		if (bindGestures.onPointerDown) {
			(bindGestures.onPointerDown as (e: React.PointerEvent) => void)(e);
		}

		e.stopPropagation();
		setHandleNodePointerDown({
			nodeId: id,
			isShiftPressed: e.shiftKey,
		});
	};

	const handlePointerUp = (e: React.PointerEvent) => {
		gestureHandlers.onPointerUp(e);
	};

	const currentZIndex = graph.hasNode(id) ? (graph.getNodeAttribute(id, 'zIndex') as number) : zIndex;

	// Node styling from context
	const borderColor = isSelected
		? canvasStyles.nodes.selectedBorderColor
		: isFocused
			? canvasStyles.nodes.selectedBorderColor
			: isVisuallyDragging
				? canvasStyles.nodes.draggingBorderColor
				: canvasStyles.nodes.defaultBorderColor;
	const borderWidth = isSelected || isVisuallyDragging ? '1.5px' : isFocused ? '2px' : '1px';
	const borderRadius = `${canvasStyles.nodes.defaultBorderRadius}px`;
	const defaultBoxShadow = `0px 8px 24px ${canvasStyles.nodes.shadowColor}`;
	const draggingBoxShadow = `0px 12px 32px ${canvasStyles.nodes.shadowColor}`;
	const selectedNodeBoxShadow = `0 0 10px 2px ${canvasStyles.nodes.selectedGlowColor}, ${defaultBoxShadow}`;
	const focusedBoxShadow = `0 0 0 2px ${canvasStyles.nodes.selectedBorderColor}, ${defaultBoxShadow}`;
	const currentBoxShadow = isSelected ? selectedNodeBoxShadow : isFocused ? focusedBoxShadow : isVisuallyDragging ? draggingBoxShadow : defaultBoxShadow;

	const nodeContent = (
		<div
			data-draggable-node="true"
			data-node-id={id}
			data-focused={isFocused || undefined}
			className={[
				isSearchHighlighted && 'canvas-search-highlight',
				isDropTarget && 'canvas-drop-target',
			].filter(Boolean).join(' ') || undefined}
			{...bindGestures}
			onPointerDown={(e: React.PointerEvent) => {
				splitGesture.onPointerDown(e);
				handlePointerDown(e);
			}}
			onPointerMove={splitGesture.onPointerMove}
			onPointerUp={(e: React.PointerEvent) => {
				splitGesture.onPointerUp(e);
				handlePointerUp(e);
			}}
			onPointerCancel={gestureHandlers.onPointerCancel}
			onContextMenu={gestureHandlers.onContextMenu}
			style={{
				width: `${nodeWidth}px`,
				height: `${nodeHeight}px`,
				backgroundColor: isVisuallyDragging ? canvasStyles.nodes.draggingBackgroundColor : canvasStyles.nodes.defaultBackground,
				border: `${borderWidth} solid ${borderColor}`,
				borderRadius,
				boxShadow: currentBoxShadow,
				cursor: isVisuallyDragging ? 'grabbing' : 'grab',
				opacity: isDimmedByFilter ? 0.2 : isVisuallyDragging ? 0.9 : 1,
				pointerEvents: isDimmedByFilter ? 'none' as const : 'auto' as const,
				transition: isVisuallyDragging
					? 'opacity 0.1s, transform 0.05s, background-color 0.1s, box-shadow 0.1s, border-color 0.1s'
					: 'opacity 0.4s, border-color 0.4s, box-shadow 0.4s, background-color 0.4s, transform 0.4s ease-in-out',
				transform: isVisuallyDragging ? 'scale(1.005)' : 'scale(1)',
				zIndex: currentZIndex,
				touchAction: 'manipulation',
				userSelect: 'auto',
				position: 'relative',
				overflow: 'hidden',
			}}
		>
			{/* Custom content */}
			{renderContent({
				node: nodeData,
				isSelected,
				isResizing,
				isDragging: isVisuallyDragging,
			})}

			{/* Resize handles */}
			<ResizeHandle direction="nw" createResizeStart={createResizeStart} handleResizeMove={handleResizeMove} handleResizeEnd={handleResizeEnd} isResizing={isResizing} />
			<ResizeHandle direction="ne" createResizeStart={createResizeStart} handleResizeMove={handleResizeMove} handleResizeEnd={handleResizeEnd} isResizing={isResizing} />
			<ResizeHandle direction="sw" createResizeStart={createResizeStart} handleResizeMove={handleResizeMove} handleResizeEnd={handleResizeEnd} isResizing={isResizing} />
			<ResizeHandle direction="se" createResizeStart={createResizeStart} handleResizeMove={handleResizeMove} handleResizeEnd={handleResizeEnd} isResizing={isResizing} alwaysVisible />
			<ResizeHandle direction="n" createResizeStart={createResizeStart} handleResizeMove={handleResizeMove} handleResizeEnd={handleResizeEnd} isResizing={isResizing} />
			<ResizeHandle direction="s" createResizeStart={createResizeStart} handleResizeMove={handleResizeMove} handleResizeEnd={handleResizeEnd} isResizing={isResizing} />
			<ResizeHandle direction="w" createResizeStart={createResizeStart} handleResizeMove={handleResizeMove} handleResizeEnd={handleResizeEnd} isResizing={isResizing} />
		</div>
	);

	// Apply wrapper if provided
	if (Wrapper) {
		return <Wrapper nodeData={nodeData}>{nodeContent}</Wrapper>;
	}

	return nodeContent;
}
