import React, { useLayoutEffect, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';

const MENU_GAP = 4;
const VIEWPORT_PAD = 8;

function useAnchoredMenuPosition(
  open: boolean,
  anchorRef: React.RefObject<HTMLElement | null>,
  menuRef: React.RefObject<HTMLElement | null>
) {
  const [coords, setCoords] = useState<{ top: number; left: number } | null>(null);

  useLayoutEffect(() => {
    if (!open) {
      setCoords(null);
      return;
    }

    const update = () => {
      const anchor = anchorRef.current;
      const menu = menuRef.current;
      if (!anchor || !menu) return;

      const anchorRect = anchor.getBoundingClientRect();
      const menuWidth = menu.offsetWidth;
      const menuHeight = menu.offsetHeight;

      let top = anchorRect.bottom + MENU_GAP;
      if (top + menuHeight > window.innerHeight - VIEWPORT_PAD) {
        top = anchorRect.top - menuHeight - MENU_GAP;
      }
      top = Math.max(
        VIEWPORT_PAD,
        Math.min(top, window.innerHeight - menuHeight - VIEWPORT_PAD)
      );

      let left = anchorRect.right - menuWidth;
      left = Math.max(
        VIEWPORT_PAD,
        Math.min(left, window.innerWidth - menuWidth - VIEWPORT_PAD)
      );

      setCoords({ top, left });
    };

    update();
    const raf = requestAnimationFrame(update);
    window.addEventListener('resize', update);
    window.addEventListener('scroll', update, true);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('resize', update);
      window.removeEventListener('scroll', update, true);
    };
  }, [open, anchorRef, menuRef]);

  return coords;
}

interface AnchoredMenuProps {
  open: boolean;
  onClose: () => void;
  anchorRef: React.RefObject<HTMLElement | null>;
  children: React.ReactNode;
  className?: string;
}

export const AnchoredMenu: React.FC<AnchoredMenuProps> = ({
  open,
  onClose,
  anchorRef,
  children,
  className = '',
}) => {
  const menuRef = useRef<HTMLDivElement | null>(null);
  const coords = useAnchoredMenuPosition(open, anchorRef, menuRef);

  useEffect(() => {
    if (!open) return;
    const handlePointerDown = (e: PointerEvent) => {
      const target = e.target as Node;
      if (menuRef.current?.contains(target) || anchorRef.current?.contains(target)) return;
      onClose();
    };
    document.addEventListener('pointerdown', handlePointerDown);
    return () => document.removeEventListener('pointerdown', handlePointerDown);
  }, [open, onClose, anchorRef]);

  if (!open) return null;

  return createPortal(
    <div
      ref={menuRef}
      role="menu"
      className={className}
      style={{
        position: 'fixed',
        top: coords?.top ?? 0,
        left: coords?.left ?? 0,
        visibility: coords ? 'visible' : 'hidden',
        zIndex: 9999,
      }}
      onClick={(e) => e.stopPropagation()}
    >
      {children}
    </div>,
    document.body
  );
};
