import { describe, it, expect } from 'vitest';
import {
	isEditableTarget,
	setHeldKeyValue,
	applyHeldKeyDelta,
} from '../gestures/gesture-provider-utils';

describe('gesture-provider-utils', () => {
	describe('isEditableTarget', () => {
		it('returns false for null', () => {
			expect(isEditableTarget(null)).toBe(false);
		});

		it('returns false for a plain div', () => {
			const div = document.createElement('div');
			expect(isEditableTarget(div)).toBe(false);
		});

		it('returns true for an input element', () => {
			const input = document.createElement('input');
			expect(isEditableTarget(input)).toBe(true);
		});

		it('returns true for a textarea element', () => {
			const textarea = document.createElement('textarea');
			expect(isEditableTarget(textarea)).toBe(true);
		});

		it('returns true for contenteditable element', () => {
			const div = document.createElement('div');
			div.setAttribute('contenteditable', 'true');
			expect(isEditableTarget(div)).toBe(true);
		});

		it('returns true for child of data-no-canvas-keyboard', () => {
			const parent = document.createElement('div');
			parent.setAttribute('data-no-canvas-keyboard', 'true');
			const child = document.createElement('span');
			parent.appendChild(child);
			document.body.appendChild(parent);
			expect(isEditableTarget(child)).toBe(true);
			document.body.removeChild(parent);
		});
	});

	describe('setHeldKeyValue', () => {
		it('returns same reference when no change', () => {
			const state: Record<string, boolean> = { a: true };
			const result = setHeldKeyValue(state, 'a', true);
			expect(result).toBe(state);
		});

		it('adds a key', () => {
			const state: Record<string, boolean> = {};
			const result = setHeldKeyValue(state, 'a', true);
			expect(result).not.toBe(state);
			expect(result.a).toBe(true);
		});

		it('removes a key', () => {
			const state: Record<string, boolean> = { a: true };
			const result = setHeldKeyValue(state, 'a', false);
			expect(result).not.toBe(state);
			expect('a' in result).toBe(false);
		});

		it('returns same reference for removing non-existent key', () => {
			const state: Record<string, boolean> = {};
			const result = setHeldKeyValue(state, 'x', false);
			expect(result).toBe(state);
		});
	});

	describe('applyHeldKeyDelta', () => {
		it('adds key on keydown', () => {
			const current = { byKey: {}, byCode: {} };
			const event = new KeyboardEvent('keydown', { key: 'a', code: 'KeyA' });
			const result = applyHeldKeyDelta(event, current);
			expect(result.byKey.a).toBe(true);
			expect(result.byCode.KeyA).toBe(true);
		});

		it('removes key on keyup', () => {
			const current = { byKey: { a: true as const }, byCode: { KeyA: true as const } };
			const event = new KeyboardEvent('keyup', { key: 'a', code: 'KeyA' });
			const result = applyHeldKeyDelta(event, current);
			expect('a' in result.byKey).toBe(false);
			expect('KeyA' in result.byCode).toBe(false);
		});

		it('returns same reference when no change', () => {
			const current = { byKey: {}, byCode: {} };
			const event = new KeyboardEvent('keyup', { key: 'x', code: 'KeyX' });
			const result = applyHeldKeyDelta(event, current);
			expect(result).toBe(current);
		});
	});
});
