import { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { cn } from '@/lib/cn';

/**
 * Anchored popover. Pass arbitrary content as children. Use Menu instead
 * for simple list-of-options dropdowns.
 *
 * The panel is rendered through a portal into #backreload-root with fixed
 * positioning. An inline `position:absolute` panel gets clipped by any
 * ancestor with `overflow:hidden|auto` — e.g. the Card/table wrapper — so a
 * popover near the bottom of a table was being cut off. Fixed + portal lets
 * it escape every clipping context while staying inside our CSS scope.
 */
export function Popover({
  trigger,
  children,
  align = 'right',
  width = 320,
}: {
  trigger: (open: boolean, toggle: () => void) => React.ReactNode;
  children: (close: () => void) => React.ReactNode;
  align?: 'left' | 'right';
  width?: number;
}) {
  const [open, setOpen] = useState(false);
  const [rect, setRect] = useState<DOMRect | null>(null);
  const anchorRef = useRef<HTMLDivElement>(null);
  const panelRef  = useRef<HTMLDivElement>(null);

  const reposition = () => {
    if (anchorRef.current) setRect(anchorRef.current.getBoundingClientRect());
  };

  // Measure the trigger before paint so the panel appears in the right place.
  useLayoutEffect(() => {
    if (open) reposition();
  }, [open]);

  useEffect(() => {
    if (!open) return;
    const onDown = (e: MouseEvent) => {
      const t = e.target as Node;
      if (anchorRef.current?.contains(t)) return;
      if (panelRef.current?.contains(t))  return;
      setOpen(false);
    };
    const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setOpen(false); };
    const onShift = () => reposition(); // keep the panel pinned to the trigger
    document.addEventListener('mousedown', onDown);
    document.addEventListener('keydown', onKey);
    window.addEventListener('scroll', onShift, true);
    window.addEventListener('resize', onShift);
    return () => {
      document.removeEventListener('mousedown', onDown);
      document.removeEventListener('keydown', onKey);
      window.removeEventListener('scroll', onShift, true);
      window.removeEventListener('resize', onShift);
    };
  }, [open]);

  const host = typeof document !== 'undefined'
    ? document.getElementById('backreload-root')
    : null;

  let panel: React.ReactNode = null;
  if (open && rect) {
    const vw = window.innerWidth;
    const vh = window.innerHeight;
    const left = align === 'right'
      ? Math.max(8, Math.min(rect.right - width, vw - width - 8))
      : Math.max(8, Math.min(rect.left, vw - width - 8));

    // Auto-flip placement. The default is BELOW the trigger, but on
    // narrow viewports or when the trigger sits low on the page (a
    // backup row near the bottom of a table) there's not enough room
    // below for the content to render and the user can't even scroll
    // because the panel itself is offscreen. Flip to ABOVE the trigger
    // when below has less than PREFERRED_MIN px AND above has more.
    const PREFERRED_MIN = 200;
    const spaceBelow = vh - rect.bottom - 16;
    const spaceAbove = rect.top - 16;
    const placeAbove = spaceBelow < PREFERRED_MIN && spaceAbove > spaceBelow;

    // When placing above, anchor by `bottom` so the panel grows
    // upward from the trigger regardless of its actual content height.
    const positionStyles: React.CSSProperties = placeAbove
      ? {
          position: 'fixed',
          bottom: vh - rect.top + 6,
          left,
          width,
          maxHeight: Math.max(160, spaceAbove),
        }
      : {
          position: 'fixed',
          top: rect.bottom + 6,
          left,
          width,
          maxHeight: Math.max(160, spaceBelow),
        };

    panel = (
      <div
        ref={panelRef}
        style={positionStyles}
        className={cn(
          'z-[100000] rounded-xl border border-ink-200 bg-white shadow-pop overflow-auto',
          'animate-[backreload-slide-in_120ms_ease-out]',
        )}
      >
        {children(() => setOpen(false))}
      </div>
    );
  }

  return (
    <div ref={anchorRef} className="relative inline-block">
      {trigger(open, () => setOpen((o) => !o))}
      {panel && host ? createPortal(panel, host) : panel}
    </div>
  );
}
