import type { Instance as PopperInstance, Options as PopperOptions } from '@popperjs/core';
import type { FC, HTMLAttributes, MutableRefObject } from 'react';
import { createPopper } from '@popperjs/core';
import classnames from 'classnames';
import React, { createRef, useCallback, useEffect, useRef, useState } from 'react';

export interface TooltipProps extends HTMLAttributes<HTMLDivElement>, Partial<PopperOptions> {
  delay?: number;
  innerRef?: MutableRefObject<HTMLDivElement>;
  triggerSelector: string;
}

const Tooltip: FC<TooltipProps> = ({
  children,
  className,
  delay = 150,
  innerRef = createRef(),
  modifiers = [],
  onFirstUpdate,
  placement = 'bottom',
  strategy = 'absolute',
  triggerSelector,
}) => {
  const [isVisible, setIsVisible] = useState<boolean>(false);
  const triggerRef = useRef<Element | null>(null);

  const show = useCallback(() => {
    window.setTimeout(() => setIsVisible(true), delay);
  }, [delay]);

  const hide = useCallback(() => {
    window.setTimeout(() => setIsVisible(false), delay);
  }, [delay]);

  // Toggle "visibility" css property as "isVisible" changes

  useEffect(() => {
    if (innerRef.current) {
      // eslint-disable-next-line no-param-reassign
      innerRef.current.style.visibility = isVisible ? 'visible' : '';
    }
  }, [innerRef, isVisible]);

  // Set the "triggerRef" when "triggerSelector" changes

  useEffect(() => {
    triggerRef.current = document.querySelector(triggerSelector);
  }, [triggerSelector]);

  // Create event listeners to toggle "isVisible" based on whether a click is within the target or outside of it

  useEffect(() => {
    triggerRef.current?.addEventListener('mouseenter', show);
    triggerRef.current?.addEventListener('mouseleave', hide);

    return () => {
      triggerRef.current?.removeEventListener('mouseenter', show);
      triggerRef.current?.removeEventListener('mouseleave', hide);
    };
  }, [hide, innerRef, show]);

  // Create tooltip

  useEffect(() => {
    let popper: PopperInstance | undefined;

    if (triggerRef.current && innerRef.current) {
      popper = createPopper(triggerRef.current, innerRef.current, {
        modifiers: [
          {
            name: 'offset',
            options: {
              offset: [0, 15],
            },
          },
          ...modifiers,
        ],
        onFirstUpdate,
        placement,
        strategy,
      });
    }

    return () => popper?.destroy();
  }, [innerRef, modifiers, onFirstUpdate, placement, strategy]);

  return (
    <div className={classnames('tooltip', className)} ref={innerRef} data-testid="Tooltip">
      {children}
      <div data-popper-arrow />
    </div>
  );
};

export default Tooltip;
