import styled from '@emotion/styled';
import { omit } from 'lodash-es';
import React, {
  DetailedHTMLProps,
  ForwardedRef,
  HTMLAttributes,
  forwardRef,
  useCallback,
  useEffect,
  useId,
  useRef,
  useState,
} from 'react';
import { createPortal } from 'react-dom';

import { useAccessibleMenuToggle } from '@/hooks/useAccessibleMenuToggle';
import { useBreakpointMd } from '@/hooks/useBreakpoint';
import { createContext, useContext } from '@/hooks/useContextSelector';
import { TriangleRightIcon } from '@/icons';

import Button, { Props as ButtonProps, ButtonShape } from '../button/Button';

const TouchTargetWrapper = styled.div`
  position: fixed;
  z-index: 100000;
  padding: 8px;
  overflow-y: auto;
`;

const MobileOverlay = styled.div`
  position: fixed;
  z-index: -1;
  background-color: rgba(0, 0, 0, 0.5);
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  backdrop-filter: blur(4px);
`;

const VisibleWrapper = styled.div`
  /* 
    note: for some reason we don't have access to css variables here :S
    possibly because the portal that renders it goes too far out
  */
  background-color: var(--color-background-tertiary);
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  white-space: nowrap;
  font-size: 12px;
  box-shadow: 0 3px 5px 0 rgba(0, 0, 0, 0.3);
  overflow: hidden;
`;

const ItemWrapper = styled.div`
  display: contents;
  > * > span {
    justify-content: flex-start;
    font-size: 14px;
  }
  svg {
    font-size: 16px;
    flex: 0 0 auto;
  }
`;

const MenuHeaderWrapper = styled.div`
  padding: 10px 16px;
`;

const MenuTitle = styled.h3`
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--color-foreground-secondary);
`;

export const ContextMenuGroup = styled.div`
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0;
  & + & {
    border-top: 1px solid var(--color-border-secondary);
  }
`;

export const ContextMenuItemMainText = styled.span`
  font-size: 14px;
  flex-grow: 1;
  text-align: left;
  margin-right: 10px;
  display: inline-block;
`;

export const ContextMenuItemSubtext = styled.span`
  font-size: 12px;
  color: var(--color-foreground-inactive);
  text-transform: none;
  font-family: var(--font-family-serif);
  margin-bottom: -2px;
`;

export const CloseMenuContext = createContext<(allParents?: boolean) => void>(
  () => {}
);
export const useCloseMenuContext = () => useContext(CloseMenuContext);
export const CloseMenuContextProvider = ({
  children,
  onClose,
}: {
  children: React.ReactNode;
  onClose: (allParents?: boolean) => void;
}) => (
  <CloseMenuContext.Provider value={onClose}>
    {children}
  </CloseMenuContext.Provider>
);

export const ContextMenuHeader = (
  props: DetailedHTMLProps<
    HTMLAttributes<HTMLHeadingElement>,
    HTMLHeadingElement
  > & { onClose: () => void }
) => {
  return (
    <MenuHeaderWrapper className='bg-quaternary'>
      <MenuTitle {...props} />
    </MenuHeaderWrapper>
  );
};

export const ContextMenuItem = ({
  keepMenusOpen,
  ...props
}: ButtonProps & { keepMenusOpen?: boolean }) => {
  const onClose = useCloseMenuContext();
  return (
    <ItemWrapper className='context-menu-item'>
      <Button
        shape={ButtonShape.Rectangle}
        {...(props as any)}
        tabIndex={0}
        onClick={
          props.onClick
            ? (...args) => {
                if (!keepMenusOpen) onClose(true);
                (props.onClick as any)?.(...args);
              }
            : undefined
        }
      />
    </ItemWrapper>
  );
};

const SafeTriangleSVG = styled.svg`
  display: block;
  position: fixed;
  pointer-events: none;
  z-index: 10000;
  overflow: visible;
  path {
    pointer-events: auto;
    fill: red;
    opacity: 0;
  }
`;

