import { useRef, useCallback } from 'react';

/**
 * Merge the default value with the normal value and update when either change
 * @param props
 * @returns the value and a method to set it
 */
export function useFocusTrap(): {
    setTrap: (node: HTMLElement | null) => void;
} {
    const ref = useRef<HTMLElement | null>(null);

    /**
     * Handle key down events first passing the event to any listeners and then trapping the focus
     *
     * @param event - React.KeyboardEvent<HTMLDivElement>
     */
    const handleKeyDown = useCallback((event) => {
        if (event.code === 'Tab') {
            if (!ref.current) {
                return;
            }

            /**
             * Gets keyboard-focusable elements within a specified element
             * @link https://zellwk.com/blog/keyboard-focusable-elements/
             */
            const focusableEles = Array.from(
                ref.current.querySelectorAll<HTMLElement>(
                    'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])',
                ),
            ).filter((ele) => {
                if (ele.hasAttribute('disabled')) {
                    return false;
                } else if (ele.getAttribute('aria-hidden')) {
                    return false;
                } else if (ele.tabIndex === -1) {
                    return false;
                }

                return true;
            });

            const firstEle = focusableEles[0];
            const lastEle = focusableEles[focusableEles.length - 1];
            const focusedEle = document.activeElement;

            if (event.shiftKey && focusedEle === firstEle) {
                event.preventDefault();

                lastEle.focus();
            } else if (!event.shiftKey && focusedEle === lastEle) {
                event.preventDefault();

                firstEle.focus();
            }
        }
    }, []);

    /**
     * Set the trap
     */
    const setTrap = useCallback(
        (node: HTMLElement | null) => {
            // cleanup
            if (ref.current) {
                ref.current.removeEventListener('keydown', handleKeyDown);
            }

            if (node) {
                // add the listener if it exists
                node.addEventListener('keydown', handleKeyDown);
            }

            // Save a reference to the node
            ref.current = node;
        },
        [handleKeyDown],
    );

    return { setTrap };
}
