import { describe, it, expect, beforeEach, vi } from 'vitest';
import {
	registerAction,
	unregisterAction,
	getHandler,
	clearHandlers,
	dispatch,
} from '../gestures/dispatcher';
import type { InputEvent, ResolvedAction, KeyInputEvent, PointerGestureEvent } from '../gestures/types';

function makeKeyEvent(phase: 'down' | 'up' = 'down'): KeyInputEvent {
	return {
		kind: 'key',
		phase,
		key: 'a',
		code: 'KeyA',
		repeat: false,
		modifiers: { shift: false, ctrl: false, alt: false, meta: false },
		heldKeys: { byKey: {}, byCode: {} },
		subject: { kind: 'background' },
	} as KeyInputEvent;
}

function makeResolution(actionId: string): ResolvedAction {
	return {
		actionId,
		binding: { id: 'test-binding', pattern: { kind: 'key' }, actionId },
		contextId: 'test-context',
		score: 100,
		consumed: false,
	};
}

describe('dispatcher', () => {
	beforeEach(() => {
		clearHandlers();
	});

	it('registerAction adds a handler', () => {
		const handler = vi.fn();
		registerAction('test', handler);
		expect(getHandler('test')).toBe(handler);
	});

	it('unregisterAction removes a handler', () => {
		registerAction('test', vi.fn());
		unregisterAction('test');
		expect(getHandler('test')).toBeUndefined();
	});

	it('getHandler returns undefined for unknown', () => {
		expect(getHandler('nonexistent')).toBeUndefined();
	});

	it('clearHandlers empties registry', () => {
		registerAction('a', vi.fn());
		registerAction('b', vi.fn());
		clearHandlers();
		expect(getHandler('a')).toBeUndefined();
		expect(getHandler('b')).toBeUndefined();
	});

	it('dispatch returns true for none action', () => {
		const result = dispatch(makeKeyEvent(), makeResolution('none'));
		expect(result).toBe(true);
	});

	it('dispatch returns false for unknown handler', () => {
		const result = dispatch(makeKeyEvent(), makeResolution('unknown'));
		expect(result).toBe(false);
	});

	it('dispatch invokes simple handler on key down', () => {
		const handler = vi.fn();
		registerAction('test', handler);
		dispatch(makeKeyEvent('down'), makeResolution('test'));
		expect(handler).toHaveBeenCalledOnce();
	});

	it('dispatch does not invoke simple handler on key up', () => {
		const handler = vi.fn();
		registerAction('test', handler);
		dispatch(makeKeyEvent('up'), makeResolution('test'));
		expect(handler).not.toHaveBeenCalled();
	});

	it('dispatch invokes phase handler onDown for key down', () => {
		const onDown = vi.fn();
		registerAction('test', { onDown });
		dispatch(makeKeyEvent('down'), makeResolution('test'));
		expect(onDown).toHaveBeenCalledOnce();
	});

	it('dispatch invokes phase handler onUp for key up', () => {
		const onUp = vi.fn();
		registerAction('test', { onUp });
		dispatch(makeKeyEvent('up'), makeResolution('test'));
		expect(onUp).toHaveBeenCalledOnce();
	});
});
