import { MoreHorizontal } from 'lucide-react';
import {
  useEffect,
  useId,
  useLayoutEffect,
  useRef,
  useState,
  type CSSProperties,
} from 'react';
import { createPortal } from 'react-dom';
import { IconButton } from './IconButton';

export interface ActionMenuItem {
  label: string;
  onClick: () => void;
  danger?: boolean;
  disabled?: boolean;
}

export interface ActionMenuProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  label: string;
  items: ActionMenuItem[];
  disabled?: boolean;
}

interface ActionMenuPosition {
  top: number;
  left: number;
  width: number;
  placement: 'top' | 'bottom';
}

const MENU_WIDTH = 190;
const VIEWPORT_MARGIN = 12;
const MENU_OFFSET = 6;

export function ActionMenu({
  open,
  onOpenChange,
  label,
  items,
  disabled,
}: ActionMenuProps) {
  const menuId = useId();
  const triggerRef = useRef<HTMLDivElement>(null);
  const popoverRef = useRef<HTMLDivElement>(null);
  const [position, setPosition] = useState<ActionMenuPosition>({
    top: 0,
    left: 0,
    width: MENU_WIDTH,
    placement: 'bottom',
  });

  useEffect(() => {
    if (disabled && open) onOpenChange(false);
  }, [disabled, onOpenChange, open]);

  useLayoutEffect(() => {
    if (!open) return;

    const updatePosition = () => {
      const trigger = triggerRef.current;
      if (!trigger) return;

      const rect = trigger.getBoundingClientRect();
      const width = Math.min(MENU_WIDTH, Math.max(180, window.innerWidth - VIEWPORT_MARGIN * 2));
      const popoverHeight = popoverRef.current?.offsetHeight ?? 0;
      const left = Math.max(
        VIEWPORT_MARGIN,
        Math.min(rect.right - width, window.innerWidth - width - VIEWPORT_MARGIN),
      );
      let top = rect.bottom + MENU_OFFSET;
      let placement: ActionMenuPosition['placement'] = 'bottom';

      if (
        popoverHeight
        && top + popoverHeight > window.innerHeight - VIEWPORT_MARGIN
        && rect.top > popoverHeight + VIEWPORT_MARGIN + MENU_OFFSET
      ) {
        top = rect.top - popoverHeight - MENU_OFFSET;
        placement = 'top';
      }

      const maxTop = popoverHeight
        ? window.innerHeight - popoverHeight - VIEWPORT_MARGIN
        : window.innerHeight - VIEWPORT_MARGIN;

      setPosition({
        top: Math.max(VIEWPORT_MARGIN, Math.min(top, maxTop)),
        left,
        width,
        placement,
      });
    };

    updatePosition();
    const frame = window.requestAnimationFrame(updatePosition);
    window.addEventListener('resize', updatePosition);
    window.addEventListener('scroll', updatePosition, true);

    return () => {
      window.cancelAnimationFrame(frame);
      window.removeEventListener('resize', updatePosition);
      window.removeEventListener('scroll', updatePosition, true);
    };
  }, [open]);

  useEffect(() => {
    if (!open) return;

    const closeOnOutsidePointer = (event: PointerEvent) => {
      const target = event.target as Node;
      if (triggerRef.current?.contains(target)) return;
      if (popoverRef.current?.contains(target)) return;
      onOpenChange(false);
    };

    const closeOnEscape = (event: KeyboardEvent) => {
      if (event.key !== 'Escape') return;
      onOpenChange(false);
    };

    document.addEventListener('pointerdown', closeOnOutsidePointer);
    document.addEventListener('keydown', closeOnEscape);

    return () => {
      document.removeEventListener('pointerdown', closeOnOutsidePointer);
      document.removeEventListener('keydown', closeOnEscape);
    };
  }, [onOpenChange, open]);

  const popoverStyle = {
    top: position.top,
    left: position.left,
    width: position.width,
  } as CSSProperties;

  return (
    <div className="ui-action-menu" ref={triggerRef}>
      <IconButton
        label={label}
        icon={<MoreHorizontal size={20} strokeWidth={2} />}
        onClick={() => onOpenChange(!open)}
        disabled={disabled}
        aria-controls={open ? menuId : undefined}
        aria-expanded={open}
        aria-haspopup="menu"
      />
      {open && typeof document !== 'undefined' ? createPortal(
        <div
          className="ui-action-menu-popover"
          data-placement={position.placement}
          id={menuId}
          ref={popoverRef}
          role="menu"
          style={popoverStyle}
        >
          {items.map((item) => (
            <button
              type="button"
              role="menuitem"
              className={item.danger ? 'is-danger' : ''}
              onClick={() => {
                onOpenChange(false);
                item.onClick();
              }}
              disabled={item.disabled}
              key={item.label}
            >
              {item.label}
            </button>
          ))}
        </div>,
        document.body,
      ) : null}
    </div>
  );
}

