// Copyright (c) 2026 PowerOn AG
// All rights reserved.
/**
 * FloatingPortal — renders floating UI on document.body with fixed positioning
 * relative to an anchor element. Escapes ancestor overflow clipping.
 */
import React, { useLayoutEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import styles from './FloatingPortal.module.css';

export type FloatingPlacement = 'top' | 'bottom' | 'auto';

export interface FloatingPortalProps {
  open: boolean;
  anchorRef: React.RefObject<HTMLElement | null>;
  onClose?: () => void;
  placement?: FloatingPlacement;
  offset?: number;
  align?: 'start' | 'center' | 'end';
  /** Keep children mounted while closed (avoids reload lag on reopen). */
  keepMounted?: boolean;
  className?: string;
  children: React.ReactNode;
}

interface FloatingCoords {
  top: number;
  left: number;
  minWidth: number;
}

function _resolvePlacement(
  preferred: FloatingPlacement,
  anchorRect: DOMRect,
  popRect: { width: number; height: number },
  offset: number,
): 'top' | 'bottom' {
  if (preferred === 'top' || preferred === 'bottom') return preferred;
  const spaceBelow = window.innerHeight - anchorRect.bottom - offset;
  const spaceAbove = anchorRect.top - offset;
  if (spaceBelow >= popRect.height) return 'bottom';
  if (spaceAbove >= popRect.height) return 'top';
  return spaceBelow >= spaceAbove ? 'bottom' : 'top';
}

function _computeCoords(
  anchor: HTMLElement,
  popEl: HTMLElement,
  placement: FloatingPlacement,
  offset: number,
  align: 'start' | 'center' | 'end',
): FloatingCoords {
  const margin = 8;
  const anchorRect = anchor.getBoundingClientRect();
  const popW = popEl.offsetWidth || 220;
  const popH = popEl.offsetHeight || 200;
  const resolved = _resolvePlacement(placement, anchorRect, { width: popW, height: popH }, offset);

  let top = resolved === 'bottom'
    ? anchorRect.bottom + offset
    : anchorRect.top - offset - popH;

  let left = anchorRect.left;
  if (align === 'center') {
    left = anchorRect.left + anchorRect.width / 2 - popW / 2;
  } else if (align === 'end') {
    left = anchorRect.right - popW;
  }

  if (left + popW > window.innerWidth - margin) {
    left = window.innerWidth - margin - popW;
  }
  if (left < margin) left = margin;

  if (top + popH > window.innerHeight - margin) {
    top = Math.max(margin, window.innerHeight - margin - popH);
  }
  if (top < margin) top = margin;

  return {
    top,
    left,
    minWidth: Math.max(anchorRect.width, 0),
  };
}

export const FloatingPortal: React.FC<FloatingPortalProps> = ({
  open,
  anchorRef,
  onClose,
  placement = 'auto',
  offset = 4,
  align = 'start',
  keepMounted = false,
  className,
  children,
}) => {
  const layerRef = useRef<HTMLDivElement>(null);
  const [coords, setCoords] = useState<FloatingCoords | null>(null);

  useLayoutEffect(() => {
    if (!open && !keepMounted) {
      setCoords(null);
      return;
    }
    if (!open) return;
    const anchor = anchorRef.current;
    const layer = layerRef.current;
    if (!anchor || !layer) return;

    const _update = () => {
      const next = _computeCoords(anchor, layer, placement, offset, align);
      setCoords(next);
    };

    _update();
    const rafId = requestAnimationFrame(_update);
    window.addEventListener('resize', _update);
    window.addEventListener('scroll', _update, true);
    return () => {
      cancelAnimationFrame(rafId);
      window.removeEventListener('resize', _update);
      window.removeEventListener('scroll', _update, true);
    };
  }, [open, keepMounted, anchorRef, placement, offset, align, children]);

  useLayoutEffect(() => {
    if (!open || !onClose) return;
    const _onPointerDown = (event: MouseEvent) => {
      const layer = layerRef.current;
      const anchor = anchorRef.current;
      const target = event.target as Node;
      if (layer?.contains(target) || anchor?.contains(target)) return;
      onClose();
    };
    document.addEventListener('mousedown', _onPointerDown);
    return () => document.removeEventListener('mousedown', _onPointerDown);
  }, [open, onClose, anchorRef]);

  if (!open && !keepMounted) return null;

  return createPortal(
    <div
      ref={layerRef}
      className={[styles.layer, className].filter(Boolean).join(' ')}
      style={!open ? {
        top: -9999,
        left: -9999,
        visibility: 'hidden',
        pointerEvents: 'none',
      } : coords ? {
        top: coords.top,
        left: coords.left,
        minWidth: coords.minWidth,
        visibility: 'visible',
      } : {
        top: -9999,
        left: -9999,
        visibility: 'hidden',
      }}
    >
      {children}
    </div>,
    document.body,
  );
};

export default FloatingPortal;
