/**
 * useDragStateMachine
 *
 * Pure drag state helpers extracted from useNodeDrag.
 * Handles: memo initialization, multi-select expansion,
 * and gesture instance tracking.
 *
 * @since 1.6.0
 */

import type Graph from 'graphology';
import type { NodeId, NodePosition, GraphNodeAttributes } from '../core/types';
import { getNodeDescendants } from '../core/group-store';

export interface DragInitialPositions {
	/** Map from nodeId → initial {x,y} before drag started */
	positions: Map<NodeId, { x: number; y: number }>;
}

/**
 * Build the initial positions map for a drag operation.
 * Includes all selected nodes + their group descendants.
 */
export function buildDragPositions(
	graph: Graph,
	selectedNodeIds: Set<NodeId>,
): Map<NodeId, { x: number; y: number }> {
	const positions = new Map<NodeId, { x: number; y: number }>();

	// Collect selected nodes
	for (const nodeId of selectedNodeIds) {
		if (graph.hasNode(nodeId)) {
			const attrs = graph.getNodeAttributes(nodeId) as GraphNodeAttributes;
			positions.set(nodeId, { x: attrs.x, y: attrs.y });
		}
	}

	// Expand to include descendants of any group nodes
	const currentKeys = Array.from(positions.keys());
	for (const nodeId of currentKeys) {
		const descendants = getNodeDescendants(graph, nodeId);
		for (const descId of descendants) {
			if (!positions.has(descId) && graph.hasNode(descId)) {
				const attrs = graph.getNodeAttributes(descId) as GraphNodeAttributes;
				positions.set(descId, { x: attrs.x, y: attrs.y });
			}
		}
	}

	return positions;
}

/**
 * Compute the position updates for all dragged nodes given
 * pixel movement and zoom level.
 */
export function computeDragUpdates(
	initialPositions: Map<NodeId, { x: number; y: number }>,
	movementX: number,
	movementY: number,
	zoom: number,
	graph: Graph,
): { id: NodeId; pos: NodePosition }[] {
	const deltaX = movementX / zoom;
	const deltaY = movementY / zoom;
	const updates: { id: NodeId; pos: NodePosition }[] = [];

	initialPositions.forEach((initialPos, nodeId) => {
		if (graph.hasNode(nodeId)) {
			updates.push({
				id: nodeId,
				pos: {
					x: initialPos.x + deltaX,
					y: initialPos.y + deltaY,
				},
			});
		}
	});

	return updates;
}

/**
 * Check whether a pointer target should prevent drag.
 */
export function isDragPrevented(target: HTMLElement): boolean {
	return (
		!!target.closest('[data-no-drag="true"]') ||
		target.tagName === 'INPUT' ||
		target.tagName === 'TEXTAREA' ||
		target.tagName === 'SELECT'
	);
}
