import type { Directive, DirectiveBinding } from 'vue';

interface DebounceBinding extends DirectiveBinding {
	value: () => void;
	arg?: string;
	modifiers?: {
		[key: string]: boolean;
	};
}

const debounceTimers = new Map<string, number>();

const debounce = (fn: () => void, key: string, delay = 300) => {
	if (debounceTimers.has(key)) {
		clearTimeout(debounceTimers.get(key) as number);
	}

	const timer = setTimeout(() => {
		fn();
		debounceTimers.delete(key);
	}, delay) as unknown as number;

	debounceTimers.set(key, timer);
};

export const debounceDirective: Directive = {
	mounted(el: HTMLElement, binding: DebounceBinding) {
		const fn = binding.value;
		const key = binding.arg || `debounce_${Date.now()}_${Math.random()}`;
		const delay = binding.modifiers?.fast ? 200 : binding.modifiers?.slow ? 500 : 300;

		el.addEventListener('click', (event: Event) => {
			event.preventDefault();
			event.stopPropagation();
			debounce(fn, key, delay);
		});

		el.dataset.debounceKey = key;
	},
	unmounted(el: HTMLElement) {
		const key = el.dataset.debounceKey;
		if (key && debounceTimers.has(key)) {
			clearTimeout(debounceTimers.get(key) as number);
			debounceTimers.delete(key);
		}
	},
};

export default debounceDirective;