const SafeTriangle = ({
  mouseoverId,
  menuWrapperRef,
  triggerWrapperRef,
}: {
  mouseoverId: string;
  menuWrapperRef: React.RefObject<HTMLDivElement | null>;
  triggerWrapperRef: React.RefObject<HTMLDivElement | null>;
}) => {
  const mouseListenerRef = useRef<(e: MouseEvent) => void>(() => {});
  const lastMouseCoordsRef = useRef<{ x: number; y: number } | null>(null);
  const expireTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  const onClose = useCloseMenuContext();

  const getPath = useCallback(
    (
      path: SVGPathElement,
      svgRect: DOMRect,
      menuRect: DOMRect,
      clientX: number,
      clientY: number
    ): string | false => {
      const { left, top, right, bottom } = menuRect;
      const triggerWrapper = triggerWrapperRef.current;
      const triggerWrapperRect = triggerWrapper?.getBoundingClientRect();
      if (!triggerWrapperRect) {
        return false;
      }

      const insideTrigger =
        clientX > triggerWrapperRect.left &&
        clientX < triggerWrapperRect.right &&
        clientY > triggerWrapperRect.top &&
        clientY < triggerWrapperRect.bottom;

      // Calculate center of parent rectangle
      const centerX = (left + right) / 2;
      const centerY = (top + bottom) / 2;

      const lastDeltaX = (lastMouseCoordsRef.current?.x ?? Infinity) - centerX;
      const lastDeltaY = (lastMouseCoordsRef.current?.y ?? Infinity) - centerY;
      const lastDelta = Math.sqrt(
        lastDeltaX * lastDeltaX + lastDeltaY * lastDeltaY
      );

      const deltaX = clientX - centerX;
      const deltaY = clientY - centerY;
      const currentDelta = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

      if (currentDelta < lastDelta || insideTrigger) {
        lastMouseCoordsRef.current = { x: clientX, y: clientY };
      } else {
        return false;
      }

      if (!insideTrigger) {
        if (expireTimeoutRef.current) {
          clearTimeout(expireTimeoutRef.current);
        }
        expireTimeoutRef.current = setTimeout(() => {
          expireTimeoutRef.current = null;
          if (path.parentElement) path.parentElement.style.display = 'none';
          window.removeEventListener('mousemove', mouseListenerRef.current);
          onClose();
        }, 100);
      }

      const clientAngle = Math.atan2(deltaY, deltaX);

      // Define the four corners with their angles
      const corners = [
        { x: left, y: top, angle: Math.atan2(top - centerY, left - centerX) }, // top-left
        { x: right, y: top, angle: Math.atan2(top - centerY, right - centerX) }, // top-right
        {
          x: right,
          y: bottom,
          angle: Math.atan2(bottom - centerY, right - centerX),
        }, // bottom-right
        {
          x: left,
          y: bottom,
          angle: Math.atan2(bottom - centerY, left - centerX),
        }, // bottom-left
      ];

      // Add client point
      let allPoints = [
        ...corners,
        { x: clientX, y: clientY, angle: clientAngle },
      ];

      // Sort by angle (clockwise from positive x-axis)
      allPoints.sort((a, b) => a.angle - b.angle);
      allPoints = allPoints.map((p) => {
        const deltaX = p.x - centerX;

        // expand x by a couple px
        let newX = p.x + (deltaX < 0 ? -2 : 2);
        let newY = p.y;

        // move to be relative to the svg
        newX -= svgRect.left;
        newY -= svgRect.top;

        return { ...p, x: newX, y: newY };
      });

      // Build the path
      const pathParts = allPoints.map((point, index) =>
        index === 0 ? `M ${point.x} ${point.y}` : `L ${point.x} ${point.y}`
      );

      return pathParts.join(' ') + ' Z';
    },
    [onClose, triggerWrapperRef]
  );

  const receivePathRef = useCallback(
    (path: SVGPathElement | null) => {
      window.removeEventListener('mousemove', mouseListenerRef.current);

      if (!path) {
        return;
      }

      mouseListenerRef.current = (e) => {
        const parent = path.parentElement;
        if (!parent) return;

        parent.setAttribute(
          'viewbox',
          `0 0 ${window.innerWidth} ${window.innerHeight}`
        );

        const menuWrapper = menuWrapperRef.current;
        if (!menuWrapper) return;

        const menuWrapperRect = menuWrapper.getBoundingClientRect();
        if (menuWrapperRect.bottom > window.innerHeight) return;

        if (
          e.clientX > menuWrapperRect.left &&
          e.clientX < menuWrapperRect.right &&
          e.clientY > menuWrapperRect.top &&
          e.clientY < menuWrapperRect.bottom
        ) {
          const parent = path.parentElement;
          if (parent) {
            parent.style.display = 'none';
          }
          if (expireTimeoutRef.current) clearTimeout(expireTimeoutRef.current);
          window.removeEventListener('mousemove', mouseListenerRef.current);
          return;
        }
        const pathDef = getPath(
          path,
          path.parentElement.getBoundingClientRect(),
          menuWrapperRect,
          e.clientX,
          e.clientY
        );
        if (pathDef !== false) {
          path.setAttribute('d', pathDef);
        }
      };

      window.addEventListener('mousemove', mouseListenerRef.current);
    },
    [getPath]
  );

  useEffect(() => {
    return () => {
      window.removeEventListener('mousemove', mouseListenerRef.current);
    };
  }, []);

  return (
    <SafeTriangleSVG id={useId()} data-mouseover-id={mouseoverId}>
      <path ref={receivePathRef} />
    </SafeTriangleSVG>
  );
};

