import {
  arrow as arrowMiddleware,
  autoUpdate,
  flip,
  offset,
  useFloating,
} from '@floating-ui/react';
import { clsx } from 'clsx';
import {
  AriaAttributes,
  CSSProperties,
  PropsWithChildren,
  ReactElement,
  ReactNode,
  cloneElement,
  useId,
  useRef,
  useState,
} from 'react';

import { CommonProps, Position } from '../common';
import {
  PositionBottom,
  PositionLeft,
  PositionRight,
  PositionTop,
} from '../common/propsValues/position';

export type TooltipProps = PropsWithChildren<{
  position?: PositionTop | PositionRight | PositionBottom | PositionLeft;
  label: ReactNode;
  id?: string;
}> &
  CommonProps;

const Tooltip = ({
  position = Position.TOP,
  children = undefined,
  label,
  id,
  className,
}: TooltipProps) => {
  const [open, setOpen] = useState(false);
  const arrowRef = useRef<HTMLDivElement | null>(null);

  const fallbackId = useId();
  const tooltipId = id ?? fallbackId;

  const { refs, floatingStyles, placement, middlewareData } = useFloating({
    placement: position,
    middleware: [
      offset(16),
      flip({ fallbackPlacements: [Position.TOP] }),
      // eslint-disable-next-line react-hooks/refs -- arrowRef is passed to floating-ui middleware, legitimate API usage
      arrowMiddleware({ element: arrowRef, padding: 8 }),
    ],
    whileElementsMounted: open ? autoUpdate : undefined,
    open,
  });

  const arrowStyle: CSSProperties = {
    ...(middlewareData.arrow?.x != null ? { left: middlewareData.arrow.x } : {}),
    ...(middlewareData.arrow?.y != null ? { top: middlewareData.arrow.y } : {}),
  };

  return (
    /* eslint-disable react-hooks/refs -- setReference and setFloating are callback refs from floating-ui, safe to pass during render */
    <span
      ref={refs.setReference}
      className="tw-tooltip-container"
      onMouseOver={() => setOpen(true)}
      onFocus={() => setOpen(true)}
      onMouseOut={() => setOpen(false)}
      onBlur={() => setOpen(false)}
    >
      {children
        ? cloneElement(children as ReactElement<Pick<AriaAttributes, 'aria-describedby'>>, {
            'aria-describedby': `${tooltipId}-tooltip`,
          })
        : null}
      <div
        ref={refs.setFloating}
        className={clsx(
          'np-tooltip',
          'np-panel',
          open ? `np-panel--open np-tooltip--open` : null,
          className,
        )}
        style={floatingStyles}
        data-popper-placement={placement}
        aria-hidden={!open}
        role="tooltip"
        id={`${tooltipId}-tooltip`}
      >
        <div className="np-panel__content tooltip-inner">
          {label}
          <div ref={arrowRef} className={clsx('np-panel__arrow')} style={arrowStyle} />
        </div>
      </div>
    </span>
    /* eslint-enable react-hooks/refs */
  );
};

export default Tooltip;
