import { useIsMobile } from '@/utils/use-is-mobile.composable';
import { ref, type ShallowRef } from 'vue';

type Options = {
  allowItemHover?: boolean;
  position?: 'bottom' | 'top';
};

const defaultOptions: Options = { allowItemHover: true, position: 'bottom' };

export const useFloatingItem = (
  trigger: ShallowRef<HTMLElement | null>,
  floatingItem: ShallowRef<HTMLElement | null>,
  options: Options = defaultOptions,
) => {
  const normalizedOptions = {
    ...defaultOptions,
    ...options,
  };

  const { isMobile } = useIsMobile();
  const floatingItemIsDisplayed = ref(false);

  function getListboxPosition() {
    const triggerRect = trigger.value?.getBoundingClientRect();
    const floatingItemRect = floatingItem.value?.getBoundingClientRect();

    if (!triggerRect || !floatingItemRect) return {};

    if (isMobile.value) {
      return {
        top: `-${floatingItemRect.height + 24}px`,
        left: `-${floatingItemRect.width - triggerRect.width}px`,
      };
    }

    return {
      top:
        options.position === 'top'
          ? `-${floatingItemRect.height - triggerRect.height}px`
          : floatingItem.value?.style.left || '0px',
    };
  }

  function hideFloatingItem(event?: MouseEvent | FocusEvent) {
    if (!floatingItem.value || !trigger.value) return;

    const target = event?.relatedTarget as Node | null;

    // Prevent floating item from being hidden if mouse goes from trigger to listbox, unless specified
    if (
      normalizedOptions.allowItemHover &&
      target &&
      (floatingItem.value.contains(target) || trigger.value.contains(target))
    ) {
      return;
    }

    floatingItem.value.classList.add('mc-sidebar__floating-item--hidden');
    floatingItemIsDisplayed.value = false;

    document.removeEventListener('mousedown', handleClickOutside);
  }

  function handleClickOutside(event: MouseEvent) {
    const target = event.target as Node;
    if (
      !floatingItem.value?.contains(target) &&
      !trigger.value?.contains(target)
    ) {
      hideFloatingItem(event);
    }
  }

  function showFloatingItem() {
    floatingItem.value?.classList.remove('mc-sidebar__floating-item--hidden');
    floatingItemIsDisplayed.value = !floatingItem.value?.classList.contains(
      'mc-sidebar__floating-item--hidden',
    );

    if (!floatingItemIsDisplayed.value) return;

    const { left, top } = getListboxPosition();

    if (!floatingItem.value) return;

    if (top) {
      floatingItem.value.style.top = top;
    }

    if (left) {
      floatingItem.value.style.left = left;
    }
    floatingItemIsDisplayed.value = true;

    document.addEventListener('mousedown', handleClickOutside);
  }

  function onTriggerKeydown(event: KeyboardEvent) {
    if (!floatingItem.value) return;

    if (['ArrowDown', 'Enter', ' '].includes(event.key)) {
      event.preventDefault();
      showFloatingItem();
      focusFirstListboxItem();
    }

    if (event.key === 'Tab') {
      hideFloatingItem();
    }
  }

  function focusFirstListboxItem() {
    const firstItem = floatingItem.value?.querySelector<HTMLElement>(
      'button, [href], [tabindex]:not([tabindex="-1"])',
    );
    firstItem?.focus();
  }

  function onListboxKeydown(event: KeyboardEvent) {
    if (event.key === 'Escape') {
      event.preventDefault();
      hideFloatingItem();
      trigger.value?.focus();
    }
  }

  return {
    floatingItemIsDisplayed,
    hideFloatingItem,
    showFloatingItem,
    onTriggerKeydown,
    onListboxKeydown,
  };
};
