import { useNavigate, useLocation } from 'react-router-dom';
import { useCallback, useEffect, useState } from 'react';

import type { MenuItemProps } from '../../types/sidebar';

import { useMenuItemExpanded } from './use-menu-item-expanded';
import { useCollapse } from './use-collapse';
import { loadAndNavigate } from '../../utils/load-and-navigate';
import { withoutPathPrefix, withPathPrefix } from '../../utils/urls';

type NestedMenuProps = MenuItemProps & {
  labelRef?: React.RefObject<HTMLElement | null>;
  nestedMenuRef?: React.RefObject<HTMLDivElement | null>;
};

export function useNestedMenu({ item, labelRef, nestedMenuRef }: NestedMenuProps) {
  const [isExpanded, setIsExpanded] = useMenuItemExpanded(item);
  // we need to know when the item is collapsed after transition to remove children from DOM
  const [canUnmount, setCanUnmount] = useState(!isExpanded);

  const navigate = useNavigate();
  const location = useLocation();

  const { style } = useCollapse({
    isExpanded,
    collapseElRef: nestedMenuRef || { current: null },
    onTransitionStateChange: (state) => {
      if (state === 'collapseEnd') {
        setCanUnmount(true);
      }
      if (state === 'expandStart') {
        setCanUnmount(false);
      }
      // signal that used in e2e tests to wait for the item to be expanded
      if (state === 'expandEnd') {
        labelRef?.current?.dispatchEvent(new CustomEvent('menu:expand-end', { bubbles: true }));
      }
    },
  });

  function scrollIfNeeded(el: Element, centerIfNeeded: boolean = false) {
    const rect = el.getBoundingClientRect();
    const isInViewport =
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth);

    // Only scroll if element is in viewport to prevent page jumping
    // @ts-ignore
    if (isInViewport && typeof el.scrollIntoViewIfNeeded === 'function') {
      // @ts-ignore
      el.scrollIntoViewIfNeeded(centerIfNeeded);
    }
  }

  // scroll to active element if needed
  useEffect(() => {
    if (item.active && labelRef && labelRef.current) {
      scrollIfNeeded(labelRef.current, true); // center item on the first scroll
    }
  }, [labelRef, item.active]);

  // scroll to expanded element if needed (position could change after collapse)
  useEffect(() => {
    if (item.active && isExpanded && labelRef && labelRef.current) {
      scrollIfNeeded(labelRef.current);
    }
  }, [labelRef, isExpanded, item.active]);

  const handleExpand = useCallback(async () => {
    if (
      item.expanded === 'always' ||
      (item.link && item.hasActiveSubItem && item.link !== withoutPathPrefix(location.pathname))
    ) {
      return;
    }

    const [firstChild] = item.items;
    if (!isExpanded && item.selectFirstItemOnExpand && firstChild.link) {
      await loadAndNavigate({ navigate, to: withPathPrefix(firstChild.link) });
    }
    setIsExpanded(!isExpanded);
  }, [item, isExpanded, navigate, location.pathname, setIsExpanded]);

  return {
    isExpanded,
    canUnmount,
    style,
    handleExpand,
  };
}
