import { describe, it, expect } from 'vitest';
import { specificity } from '../gestures/specificity';
import type { InputEvent, InputPattern, Modifiers } from '../gestures/types';
import { NO_MODIFIERS } from '../gestures/types';

const EMPTY_HELD: any = { byKey: {}, byCode: {} };

function makePointerEvent(overrides: Partial<InputEvent> = {}): InputEvent {
	return {
		kind: 'pointer',
		type: 'tap',
		phase: 'instant',
		source: 'mouse',
		button: 0,
		modifiers: NO_MODIFIERS,
		heldKeys: EMPTY_HELD,
		screenX: 0,
		screenY: 0,
		worldX: 0,
		worldY: 0,
		subject: { kind: 'background' },
		...overrides,
	} as any;
}

function makeKeyEvent(overrides: Partial<any> = {}): InputEvent {
	return {
		kind: 'key',
		phase: 'down',
		key: 'a',
		code: 'KeyA',
		modifiers: NO_MODIFIERS,
		heldKeys: EMPTY_HELD,
		...overrides,
	} as any;
}

describe('gesture specificity', () => {
	describe('type matching', () => {
		it('matches exact type', () => {
			const score = specificity({ type: 'tap' }, makePointerEvent({ type: 'tap' }));
			expect(score).toBeGreaterThan(0);
		});

		it('returns -1 on type mismatch', () => {
			const score = specificity({ type: 'drag' }, makePointerEvent({ type: 'tap' }));
			expect(score).toBe(-1);
		});
	});

	describe('subject matching', () => {
		it('matches subjectKind', () => {
			const score = specificity(
				{ type: 'tap', subjectKind: 'node' },
				makePointerEvent({ type: 'tap', subject: { kind: 'node', id: 'n1' } } as any),
			);
			expect(score).toBeGreaterThan(0);
		});

		it('returns -1 on subjectKind mismatch', () => {
			const score = specificity(
				{ type: 'tap', subjectKind: 'node' },
				makePointerEvent({ type: 'tap', subject: { kind: 'background' } } as any),
			);
			expect(score).toBe(-1);
		});
	});

	describe('source matching', () => {
		it('matches source', () => {
			const s1 = specificity({ type: 'tap', source: 'finger' }, makePointerEvent({ type: 'tap', source: 'finger' } as any));
			expect(s1).toBeGreaterThan(0);
		});

		it('returns -1 on source mismatch', () => {
			const s = specificity({ type: 'tap', source: 'pencil' }, makePointerEvent({ type: 'tap', source: 'mouse' } as any));
			expect(s).toBe(-1);
		});
	});

	describe('modifier scoring', () => {
		it('positive modifier adds score', () => {
			const withMod = specificity(
				{ type: 'tap', modifiers: { shift: true } },
				makePointerEvent({ type: 'tap', modifiers: { shift: true, ctrl: false, alt: false, meta: false } }),
			);
			const without = specificity({ type: 'tap' }, makePointerEvent({ type: 'tap' }));
			expect(withMod).toBeGreaterThan(without);
		});

		it('returns -1 on modifier mismatch', () => {
			const score = specificity(
				{ type: 'tap', modifiers: { shift: true } },
				makePointerEvent({ type: 'tap', modifiers: NO_MODIFIERS }),
			);
			expect(score).toBe(-1);
		});
	});

	describe('button matching', () => {
		it('matches button value', () => {
			const score = specificity(
				{ type: 'tap', button: 2 },
				makePointerEvent({ type: 'tap', button: 2 } as any),
			);
			expect(score).toBeGreaterThan(0);
		});

		it('returns -1 on button mismatch', () => {
			const score = specificity(
				{ type: 'tap', button: 2 },
				makePointerEvent({ type: 'tap', button: 0 } as any),
			);
			expect(score).toBe(-1);
		});
	});

	describe('key matching', () => {
		it('matches key', () => {
			const score = specificity({ kind: 'key', key: 'a' }, makeKeyEvent({ key: 'a' }));
			expect(score).toBeGreaterThan(0);
		});

		it('returns -1 on key mismatch', () => {
			const score = specificity({ kind: 'key', key: 'b' }, makeKeyEvent({ key: 'a' }));
			expect(score).toBe(-1);
		});
	});

	describe('more-specific wins', () => {
		it('type+subject > type alone', () => {
			const event = makePointerEvent({ type: 'tap', subject: { kind: 'node', id: 'n1' } } as any);
			const s1 = specificity({ type: 'tap' }, event);
			const s2 = specificity({ type: 'tap', subjectKind: 'node' }, event);
			expect(s2).toBeGreaterThan(s1);
		});
	});
});
