import { describe, it, expect } from 'vitest';
import {
	transition,
	IDLE,
	LONG_PRESS_TIMER,
	SETTLE_TIMER,
	DEFAULT_LONG_PRESS_MS,
	DEFAULT_MULTI_TAP_WINDOW_MS,
} from '../timed-state';

describe('timed-state', () => {
	describe('single tap', () => {
		it('down from idle → pressed, schedules long-press timer', () => {
			const result = transition(IDLE, 'down');
			expect(result.state.tag).toBe('pressed');
			expect(result.state.tapCount).toBe(0);
			expect(result.scheduleTimer).toEqual({
				id: LONG_PRESS_TIMER,
				delayMs: DEFAULT_LONG_PRESS_MS,
			});
		});

		it('up from pressed → released with tap emit', () => {
			const pressed = { tag: 'pressed' as const, tapCount: 0 };
			const result = transition(pressed, 'up');
			expect(result.state.tag).toBe('released');
			expect(result.state.tapCount).toBe(1);
			expect(result.emit).toBe('tap');
			expect(result.cancelTimer).toBe(LONG_PRESS_TIMER);
			expect(result.scheduleTimer).toEqual({
				id: SETTLE_TIMER,
				delayMs: DEFAULT_MULTI_TAP_WINDOW_MS,
			});
		});

		it('settle timer from released → idle', () => {
			const released = { tag: 'released' as const, tapCount: 1 };
			const result = transition(released, 'timer:settle');
			expect(result.state).toEqual(IDLE);
		});
	});

	describe('double tap', () => {
		it('down from released (tapCount=1) → pressed with tapCount=1', () => {
			const released = { tag: 'released' as const, tapCount: 1 };
			const result = transition(released, 'down');
			expect(result.state.tag).toBe('pressed');
			expect(result.state.tapCount).toBe(1);
			expect(result.cancelTimer).toBe(SETTLE_TIMER);
		});

		it('up from pressed (tapCount=1) → released with double-tap emit', () => {
			const pressed = { tag: 'pressed' as const, tapCount: 1 };
			const result = transition(pressed, 'up');
			expect(result.state.tapCount).toBe(2);
			expect(result.emit).toBe('double-tap');
		});
	});

	describe('triple tap', () => {
		it('up from pressed (tapCount=2) → released with triple-tap emit', () => {
			const pressed = { tag: 'pressed' as const, tapCount: 2 };
			const result = transition(pressed, 'up');
			expect(result.state.tapCount).toBe(3);
			expect(result.emit).toBe('triple-tap');
		});
	});

	describe('long press', () => {
		it('long-press timer from pressed → long-pressed with emit', () => {
			const pressed = { tag: 'pressed' as const, tapCount: 0 };
			const result = transition(pressed, 'timer:long-press');
			expect(result.state.tag).toBe('long-pressed');
			expect(result.emit).toBe('long-press');
		});

		it('up from long-pressed → idle', () => {
			const longPressed = { tag: 'long-pressed' as const, tapCount: 0 };
			const result = transition(longPressed, 'up');
			expect(result.state).toEqual(IDLE);
		});

		it('long-press timer ignored in non-pressed state', () => {
			const result = transition(IDLE, 'timer:long-press');
			expect(result.state).toEqual(IDLE);
			expect(result.emit).toBeUndefined();
		});
	});

	describe('drag (move beyond threshold)', () => {
		it('move-beyond-threshold from pressed → idle, cancels long-press', () => {
			const pressed = { tag: 'pressed' as const, tapCount: 0 };
			const result = transition(pressed, 'move-beyond-threshold');
			expect(result.state).toEqual(IDLE);
			expect(result.cancelTimer).toBe(LONG_PRESS_TIMER);
			expect(result.emit).toBeUndefined();
		});

		it('move-beyond-threshold from idle is no-op', () => {
			const result = transition(IDLE, 'move-beyond-threshold');
			expect(result.state).toEqual(IDLE);
		});
	});

	describe('cancel', () => {
		it('cancel from any state → idle, cancels timers', () => {
			const pressed = { tag: 'pressed' as const, tapCount: 1 };
			const result = transition(pressed, 'cancel');
			expect(result.state).toEqual(IDLE);
			expect(result.cancelTimer).toBe(LONG_PRESS_TIMER);
			expect(result.emit).toBeUndefined();
		});
	});
});
