import { useState, useRef, useLayoutEffect } from "react";

/**
 * Hook for calculating and updating menu position with:
 * - requestAnimationFrame throttling for smooth updates
 * - ResizeObserver for trigger element changes
 * - Scrollable parent container detection
 * - Support for both portal and non-portal modes
 *
 * @param {Object} params
 * @param {boolean} params.isOpen - Whether the menu is open
 * @param {React.RefObject} params.triggerRef - Ref to the trigger element (button)
 * @param {React.RefObject} params.menuRef - Ref to the menu element
 * @param {boolean} params.withPortal - Whether using portal mode
 * @param {string} params.dropdownPosition - Force 'top' or 'bottom' position
 * @returns {Object} menuStyle - Style object for positioning the menu
 */
export const useMenuPosition = ({
  isOpen,
  triggerRef,
  menuRef,
  withPortal = false,
  dropdownPosition,
}) => {
  const [menuStyle, setMenuStyle] = useState({
    opacity: 0, // Start hidden to prevent flash
  });
  const [placement, setPlacement] = useState("bottom");

  const positionUpdateRef = useRef(null);
  const lastPositionRef = useRef(null);
  const isHiddenRef = useRef(true);

  useLayoutEffect(() => {
    if (!isOpen || !triggerRef?.current) {
      if (!isHiddenRef.current) {
        setMenuStyle({ opacity: 0 });
        isHiddenRef.current = true;
      }
      return;
    }
    isHiddenRef.current = false;

    const calculatePosition = () => {
      if (!triggerRef.current) return null;

      const triggerRect = triggerRef.current.getBoundingClientRect();
      const viewportWidth = window.innerWidth;
      const viewportHeight = window.innerHeight;
      const scrollX = window.scrollX || document.documentElement.scrollLeft;
      const scrollY = window.scrollY || document.documentElement.scrollTop;

      // Get actual menu height if available, otherwise use max height estimate
      let menuHeight = 320;
      if (menuRef?.current) {
        const menuRect = menuRef.current.getBoundingClientRect();
        menuHeight = menuRect.height || 320;
      }

      const menuWidth = triggerRect.width;
      const spacing = 6; // Gap between trigger and menu
      const viewportTopOffset = 64; // Keep at least 64px from top

      // Calculate available space in each direction
      const spaceBelow = viewportHeight - triggerRect.bottom;
      const spaceAbove = triggerRect.top - viewportTopOffset;

      // Determine position based on dropdownPosition prop or available space
      let position = "bottom";

      if (dropdownPosition === "top" || dropdownPosition === "bottom") {
        position = dropdownPosition;
      } else {
        // Auto-detect best position
        if (spaceBelow >= menuHeight + spacing) {
          position = "bottom";
        } else if (spaceAbove >= menuHeight + spacing) {
          position = "top";
        } else {
          // Use whichever has more space
          position = spaceBelow >= spaceAbove ? "bottom" : "top";
        }
      }

      let top = 0;
      let left = 0;

      if (withPortal) {
        // Portal mode: absolute positioning relative to document
        if (position === "bottom") {
          top = triggerRect.bottom + scrollY + spacing;
        } else {
          // Anchor menu bottom to trigger top so it stays flush when menu height changes (e.g. during search)
          top = triggerRect.top + scrollY - spacing;
        }
        left = triggerRect.left + scrollX;

        // Ensure minimum top offset
        if (position === "bottom") {
          top = Math.max(viewportTopOffset + scrollY, top);
        }
      } else {
        // Non-portal mode: relative positioning to container
        if (position === "bottom") {
          top = triggerRect.height + spacing - 4;
        } else {
          top = -menuHeight - spacing;
        }
        // Left stays at 0 for relative positioning
        left = 0;

        // Ensure overall position is >= 64px from viewport top
        const absoluteY = triggerRect.top;
        const minTopRelative = viewportTopOffset - absoluteY;
        top = Math.max(minTopRelative, top);
      }

      // Adjust horizontal position to keep menu within viewport (portal mode only)
      if (withPortal) {
        let adjustedLeft = left - scrollX;
        if (adjustedLeft + menuWidth > viewportWidth) {
          adjustedLeft = Math.max(0, viewportWidth - menuWidth - 10);
        }
        if (adjustedLeft < 0) {
          adjustedLeft = 10;
        }
        left = adjustedLeft + scrollX;
      }

      return {
        top,
        left,
        width: menuWidth,
        position,
      };
    };

    const applyPosition = (newPosition) => {
      if (!newPosition) return;

      const lastPos = lastPositionRef.current;
      if (
        !lastPos ||
        lastPos.top !== newPosition.top ||
        lastPos.left !== newPosition.left ||
        lastPos.width !== newPosition.width ||
        lastPos.position !== newPosition.position
      ) {
        lastPositionRef.current = {
          top: newPosition.top,
          left: newPosition.left,
          width: newPosition.width,
          position: newPosition.position,
        };

        const style = {
          top: newPosition.top,
          width: newPosition.width + "px",
          minWidth: newPosition.width + "px",
          opacity: 1, // Make visible
        };

        if (withPortal) {
          style.left = newPosition.left;
          style.position = "absolute";
          if (newPosition.position === "top") {
            style.transform = "translateY(-100%)";
          } else {
            style.transform = "none";
          }
        }

        setMenuStyle(style);
        setPlacement(newPosition.position);
      }
    };

    const updatePosition = () => {
      // Cancel any pending animation frame
      if (positionUpdateRef.current !== null) {
        cancelAnimationFrame(positionUpdateRef.current);
      }

      // Use requestAnimationFrame for smooth, batched updates
      positionUpdateRef.current = requestAnimationFrame(() => {
        const newPosition = calculatePosition();
        if (newPosition) {
          applyPosition(newPosition);
        }
        positionUpdateRef.current = null;
      });
    };

    // Initial position update (synchronous)
    const initialPos = calculatePosition();
    if (initialPos) {
      applyPosition(initialPos);
    }

    // ResizeObserver to handle trigger resize
    let resizeObserver = null;
    if (typeof ResizeObserver !== "undefined") {
      resizeObserver = new ResizeObserver(() => {
        updatePosition();
      });
      resizeObserver.observe(triggerRef.current);
      if (menuRef?.current) {
        resizeObserver.observe(menuRef.current);
      }
    }

    // Throttled event handlers
    let resizeFrame = null;
    const handleResize = () => {
      if (resizeFrame !== null) return;
      resizeFrame = requestAnimationFrame(() => {
        updatePosition();
        resizeFrame = null;
      });
    };

    let scrollFrame = null;
    const handleScroll = () => {
      if (scrollFrame !== null) return;
      scrollFrame = requestAnimationFrame(() => {
        updatePosition();
        scrollFrame = null;
      });
    };

    // Find all scrollable parent containers
    const scrollableParents = [];
    let parent = triggerRef.current?.parentElement;
    while (parent) {
      const style = window.getComputedStyle(parent);
      const overflowY = style.overflowY;
      const overflowX = style.overflowX;
      if (
        overflowY === "auto" ||
        overflowY === "scroll" ||
        overflowX === "auto" ||
        overflowX === "scroll"
      ) {
        scrollableParents.push(parent);
      }
      parent = parent.parentElement;
    }

    // Add scroll listeners to all scrollable parents
    scrollableParents.forEach((scrollableParent) => {
      scrollableParent.addEventListener("scroll", handleScroll, {
        passive: true,
      });
    });

    window.addEventListener("resize", handleResize, { passive: true });
    window.addEventListener("scroll", handleScroll, {
      passive: true,
      capture: true,
    });

    return () => {
      if (positionUpdateRef.current !== null) {
        cancelAnimationFrame(positionUpdateRef.current);
        positionUpdateRef.current = null;
      }
      if (resizeFrame !== null) {
        cancelAnimationFrame(resizeFrame);
      }
      if (scrollFrame !== null) {
        cancelAnimationFrame(scrollFrame);
      }
      if (resizeObserver) {
        resizeObserver.disconnect();
      }
      scrollableParents.forEach((scrollableParent) => {
        scrollableParent.removeEventListener("scroll", handleScroll);
      });
      window.removeEventListener("resize", handleResize);
      window.removeEventListener("scroll", handleScroll, true);
      lastPositionRef.current = null;
    };
  }, [isOpen, triggerRef, menuRef, withPortal, dropdownPosition]);

  return { menuStyle, placement };
};

export default useMenuPosition;