export const ContextMenu = forwardRef(function ContextMenu(
  {
    children,
    placement = 'bottom-left',
    className,
    clientCoords,
    mouseoverId,
  }: {
    children: React.ReactNode[] | React.ReactNode;
    placement?:
      | 'bottom-left'
      | 'bottom-right'
      | 'bottom-middle'
      | 'top-left'
      | 'top-right'
      | 'top-middle'
      | 'left'
      | 'right';
    className?: string;
    clientCoords?: {
      top?: number;
      left?: number;
      right?: number;
      bottom?: number;
    };
    onMouseLeave?: () => void;
    onMouseEnter?: () => void;
    mouseoverId?: string;
  },
  ref: ForwardedRef<HTMLDivElement>
) {
  const wrapperRef = useRef<HTMLDivElement>(null);
  const anchorPlaceholderRef = useRef<HTMLDivElement>(null);
  const isMobile = !useBreakpointMd();

  useEffect(() => {
    const wrapperElement = wrapperRef.current;
    const anchorElement = anchorPlaceholderRef.current;

    if (!wrapperElement || !anchorElement) {
      return;
    }

    const positionMenu = () => {
      if (!wrapperRef.current || !anchorPlaceholderRef.current) return;

      const localWrapperElement = wrapperRef.current;
      const localAnchorElement = anchorPlaceholderRef.current;

      if (isMobile) {
        localWrapperElement.style.top = 'auto';
        localWrapperElement.style.maxHeight = '100vh';
        localWrapperElement.style.left = '0';
        localWrapperElement.style.right = '0';
        localWrapperElement.style.bottom = '0';
        localWrapperElement.style.marginLeft = '';
        localWrapperElement.style.marginTop = '';
        return;
      }

      let calculatedTop = 0;
      let calculatedLeft = 0;
      const screenEdgeMargin = 10;
      // Reset all positioning styles to ensure clean slate before calculations
      // This prevents any previous styles from interfering with getBoundingClientRect() measurements
      // and ensures consistent behavior when switching between clientCoords and placement-based positioning
      localWrapperElement.style.top = 'auto';
      localWrapperElement.style.left = 'auto';
      localWrapperElement.style.right = 'auto';
      localWrapperElement.style.bottom = 'auto';
      localWrapperElement.style.marginLeft = '';
      localWrapperElement.style.marginTop = '';

      if (clientCoords) {
        // When clientCoords are provided, temporarily apply them to measure the actual position
        // This allows us to support both top/left and right/bottom positioning while still
        // using getBoundingClientRect() for accurate measurements
        if (clientCoords.top !== undefined)
          localWrapperElement.style.top = `${clientCoords.top}px`;
        if (clientCoords.left !== undefined)
          localWrapperElement.style.left = `${clientCoords.left}px`;
        if (clientCoords.right !== undefined)
          localWrapperElement.style.right = `${clientCoords.right}px`;
        if (clientCoords.bottom !== undefined)
          localWrapperElement.style.bottom = `${clientCoords.bottom}px`;

        // Get the actual position after applying clientCoords
        const rectAfterClientCoords =
          localWrapperElement.getBoundingClientRect();
        calculatedTop = rectAfterClientCoords.top;
        calculatedLeft = rectAfterClientCoords.left;

        // Clear temporary styles before applying final position
        localWrapperElement.style.top = 'auto';
        localWrapperElement.style.left = 'auto';
        localWrapperElement.style.right = 'auto';
        localWrapperElement.style.bottom = 'auto';
      } else {
        // When using placement prop, calculate position relative to the anchor element's parent
        const parent = localAnchorElement.parentElement;
        if (!parent) return;

        const parentRect = parent.getBoundingClientRect();
        const menuWidth = localWrapperElement.offsetWidth;
        const menuHeight = localWrapperElement.offsetHeight;

        let correctedPlacement = placement;
        if (
          placement === 'right' &&
          parentRect.right + menuWidth > window.innerWidth
        ) {
          correctedPlacement = 'left';
        } else if (placement === 'left' && parentRect.left - menuWidth < 0) {
          correctedPlacement = 'right';
        }

        // Calculate initial position based on placement prop
        // For top placements, we need to account for menu height to position above the anchor
        // For left placements, we need to account for menu width to align with right edge
        if (correctedPlacement === 'bottom-left') {
          calculatedTop = parentRect.bottom;
          calculatedLeft = parentRect.right - menuWidth;
        } else if (correctedPlacement === 'bottom-right') {
          calculatedTop = parentRect.bottom;
          calculatedLeft = parentRect.left;
        } else if (correctedPlacement === 'top-left') {
          calculatedTop = parentRect.top - menuHeight;
          calculatedLeft = parentRect.right - menuWidth;
        } else if (correctedPlacement === 'top-right') {
          calculatedTop = parentRect.top - menuHeight;
          calculatedLeft = parentRect.left;
        } else if (correctedPlacement === 'top-middle') {
          calculatedTop = parentRect.top - menuHeight;
          calculatedLeft =
            parentRect.left + parentRect.width / 2 - menuWidth / 2;
        } else if (correctedPlacement === 'bottom-middle') {
          calculatedTop = parentRect.bottom;
          calculatedLeft =
            parentRect.left + parentRect.width / 2 - menuWidth / 2;
        } else if (correctedPlacement === 'left') {
          calculatedTop = parentRect.top;
          calculatedLeft = parentRect.left - menuWidth;
        } else if (correctedPlacement === 'right') {
          calculatedTop = parentRect.top;
          calculatedLeft = parentRect.right;
        }
      }

      // Get current menu dimensions and viewport size for collision detection
      const menuRect = localWrapperElement.getBoundingClientRect();
      const viewportWidth = window.innerWidth;
      const viewportHeight = window.innerHeight;

      let finalTop = calculatedTop;
      let finalLeft = calculatedLeft;

      // Ensure menu stays within viewport bounds with margin
      // Handle both positive dimensions and edge cases where dimensions might be 0 or NaN
      if (menuRect.height > 0) {
        if (finalTop < screenEdgeMargin) {
          finalTop = screenEdgeMargin;
        } else if (
          finalTop + menuRect.height >
          viewportHeight - screenEdgeMargin
        ) {
          finalTop = Math.max(
            screenEdgeMargin,
            viewportHeight - screenEdgeMargin - menuRect.height
          );
        }
      } else if (isNaN(finalTop)) {
        finalTop = screenEdgeMargin;
      }

      if (menuRect.width > 0) {
        if (finalLeft < screenEdgeMargin) {
          finalLeft = screenEdgeMargin;
        } else if (
          finalLeft + menuRect.width >
          viewportWidth - screenEdgeMargin
        ) {
          finalLeft = Math.max(
            screenEdgeMargin,
            viewportWidth - screenEdgeMargin - menuRect.width
          );
        }
      } else if (isNaN(finalLeft)) {
        finalLeft = screenEdgeMargin;
      }

      localWrapperElement.style.top = `${finalTop}px`;
      localWrapperElement.style.left = `${finalLeft}px`;

      localWrapperElement.style.right = 'auto';
      localWrapperElement.style.bottom = 'auto';
      localWrapperElement.style.marginLeft = '';
      localWrapperElement.style.marginTop = '';

      localWrapperElement.style.maxHeight = `${window.innerHeight - 2 * screenEdgeMargin}px`;
    };

    // Use requestAnimationFrame to ensure smooth updates and prevent layout thrashing
    const rafId = requestAnimationFrame(positionMenu);

    // Watch for size changes in the menu itself (e.g., content changes)
    const resizeObserver = new ResizeObserver(() => {
      requestAnimationFrame(positionMenu);
    });
    if (wrapperElement) {
      resizeObserver.observe(wrapperElement);
    }

    // Update position on window resize and scroll
    window.addEventListener('resize', positionMenu);
    window.addEventListener('scroll', positionMenu, true);

    return () => {
      // Clean up all event listeners and observers to prevent memory leaks
      cancelAnimationFrame(rafId);
      if (wrapperElement) {
        resizeObserver.unobserve(wrapperElement);
      }
      resizeObserver.disconnect();
      window.removeEventListener('resize', positionMenu);
      window.removeEventListener('scroll', positionMenu, true);
    };
  }, [clientCoords, placement, isMobile]);

  const receiveWrapperRef = useCallback(
    (el: HTMLDivElement) => {
      if (ref) {
        if (typeof ref === 'function') {
          ref(el);
        } else {
          ref.current = el;
        }
      }
      wrapperRef.current = el;
    },
    [ref]
  );

  const onClose = useCloseMenuContext();

  return (
    <div
      ref={anchorPlaceholderRef}
      className='relative h-0 w-0'
      onContextMenu={(e) => e.stopPropagation()}
    >
      {createPortal(
        <>
          <TouchTargetWrapper
            data-mouseover-id={mouseoverId}
            ref={receiveWrapperRef}
            data-context-menu
          >
            {isMobile && <MobileOverlay onClick={() => onClose()} />}
            <VisibleWrapper className={className}>{children}</VisibleWrapper>
          </TouchTargetWrapper>
        </>,
        document.body
      )}
    </div>
  );
});

