import {useCallback, useEffect, useReducer, useState} from "react";
//Based on https://www.fullstacklabs.co/blog/keyboard-shortcuts-with-react-hooks
const blacklistedTargets = []; // ["INPUT", "TEXTAREA"];
export const useKeyboardSaveShortcut = (callback) =>
    useKeyboardShortcut(["Control", "S"], callback, {overrideSystem: true});

const keysReducer = (state, action) => {
    switch (action.type) {
        case "set-key-down":
            const keydownState = {...state, [action.key]: true};
            return keydownState;
        case "set-key-up":
            const keyUpState = {...state, [action.key]: false};
            return keyUpState;
        case "reset-keys":
            const resetState = {...action.data};
            return resetState;
        default:
            return state;
    }
};

const useKeyboardShortcut = (shortcutKeys, callback, options) => {
    if (!Array.isArray(shortcutKeys))
        throw new Error(
            "The first parameter to `useKeyboardShortcut` must be an ordered array of `KeyboardEvent.key` strings."
        );

    if (!shortcutKeys.length)
        throw new Error(
            "The first parameter to `useKeyboardShortcut` must contain atleast one `KeyboardEvent.key` string."
        );

    if (!callback || typeof callback !== "function")
        throw new Error(
            "The second parameter to `useKeyboardShortcut` must be a function that will be envoked when the keys are pressed."
        );

    const {overrideSystem} = options || {};
    const initalKeyMapping = shortcutKeys.reduce((currentKeys, key) => {
        currentKeys[key.toLowerCase()] = false;
        return currentKeys;
    }, {});

    const [keys, setKeys] = useReducer(keysReducer, initalKeyMapping);
    const [listenersAdded, setListenersAdded] = useState(false);

    const keydownListener = useCallback(
        (assignedKey) => (keydownEvent) => {
            const loweredKey = assignedKey.toLowerCase();

            if (keydownEvent.repeat) return;
            if (blacklistedTargets.includes(keydownEvent.target.tagName)) return;
            if (loweredKey !== keydownEvent.key.toLowerCase()) return;
            if (keys[loweredKey] === undefined) return;

            if (overrideSystem) {
                keydownEvent.preventDefault();
                disabledEventPropagation(keydownEvent);
            }

            setKeys({type: "set-key-down", key: loweredKey});
            return false;
        },
        [keys, overrideSystem]
    );

    const keyupListener = useCallback(
        (assignedKey) => (keyupEvent) => {
            const raisedKey = assignedKey.toLowerCase();

            if (blacklistedTargets.includes(keyupEvent.target.tagName)) return;
            if (keyupEvent.key.toLowerCase() !== raisedKey) return;
            if (keys[raisedKey] === undefined) return;

            if (overrideSystem) {
                keyupEvent.preventDefault();
                disabledEventPropagation(keyupEvent);
            }

            setKeys({type: "set-key-up", key: raisedKey});
            return false;
        },
        [keys, overrideSystem]
    );

    useEffect(() => {
        if (!Object.values(keys).filter((value) => !value).length) {
            callback(keys);
            setKeys({type: "reset-keys", data: initalKeyMapping});
        } else {
            setKeys({type: null});
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [callback, keys]);

    useEffect(() => {
        if (!listenersAdded) {
            console.log('Added events for keyup and keydown');
            shortcutKeys.forEach((k) => {
                window.addEventListener("keydown", keydownListener(k));
                window.addEventListener("keyup", keyupListener(k))
            });
        }

        setListenersAdded(true);

        return () => {
            shortcutKeys.forEach((k) => {
                window.removeEventListener("keydown", keydownListener(k));
                window.removeEventListener("keyup", keyupListener(k));
            });
        }
    }, [listenersAdded]);

};

export default useKeyboardShortcut;

function disabledEventPropagation(e) {
    if (e) {
        if (e.stopPropagation) {
            e.stopPropagation();
        } else if (window.event) {
            window.event.cancelBubble = true;
        }
    }
}
