/**
 * Layer 2: Timed State Machine
 *
 * Per-pointer state machine for recognizing taps, double-taps,
 * triple-taps, and long-press. Pure functions — no timers inside.
 * The caller manages setTimeout via the returned timer commands.
 *
 * State diagram:
 *   idle → pressed (on down)
 *   pressed → released (on up, emit tap/double-tap/triple-tap)
 *   pressed → idle (on move-beyond-threshold, becomes drag)
 *   pressed → long-pressed (on long-press-timer)
 *   released → pressed (on down within multi-tap window)
 *   released → idle (on settle-timer)
 *   long-pressed → idle (on up)
 *   any → idle (on cancel)
 */

import type { GestureType, TimedState, TimedStateResult } from './types';

// Timer IDs
export const LONG_PRESS_TIMER = 'long-press';
export const SETTLE_TIMER = 'settle';

// Defaults (overridable via config)
export const DEFAULT_LONG_PRESS_MS = 600;
export const DEFAULT_MULTI_TAP_WINDOW_MS = 300;

export const IDLE: TimedState = { tag: 'idle', tapCount: 0 };

export type TimedEvent =
	| 'down'
	| 'up'
	| 'move-beyond-threshold'
	| 'cancel'
	| 'timer:long-press'
	| 'timer:settle';

export interface TimedConfig {
	longPressMs: number;
	multiTapWindowMs: number;
}

const DEFAULT_CONFIG: TimedConfig = {
	longPressMs: DEFAULT_LONG_PRESS_MS,
	multiTapWindowMs: DEFAULT_MULTI_TAP_WINDOW_MS,
};

/**
 * Pure state transition. Returns new state + optional side effects
 * (emit gesture, schedule/cancel timer).
 */
export function transition(
	state: TimedState,
	event: TimedEvent,
	config: TimedConfig = DEFAULT_CONFIG,
): TimedStateResult {
	switch (event) {
		case 'down':
			return onDown(state, config);
		case 'up':
			return onUp(state, config);
		case 'move-beyond-threshold':
			return onMoveBeyond(state);
		case 'cancel':
			return onCancel();
		case 'timer:long-press':
			return onLongPressTimer(state);
		case 'timer:settle':
			return onSettleTimer();
		default:
			return { state };
	}
}

function onDown(state: TimedState, config: TimedConfig): TimedStateResult {
	switch (state.tag) {
		case 'idle':
			return {
				state: { tag: 'pressed', tapCount: 0 },
				cancelTimer: SETTLE_TIMER,
				scheduleTimer: { id: LONG_PRESS_TIMER, delayMs: config.longPressMs },
			};
		case 'released':
			// Continue multi-tap sequence
			return {
				state: { tag: 'pressed', tapCount: state.tapCount },
				cancelTimer: SETTLE_TIMER,
				scheduleTimer: { id: LONG_PRESS_TIMER, delayMs: config.longPressMs },
			};
		default:
			return { state };
	}
}

function onUp(state: TimedState, config: TimedConfig): TimedStateResult {
	if (state.tag !== 'pressed') {
		// long-pressed → idle on up
		if (state.tag === 'long-pressed') {
			return {
				state: IDLE,
				cancelTimer: LONG_PRESS_TIMER,
			};
		}
		return { state };
	}

	const newCount = state.tapCount + 1;
	const emit = tapTypeForCount(newCount);

	return {
		state: { tag: 'released', tapCount: newCount },
		emit,
		cancelTimer: LONG_PRESS_TIMER,
		scheduleTimer: { id: SETTLE_TIMER, delayMs: config.multiTapWindowMs },
	};
}

function onMoveBeyond(state: TimedState): TimedStateResult {
	if (state.tag === 'pressed') {
		return {
			state: IDLE,
			cancelTimer: LONG_PRESS_TIMER,
		};
	}
	return { state };
}

function onCancel(): TimedStateResult {
	return {
		state: IDLE,
		cancelTimer: LONG_PRESS_TIMER,
	};
}

function onLongPressTimer(state: TimedState): TimedStateResult {
	if (state.tag === 'pressed') {
		return {
			state: { tag: 'long-pressed', tapCount: 0 },
			emit: 'long-press',
		};
	}
	return { state };
}

function onSettleTimer(): TimedStateResult {
	return { state: IDLE };
}

function tapTypeForCount(count: number): GestureType {
	switch (count) {
		case 2:
			return 'double-tap';
		case 3:
			return 'triple-tap';
		default:
			return 'tap';
	}
}