const useContextMenuController = ({
  openOnMouseOver = false,
  onOpenChange,
}: {
  openOnMouseOver?: boolean;
  onOpenChange?: (open: boolean) => void;
} = {}) => {
  const [menuOpen, setMenuOpen] = useState(false);

  const updateMenuOpen = useCallback(
    (open: boolean) => {
      setMenuOpen(open);
      onOpenChange?.(open);
    },
    [onOpenChange]
  );

  const isMobile = !useBreakpointMd();

  useEffect(() => {
    if (menuOpen && !isMobile) {
      const onKeyDown = (e: KeyboardEvent) => {
        if (e.key === 'Escape') {
          updateMenuOpen(false);
        }
      };
      const onMouseDown = (e: MouseEvent) => {
        if (e.target instanceof HTMLElement || e.target instanceof SVGElement) {
          // Keep this menu open if clicking inside it
          if (e.target.closest('[data-context-menu]')) {
            return;
          }
          // Check if clicking a menu trigger
          const clickedTrigger = e.target.closest(
            '[data-context-menu-trigger]'
          );
          if (clickedTrigger) {
            // If it's OUR trigger, let it handle toggling (don't close here)
            if (clickedTrigger.getAttribute('data-button-id') === buttonId) {
              return;
            }
            // If it's a DIFFERENT trigger, close this menu
            // (the other menu will open via its own handler)
            updateMenuOpen(false);
            return;
          }
        }
        // Close this menu for any other click
        updateMenuOpen(false);
      };
      setTimeout(() => {
        window.addEventListener('keydown', onKeyDown, { capture: true });
        window.addEventListener('mousedown', onMouseDown, { capture: true });
      }, 0);
      return () => {
        window.removeEventListener('keydown', onKeyDown, { capture: true });
        window.removeEventListener('mousedown', onMouseDown, { capture: true });
      };
    }
  }, [menuOpen, isMobile]);

  const mouseoverId = useId();

  useEffect(() => {
    if (!openOnMouseOver || !menuOpen || isMobile) return;

    const onMouseMove = (e: MouseEvent) => {
      if (e.target instanceof HTMLElement) {
        const matchingParent = e.target.closest(
          `[data-mouseover-id=${mouseoverId}]`
        );
        if (!matchingParent) {
          updateMenuOpen(false);
        } else {
          updateMenuOpen(true);
        }
      }
    };

    window.addEventListener('mousemove', onMouseMove);

    return () => {
      window.removeEventListener('mousemove', onMouseMove);
    };
  }, [openOnMouseOver, menuOpen, isMobile]);

  const menuWrapperRef = useRef<HTMLDivElement>(null);
  const triggerWrapperRef = useRef<HTMLDivElement>(null);
  const buttonId = useId();

  const onCloseParent = useCloseMenuContext();

  const handleClose = useCallback(
    (allParents?: boolean) => {
      updateMenuOpen(false);
      if (allParents) {
        onCloseParent(true);
      }
    },
    [updateMenuOpen, onCloseParent]
  );

  return {
    menuWrapperRef,
    triggerWrapperRef,
    buttonId,
    handleClose,
    mouseoverId,
    updateMenuOpen,
    menuOpen,
  };
};

