/**
 * Input Action Helpers
 *
 * Pure store-based helper functions used by useRegisterInputActions.
 * These are extracted for testability — each function operates on a
 * Jotai store without React dependencies.
 */

import type { Store } from 'jotai/vanilla/store';
import {
	addNodesToSelectionAtom,
	cancelSelectionAtom,
	clearEdgeSelectionAtom,
	clearSelectionAtom,
	copyToClipboardAtom,
	draggingNodeIdAtom,
	edgeCreationAtom,
	endNodeDragAtom,
	focusedNodeIdAtom,
	graphAtom,
	nodeKeysAtom,
	nodePositionUpdateCounterAtom,
	optimisticDeleteEdgeAtom,
	optimisticDeleteNodeAtom,
	provideInputAtom,
	pushHistoryAtom,
	resetInputModeAtom,
	selectSingleNodeAtom,
	selectedEdgeIdAtom,
	selectedNodeIdsAtom,
	setFocusedNodeAtom,
	setKeyboardInteractionModeAtom,
	resetKeyboardInteractionModeAtom,
	toggleNodeInSelectionAtom,
	uiNodesAtom,
} from '../core';
import { isFilterActiveAtom, clearSearchAtom } from '../core/search-store';
import { inputModeAtom, keyboardInteractionModeAtom } from '../core/interaction-store';
import { selectionPathAtom } from '../core/selection-path-store';
import { commandLineVisibleAtom, closeCommandLineAtom } from '../commands/store';
import type { GestureSubject, InputEvent } from './types';
import { isKeyInputEvent } from './types';

// Re-export extracted modules for backward compat
export {
	isRepeatBlocked,
	getSelectedNodeIds,
	clearSelectionState,
	resolveFocusableNodeId,
	getCurrentSubject,
	updateKeySubject,
} from './modifier-helpers';
import { getSelectedNodeIds, clearSelectionState, resolveFocusableNodeId, updateKeySubject } from './modifier-helpers';

export {
	findNearestNode,
	cycleFocus,
	navigateFocus,
	activateFocusedNode,
	type Direction,
} from './gesture-classification';

// =============================================================================
// Constants
// =============================================================================

export const EMPTY_EDGE_CREATION = {
	isCreating: false,
	sourceNodeId: null,
	sourceNodePosition: null,
	targetPosition: null,
	hoveredTargetNodeId: null,
	sourceHandle: null,
	targetHandle: null,
	sourcePort: null,
	targetPort: null,
	snappedTargetPosition: null,
} as const;

// =============================================================================
// Mutations
// =============================================================================

export function nudgeSelection(store: Store, dx: number, dy: number, label: string): void {
	const selected = getSelectedNodeIds(store);
	if (selected.length === 0) {
		const focused = store.get(focusedNodeIdAtom);
		if (focused) {
			store.set(selectSingleNodeAtom, focused);
		}
	}

	const nodeIds = getSelectedNodeIds(store);
	if (nodeIds.length === 0) return;

	const graph = store.get(graphAtom).copy();
	store.set(pushHistoryAtom, label);

	for (const nodeId of nodeIds) {
		if (!graph.hasNode(nodeId)) continue;
		const x = graph.getNodeAttribute(nodeId, 'x') as number;
		const y = graph.getNodeAttribute(nodeId, 'y') as number;
		graph.setNodeAttribute(nodeId, 'x', x + dx);
		graph.setNodeAttribute(nodeId, 'y', y + dy);
	}

	store.set(graphAtom, graph);
	store.set(nodePositionUpdateCounterAtom, (count: number) => count + 1);
}

export function deleteSelection(store: Store): void {
	const selectedEdgeId = store.get(selectedEdgeIdAtom);
	if (selectedEdgeId) {
		store.set(optimisticDeleteEdgeAtom, { edgeKey: selectedEdgeId });
		store.set(clearEdgeSelectionAtom);
	}

	const selectedNodeIds = getSelectedNodeIds(store);
	if (selectedNodeIds.length === 0) return;

	store.set(pushHistoryAtom, selectedNodeIds.length > 1 ? `Delete ${selectedNodeIds.length} nodes` : 'Delete node');
	const focusedNodeId = store.get(focusedNodeIdAtom);

	for (const nodeId of selectedNodeIds) {
		store.set(optimisticDeleteNodeAtom, { nodeId });
	}

	if (focusedNodeId && selectedNodeIds.includes(focusedNodeId)) {
		store.set(setFocusedNodeAtom, null);
	}

	store.set(clearSelectionAtom);
}

export function cutSelection(store: Store): void {
	const selectedNodeIds = getSelectedNodeIds(store);
	if (selectedNodeIds.length === 0) return;

	store.set(copyToClipboardAtom, selectedNodeIds);
	deleteSelection(store);
}

// =============================================================================
// Interaction State
// =============================================================================

export function cancelActiveInteraction(store: Store): boolean {
	let didCancel = false;

	if (store.get(draggingNodeIdAtom) !== null) {
		store.set(endNodeDragAtom);
		didCancel = true;
	}

	if (store.get(edgeCreationAtom).isCreating) {
		store.set(edgeCreationAtom, EMPTY_EDGE_CREATION);
		didCancel = true;
	}

	if (store.get(inputModeAtom).type === 'pickPoint' || store.get(inputModeAtom).type === 'pickNode' || store.get(inputModeAtom).type === 'pickNodes') {
		store.set(resetInputModeAtom);
		didCancel = true;
	}

	const selectionPath = store.get(selectionPathAtom);
	if (selectionPath !== null) {
		store.set(cancelSelectionAtom);
		didCancel = true;
	}

	return didCancel;
}

export function escapeInput(store: Store): void {
	if (cancelActiveInteraction(store)) return;

	if (store.get(keyboardInteractionModeAtom) === 'manipulate') {
		store.set(resetKeyboardInteractionModeAtom);
		return;
	}

	if (store.get(isFilterActiveAtom)) {
		store.set(clearSearchAtom);
		return;
	}

	if (store.get(commandLineVisibleAtom)) {
		store.set(closeCommandLineAtom);
		return;
	}

	if (store.get(selectedNodeIdsAtom).size > 0 || store.get(selectedEdgeIdAtom) !== null) {
		clearSelectionState(store);
	}
}

// =============================================================================
// Pick Mode
// =============================================================================

export function resolvePickNode(event: InputEvent, store: Store): void {
	updateKeySubject(event, store);
	if (event.subject?.kind !== 'node') return;
	if (store.get(inputModeAtom).type === 'pickNodes') {
		store.set(toggleNodeInSelectionAtom, event.subject.nodeId);
		store.set(setFocusedNodeAtom, event.subject.nodeId);
		return;
	}
	store.set(provideInputAtom, event.subject.nodeId);
	store.set(resetInputModeAtom);
}

export function finishPickNodes(store: Store): void {
	store.set(provideInputAtom, getSelectedNodeIds(store));
	store.set(resetInputModeAtom);
}

export function resolvePickPoint(event: InputEvent, store: Store): void {
	if (event.kind !== 'pointer') return;
	store.set(provideInputAtom, event.worldPosition);
	store.set(resetInputModeAtom);
}

export function selectAll(store: Store): void {
	clearSelectionState(store);
	store.set(addNodesToSelectionAtom, store.get(nodeKeysAtom));
}
