import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';

function getOS() {
	let userAgent = window.navigator.userAgent.toLowerCase(),
		macosPlatforms = /(macintosh|macintel|macppc|mac68k|macos)/i,
		windowsPlatforms = /(win32|win64|windows|wince)/i,
		iosPlatforms = /(iphone|ipad|ipod)/i,
		os = null;

	if (macosPlatforms.test(userAgent)) {
		os = 'macos';
	} else if (iosPlatforms.test(userAgent)) {
		os = 'ios';
	} else if (windowsPlatforms.test(userAgent)) {
		os = 'windows';
	} else if (/android/.test(userAgent)) {
		os = 'android';
	} else if (!os && /linux/.test(userAgent)) {
		os = 'linux';
	}

	return os;
}

const useKeyPress = (
	keys: string[],
	modifiers: false | 'ctrl' | 'shift' | 'alt' | 'meta' = false,
	callback: Function,
	nodes?: Node[]
) => {
	const os = getOS();

	// implement the callback ref pattern
	const callbackRef = useRef(callback);
	useLayoutEffect(() => {
		callbackRef.current = callback;
	});

	// handle what happens on key press
	const handleKeyPress = useCallback(
		(event: Event) => {
			// check if one of the key is part of the ones we want
			if (keys.some((key) => (event as KeyboardEvent).key === key)) {
				if (modifiers) {
					if (
						modifiers === 'ctrl' &&
						((event as KeyboardEvent).ctrlKey ||
							(os === 'macos' && (event as KeyboardEvent).metaKey))
					) {
						if (keys.length === 1 && keys[0] === 's') {
							event.preventDefault();
						}
						callbackRef.current(event);
					} else if (
						modifiers === 'shift' &&
						(event as KeyboardEvent).shiftKey
					) {
						callbackRef.current(event);
					} else if (modifiers === 'alt' && (event as KeyboardEvent).altKey) {
						callbackRef.current(event);
					} else if (modifiers === 'meta' && (event as KeyboardEvent).metaKey) {
						callbackRef.current(event);
					}
				} else {
					callbackRef.current(event);
				}
			}
		},
		[keys]
	);

	useEffect(() => {
		// target is either the provided node or the document
		const targetNodes = nodes ?? [document];

		if (targetNodes && targetNodes.length) {
			targetNodes.forEach((node) => {
				node && node.addEventListener('keydown', handleKeyPress);
			});
		}

		// remove the event listener
		return () => {
			if (targetNodes && targetNodes.length) {
				targetNodes.forEach((node) => {
					node && node.removeEventListener('keydown', handleKeyPress);
				});
			}
		};
	}, [handleKeyPress, nodes]);
};

export default useKeyPress;
