/**
 * Layer 4: Gesture Dispatcher
 *
 * Routes resolved actions to registered handlers, respecting input phase.
 * Simple function handlers fire only on pointer 'start'/'instant' and key 'down'.
 * Object handlers route each phase to a dedicated method.
 */

import type {
	ActionHandler,
	InputEvent,
	InputPhase,
	KeyInputEvent,
	PhaseHandler,
	PointerGestureEvent,
	ResolvedAction,
} from './types';
import { isKeyInputEvent } from './types';

// =============================================================================
// Action Registry
// =============================================================================

const handlers = new Map<string, ActionHandler>();

/**
 * Register an action handler. Overwrites any existing handler for the same ID.
 */
export function registerAction(actionId: string, handler: ActionHandler): void {
	handlers.set(actionId, handler);
}

/**
 * Remove a registered action handler.
 */
export function unregisterAction(actionId: string): void {
	handlers.delete(actionId);
}

/**
 * Get a registered handler (for testing/introspection).
 */
export function getHandler(actionId: string): ActionHandler | undefined {
	return handlers.get(actionId);
}

/**
 * Clear all registered handlers (for testing).
 */
export function clearHandlers(): void {
	handlers.clear();
}

// =============================================================================
// Dispatch
// =============================================================================

/**
 * Dispatch a resolved gesture event to its action handler.
 *
 * Returns true if a handler was found and invoked, false otherwise.
 * The 'none' action ID is always a no-op — it means the binding
 * intentionally blocks the gesture without executing anything.
 */
export function dispatch(
	event: InputEvent,
	resolution: ResolvedAction,
): boolean {
	if (resolution.actionId === 'none') return true;

	const handler = handlers.get(resolution.actionId);
	if (!handler) return false;

	if (typeof handler === 'function') {
		// Simple handler: fire on pointer start/instant and key down.
		if (
			(isKeyInputEvent(event) && event.phase === 'down') ||
			(!isKeyInputEvent(event) && (event.phase === 'start' || event.phase === 'instant'))
		) {
			handler(event);
		}
		return true;
	}

	// Phase-aware handler
	routePhase(handler, event.phase, event);
	return true;
}

function routePhase(
	handler: PhaseHandler,
	phase: InputPhase,
	event: InputEvent,
): void {
	if (isKeyInputEvent(event)) {
		routeKeyPhase(handler, phase, event);
		return;
	}

	switch (phase) {
		case 'start':
			handler.onStart?.(event as PointerGestureEvent);
			break;
		case 'move':
			handler.onMove?.(event as PointerGestureEvent);
			break;
		case 'end':
			handler.onEnd?.(event as PointerGestureEvent);
			break;
		case 'instant':
			handler.onInstant?.(event as PointerGestureEvent);
			break;
		case 'cancel':
			handler.onCancel?.(event as PointerGestureEvent);
			break;
	}
}

function routeKeyPhase(
	handler: PhaseHandler,
	phase: InputPhase,
	event: KeyInputEvent,
): void {
	switch (phase) {
		case 'down':
			handler.onDown?.(event);
			break;
		case 'up':
			handler.onUp?.(event);
			break;
	}
}