export const ContextMenuTrigger = ({
  title,
  ButtonComponent,
  ContentsComponent,
  className = '',
  menuClassName = '',
  placement = 'bottom-left',
  onOpenChange,
  openOnMouseOver,
}: {
  title?: React.ReactNode;
  ButtonComponent: React.ComponentType<{
    onClick: (e: React.MouseEvent) => void;
    isOpen: boolean;
    onMouseDown: (e: React.MouseEvent) => void;
    onMouseOver: (e: React.MouseEvent) => void;
    onTouchStart?: (e: React.TouchEvent) => void;
    onTouchEnd?: (e: React.TouchEvent) => void;
    onKeyDown: (e: React.KeyboardEvent) => void;
  }>;
  ContentsComponent: React.ComponentType<{
    onClose: () => void;
  }>;
  className?: string;
  menuClassName?: string;
  placement?:
    | 'bottom-left'
    | 'bottom-right'
    | 'bottom-middle'
    | 'top-left'
    | 'top-right'
    | 'top-middle'
    | 'left'
    | 'right';
  onOpenChange?: (open: boolean) => void;
  openOnMouseOver?: boolean;
}) => {
  const {
    menuWrapperRef,
    triggerWrapperRef,
    buttonId,
    handleClose,
    mouseoverId,
    updateMenuOpen,
    menuOpen,
  } = useContextMenuController({ openOnMouseOver, onOpenChange });

  const isMobile = !useBreakpointMd();
  const accessibleKeyDown = useAccessibleMenuToggle();

  return (
    <div className={className || 'relative'} ref={triggerWrapperRef}>
      <ButtonComponent
        data-button-id={buttonId}
        data-mouseover-id={mouseoverId}
        data-context-menu-trigger
        isOpen={menuOpen}
        onKeyDown={
          accessibleKeyDown({
            menuRef: menuWrapperRef,
            onToggle: useCallback(() => updateMenuOpen(!menuOpen), [menuOpen]),
            isOpen: menuOpen,
          }) as any
        }
        onClick={(e) => e.stopPropagation()}
        onMouseDown={(e) => {
          e.stopPropagation();
          // Toggle menu state. The global mousedown listener
          // handles closing other menus when clicking different triggers
          if (e.button === 0) {
            updateMenuOpen(!menuOpen);
          }
        }}
        onMouseOver={() => {
          if (openOnMouseOver) {
            updateMenuOpen(true);
          }
        }}
      />
      {menuOpen && (
        <CloseMenuContextProvider onClose={handleClose}>
          {openOnMouseOver &&
            !isMobile &&
            createPortal(
              <SafeTriangle
                triggerWrapperRef={triggerWrapperRef}
                menuWrapperRef={menuWrapperRef}
                mouseoverId={mouseoverId}
              />,
              document.body.querySelector(`[data-button-id="${buttonId}"]`) ??
                triggerWrapperRef.current!
            )}
          <ContextMenu
            className={menuClassName}
            mouseoverId={mouseoverId}
            placement={placement}
            ref={menuWrapperRef}
          >
            {title && (
              <ContextMenuHeader onClose={handleClose}>
                {title}
              </ContextMenuHeader>
            )}

            <ContentsComponent onClose={handleClose} />
          </ContextMenu>
        </CloseMenuContextProvider>
      )}
    </div>
  );
};

