/**
 * Selection state management
 *
 * Manages node and edge selection state.
 */

import { atom } from 'jotai';
import type { NodeId } from './types';
import { createDebug } from '../utils/debug';

const debug = createDebug('selection');

// --- Core Selection Atoms ---

/**
 * Set of currently selected node IDs
 */
export const selectedNodeIdsAtom = atom<Set<NodeId>>(new Set<NodeId>());

/**
 * Currently selected edge ID (only one edge can be selected at a time)
 */
export const selectedEdgeIdAtom = atom<string | null>(null);

// --- Selection Actions ---

/**
 * Handle node pointer down with shift-click support for multi-select
 */
export type NodePointerDownPayload = {
	nodeId: NodeId;
	isShiftPressed: boolean;
};

export const handleNodePointerDownSelectionAtom = atom(null, (get, set, { nodeId, isShiftPressed }: NodePointerDownPayload) => {
	const currentSelection = get(selectedNodeIdsAtom);
	debug('handleNodePointerDownSelection: nodeId=%s, shift=%s, current=%o', nodeId, isShiftPressed, Array.from(currentSelection));

	// Clear edge selection when selecting a node
	set(selectedEdgeIdAtom, null);

	if (isShiftPressed) {
		const newSelection = new Set(currentSelection);
		if (newSelection.has(nodeId)) {
			newSelection.delete(nodeId);
		} else {
			newSelection.add(nodeId);
		}
		debug('Shift-click, setting selection to: %o', Array.from(newSelection));
		set(selectedNodeIdsAtom, newSelection);
	} else {
		// Only change selection if node is NOT already selected
		// This allows multi-node drag to work
		if (!currentSelection.has(nodeId)) {
			debug('Node not in selection, selecting: %s', nodeId);
			set(selectedNodeIdsAtom, new Set([nodeId]));
		} else {
			debug('Node already selected, preserving multi-select');
		}
	}
});

/**
 * Select a single node, clearing any previous selection
 */
export const selectSingleNodeAtom = atom(null, (get, set, nodeId: NodeId | null | undefined) => {
	debug('selectSingleNode: %s', nodeId);
	set(selectedEdgeIdAtom, null);

	if (nodeId === null || nodeId === undefined) {
		debug('Clearing selection');
		set(selectedNodeIdsAtom, new Set());
	} else {
		const currentSelection = get(selectedNodeIdsAtom);
		if (currentSelection.has(nodeId) && currentSelection.size === 1) {
			return; // Already the only selection
		}
		set(selectedNodeIdsAtom, new Set([nodeId]));
	}
});

/**
 * Toggle a node in/out of selection
 */
export const toggleNodeInSelectionAtom = atom(null, (get, set, nodeId: NodeId) => {
	const currentSelection = get(selectedNodeIdsAtom);
	const newSelection = new Set(currentSelection);
	if (newSelection.has(nodeId)) {
		newSelection.delete(nodeId);
	} else {
		newSelection.add(nodeId);
	}
	set(selectedNodeIdsAtom, newSelection);
});

/**
 * Clear all node selection
 */
export const clearSelectionAtom = atom(null, (_get, set) => {
	debug('clearSelection');
	set(selectedNodeIdsAtom, new Set());
});

/**
 * Add multiple nodes to selection
 */
export const addNodesToSelectionAtom = atom(null, (get, set, nodeIds: NodeId[]) => {
	const currentSelection = get(selectedNodeIdsAtom);
	const newSelection = new Set(currentSelection);
	for (const nodeId of nodeIds) {
		newSelection.add(nodeId);
	}
	set(selectedNodeIdsAtom, newSelection);
});

/**
 * Remove multiple nodes from selection
 */
export const removeNodesFromSelectionAtom = atom(null, (get, set, nodeIds: NodeId[]) => {
	const currentSelection = get(selectedNodeIdsAtom);
	const newSelection = new Set(currentSelection);
	for (const nodeId of nodeIds) {
		newSelection.delete(nodeId);
	}
	set(selectedNodeIdsAtom, newSelection);
});

/**
 * Select an edge (clears node selection)
 */
export const selectEdgeAtom = atom(null, (get, set, edgeId: string | null) => {
	set(selectedEdgeIdAtom, edgeId);
	if (edgeId !== null) {
		set(selectedNodeIdsAtom, new Set());
	}
});

/**
 * Clear edge selection
 */
export const clearEdgeSelectionAtom = atom(null, (_get, set) => {
	set(selectedEdgeIdAtom, null);
});

// --- Keyboard Focus ---

/**
 * The node that has keyboard focus (distinct from selection).
 * Used for arrow key navigation between nodes.
 * null means no node has keyboard focus.
 */
export const focusedNodeIdAtom = atom<string | null>(null);

/**
 * Set keyboard focus to a node, or clear focus with null
 */
export const setFocusedNodeAtom = atom(null, (_get, set, nodeId: string | null) => {
	set(focusedNodeIdAtom, nodeId);
});

/**
 * Whether a node currently has keyboard focus
 */
export const hasFocusedNodeAtom = atom((get) => get(focusedNodeIdAtom) !== null);

// --- Derived Atoms ---

/**
 * Count of selected nodes
 */
export const selectedNodesCountAtom = atom((get) => get(selectedNodeIdsAtom).size);

/**
 * Whether any node is selected
 */
export const hasSelectionAtom = atom((get) => get(selectedNodeIdsAtom).size > 0);
