/**
 * Gesture Classification — Navigation
 *
 * Pure navigation functions for keyboard-based node traversal:
 * findNearestNode, cycleFocus, navigateFocus, activateFocusedNode.
 *
 * Extracted from input-action-helpers.ts in v1.6.0.
 *
 * @since 1.6.0
 */

import type { Store } from 'jotai/vanilla/store';
import {
	focusedNodeIdAtom,
	selectSingleNodeAtom,
	setFocusedNodeAtom,
	setKeyboardInteractionModeAtom,
	uiNodesAtom,
} from '../core';
import { resolveFocusableNodeId } from './modifier-helpers';

export type Direction = 'up' | 'down' | 'left' | 'right';

/**
 * Find the nearest node in a given direction from the current focus.
 */
export function findNearestNode(
	currentNodeId: string | null,
	store: Store,
	direction: Direction,
): string | null {
	const nodes = store.get(uiNodesAtom);
	if (nodes.length === 0) return null;

	if (!currentNodeId) {
		const sorted = [...nodes].sort((a, b) => a.position.y - b.position.y || a.position.x - b.position.x);
		return sorted[0]?.id ?? null;
	}

	const currentNode = nodes.find((node: { id: string }) => node.id === currentNodeId);
	if (!currentNode) return nodes[0]?.id ?? null;

	const cx = currentNode.position.x + (currentNode.width ?? 200) / 2;
	const cy = currentNode.position.y + (currentNode.height ?? 100) / 2;

	let bestId: string | null = null;
	let bestScore = Number.POSITIVE_INFINITY;

	for (const candidate of nodes) {
		if (candidate.id === currentNode.id) continue;

		const nx = candidate.position.x + (candidate.width ?? 200) / 2;
		const ny = candidate.position.y + (candidate.height ?? 100) / 2;
		const dx = nx - cx;
		const dy = ny - cy;

		let isValid = false;
		switch (direction) {
			case 'right': isValid = dx > 0; break;
			case 'left': isValid = dx < 0; break;
			case 'down': isValid = dy > 0; break;
			case 'up': isValid = dy < 0; break;
		}

		if (!isValid) continue;

		const dist = Math.sqrt(dx * dx + dy * dy);
		const isHorizontal = direction === 'left' || direction === 'right';
		const perpendicularDistance = isHorizontal ? Math.abs(dy) : Math.abs(dx);
		const score = dist + perpendicularDistance * 0.5;

		if (score < bestScore) {
			bestScore = score;
			bestId = candidate.id;
		}
	}

	return bestId;
}

/**
 * Cycle focus to the next/previous node by z-index order.
 */
export function cycleFocus(store: Store, direction: 1 | -1): void {
	const nodes = store.get(uiNodesAtom);
	if (nodes.length === 0) return;

	const focusedNodeId = store.get(focusedNodeIdAtom);
	const sorted = [...nodes].sort((a, b) => a.zIndex - b.zIndex);
	const currentIdx = focusedNodeId ? sorted.findIndex((node: { id: string }) => node.id === focusedNodeId) : -1;
	const nextIdx = direction === -1
		? (currentIdx <= 0 ? sorted.length - 1 : currentIdx - 1)
		: (currentIdx + 1) % sorted.length;

	store.set(setFocusedNodeAtom, sorted[nextIdx].id);
}

/**
 * Navigate focus to the nearest node in a direction.
 */
export function navigateFocus(store: Store, direction: Direction): void {
	const nextId = findNearestNode(resolveFocusableNodeId(store), store, direction);
	if (nextId) {
		store.set(setFocusedNodeAtom, nextId);
	}
}

/**
 * Activate the focused node — select it and optionally enter manipulate mode.
 */
export function activateFocusedNode(store: Store, enterManipulate: boolean): void {
	const focusedNodeId = resolveFocusableNodeId(store);
	if (!focusedNodeId) return;

	store.set(setFocusedNodeAtom, focusedNodeId);
	store.set(selectSingleNodeAtom, focusedNodeId);
	if (enterManipulate) {
		store.set(setKeyboardInteractionModeAtom, 'manipulate');
	}
}