export const RightClickMenuTrigger = ({
  children,
  ContentsComponent,
  title,
}: {
  children: React.ReactNode;
  ContentsComponent?: React.ComponentType<{
    onClose: () => void;
  }> | null;
  title?: React.ReactNode;
}) => {
  const {
    menuWrapperRef,
    triggerWrapperRef,
    handleClose,
    mouseoverId,
    updateMenuOpen,
    menuOpen,
  } = useContextMenuController();

  const lastRightClickRef = useRef<{ x: number; y: number } | null>(null);

  if (!ContentsComponent) {
    return children;
  }

  return (
    <div
      ref={triggerWrapperRef}
      onContextMenu={(e) => {
        e.preventDefault();
        lastRightClickRef.current = { x: e.clientX, y: e.clientY };
        updateMenuOpen(true);
      }}
    >
      {children}
      {menuOpen && (
        <CloseMenuContextProvider onClose={handleClose}>
          <ContextMenu
            mouseoverId={mouseoverId}
            placement={'bottom-right'}
            clientCoords={{
              top: lastRightClickRef.current?.y ?? 0,
              left: lastRightClickRef.current?.x ?? 0,
            }}
            ref={menuWrapperRef}
          >
            {title && (
              <ContextMenuHeader onClose={handleClose}>
                {title}
              </ContextMenuHeader>
            )}
            <ContentsComponent onClose={handleClose} />
          </ContextMenu>
        </CloseMenuContextProvider>
      )}
    </div>
  );
};

export const ContextSubmenuTrigger = (props: ButtonProps) => {
  return (
    <ContextMenuItem
      {...(omit(props, 'children') as ButtonProps)}
      keepMenusOpen
    >
      <ContextMenuItemMainText>{props.children}</ContextMenuItemMainText>
      <ContextMenuItemSubtext>
        <TriangleRightIcon className='-mr-2 text-foreground-primary' />
      </ContextMenuItemSubtext>
    </ContextMenuItem>
  );
};
