import {
  arrow,
  autoPlacement,
  autoUpdate,
  FloatingArrow,
  FloatingPortal,
  offset as offsetFn,
  useFloating,
  type Placement,
} from "@floating-ui/react";
import clsx from "clsx";
import {
  cloneElement,
  useRef,
  type PropsWithChildren,
  type ReactElement,
  type Ref,
} from "react";
import styles from "./AnchoredPopover.module.css";

interface AnchoredPopoverProps<T extends HTMLElement>
  extends PropsWithChildren {
  referenceComponent: ReactElement<{ ref?: Ref<T> }>;
  allowedPlacements: Placement[];
  className?: string;
  offset?: number;
  open?: boolean;
  floatingArrow?: boolean;
  floatingArrowClassName?: string;
}

export const AnchoredPopover = <T extends HTMLElement>({
  className,
  children,
  open,
  referenceComponent,
  offset,
  allowedPlacements,
  floatingArrow,
  floatingArrowClassName,
  ...rest
}: AnchoredPopoverProps<T>) => {
  const arrowRef = useRef(null);
  const { refs, floatingStyles, context, isPositioned } = useFloating({
    strategy: "fixed",
    transform: false,
    open,
    middleware: [
      offset ? offsetFn(offset) : null,
      arrow({ element: arrowRef }),
      autoPlacement({
        allowedPlacements,
      }),
    ].filter(Boolean),
    whileElementsMounted: autoUpdate,
  });

  return (
    <>
      {cloneElement(referenceComponent, { ref: refs.setReference })}
      <FloatingPortal>
        <div
          ref={refs.setFloating}
          style={floatingStyles}
          className={clsx([styles.wrapper, className])}
          {...rest}
        >
          {children}
          {floatingArrow && isPositioned && (
            <FloatingArrow
              ref={arrowRef}
              context={context}
              className={floatingArrowClassName}
            />
          )}
        </div>
      </FloatingPortal>
    </>
  );
};
