/* eslint-disable react-hooks/exhaustive-deps */
import { useRef, useState, useCallback, useMemo } from 'react';

export interface UseTogglableTabInterface {
  toggleProperty: string;
  singleOpen?: boolean;
  toggleOpenClose?: boolean;
  toggleParentOpen?: boolean;
  onToggleProperty: string;
  initial?: Record<string, boolean>;
  firstOpen?: boolean;
  ariaNavigation?: boolean;
  toggleHook?: (id: string) => void;
}
export interface UseTogglableTabReturn {
  register: (el: string) => any;
  registered: { current: Record<string, HTMLElement> };
  opened: Record<string, boolean>;
  toggle: (id: string) => void;
  setOpened: (
    opened:
      | ((curOpened: Record<string, boolean>) => Record<string, boolean>)
      | Record<string, boolean>
  ) => void;
}

export function useTogglableTab({
  toggleProperty,
  singleOpen,
  onToggleProperty,
  initial,
  firstOpen,
  ariaNavigation,
  toggleHook,
}: UseTogglableTabInterface): UseTogglableTabReturn {
  const [opened, setOpened] = useState(initial || {});
  const registered = useRef<Record<string, HTMLElement>>({});
  const sectionsOrder = useRef<string[]>([]);

  const registerRef = useCallback((el) => {
    if (!el) return;
    const key = el.getAttribute('aria-controls') as string;
    if (registered.current[key]) return;
    registered.current[key] = el;
    sectionsOrder.current.push(key);
    if (firstOpen && sectionsOrder.current.length === 1) {
      setOpened({
        [key]: true,
      });
    }
  }, []);

  const toggle = useCallback((e) => {
    const key = e.currentTarget.getAttribute('aria-controls') as string;
    if (toggleHook) {
      toggleHook(key);
    }
    e.preventDefault();
    if (singleOpen) {
      setOpened({ [key]: true });
    } else {
      setOpened((prev) => ({ ...prev, [key]: !prev[key] }));
    }
  }, []);

  const onKeyDown = useCallback((e) => {
    const key = e.target.getAttribute('aria-controls');
    const currentIndex = sectionsOrder.current.findIndex((k) => k === key);
    const nextKey = sectionsOrder.current[currentIndex + 1];
    const previousKey = sectionsOrder.current[currentIndex - 1];
    switch (e.key) {
      case 'ArrowRight':
      case 'ArrowDown':
        if (nextKey) {
          registered.current[nextKey].focus();
        }
        break;
      case 'ArrowLeft':
      case 'ArrowUp':
        if (previousKey) {
          registered.current[previousKey].focus();
        }
        break;
      case ' ':
      case 'Enter':
        toggle(e);
        break;
    }
  }, []);

  const register = useCallback(
    (key) => {
      const isOpen = !!opened[key];
      return {
        ref: registerRef,
        [toggleProperty]: isOpen,
        [onToggleProperty]: toggle,
        'aria-controls': `${key}`,
        ...(ariaNavigation && { onKeyDown, tabIndex: opened[key] ? 0 : -1 }),
      };
    },
    [opened]
  );

  return useMemo(
    () => ({
      register,
      registered,
      opened,
      toggle,
      setOpened,
    }),
    [opened]
  );
}
