import { useClickAway } from "@uidotdev/usehooks";
import { AnimatePresence, motion } from "framer-motion";
import React, {
	type ReactNode,
	useLayoutEffect,
	useRef,
	useState,
} from "react";
import ReactDOM from "react-dom";

import { useTheme } from "@hooks/useTheme";
import { getBorderRadius } from "@utils/getBorderRadius";
import { twMerge } from "@utils/tailwindMerge";

export interface DropdownItem {
	id: string;
	label: ReactNode;
	icon?: string;
	disabled?: boolean;
	onClick?: () => void;
	type?: string;
}

export interface DropdownProps {
	trigger: ReactNode;
	itemRenderer: (info: {
		item: DropdownItem;
		index: number;
		handleSelect: () => void;
	}) => ReactNode;
	items: DropdownItem[];
	position?: "left" | "right" | "center";
	className?: string;
	menuClassName?: string;
	disabled?: boolean;
	onItemClick?: (item: DropdownItem) => void;
}

export const Dropdown = ({
	trigger,
	items,
	position = "right",
	className,
	menuClassName,
	disabled = false,
	onItemClick,
	itemRenderer,
}: DropdownProps) => {
	const theme = useTheme();
	const [isOpen, setIsOpen] = useState(false);
	const [coords, setCoords] = useState<{ top: number; left: number } | null>(
		null,
	);

	const containerRef = useClickAway<HTMLDivElement>((event) => {
		// Don't close if clicking on the trigger
		if (triggerRef.current?.contains(event.target as Node)) {
			return;
		}
		setIsOpen(false);
	});
	const triggerRef = useRef<HTMLDivElement>(null);
	const bungeeRootRef = useRef<Element | null>(null);

	// Get the root element only on client side to avoid SSR issues
	useLayoutEffect(() => {
		bungeeRootRef.current = document.querySelector("#bn-swap-wrap"); // select by id to avoid conflicts with the same class name used in the interface. TODO - find better way to use the bungee tailwind classes in the interface.
	}, []);

	const handleToggle = () => {
		if (!disabled) {
			setIsOpen(!isOpen);
		}
	};

	const handleItemClick = (item: DropdownItem) => {
		if (!item.disabled) {
			item.onClick?.();
			onItemClick?.(item);
			setIsOpen(false);
		}
	};

	// Measure trigger position when opening
	useLayoutEffect(() => {
		if (isOpen && triggerRef.current && bungeeRootRef.current) {
			const triggerRect = triggerRef.current.getBoundingClientRect();
			const containerRect = bungeeRootRef.current.getBoundingClientRect();

			setCoords({
				top: triggerRect.bottom - containerRect.top,
				left:
					position === "left"
						? triggerRect.left - containerRect.left
						: position === "center"
							? triggerRect.left + triggerRect.width / 2 - containerRect.left
							: triggerRect.right - containerRect.left,
			});
		}
	}, [isOpen, position]);

	const containerClasses = twMerge(
		"bn-relative bn-inline-block bn-max-w-max bn-z-1",
		className,
	);

	const menuClasses = twMerge(
		"bn-absolute !bn-z-20 bn-min-w-[215px] bn-w-max bn-bg-layer-2 bn-border bn-border-subtle bn-shadow-lg bn-overflow-hidden bn-p-3 bn-flex bn-flex-col bn-gap-3",
		menuClassName,
	);

	const menu = (
		<AnimatePresence>
			{isOpen && coords && (
				<motion.div
					initial={{ opacity: 0, y: -10 }}
					animate={{ opacity: 1, y: 0 }}
					exit={{ opacity: 0, y: -10 }}
					transition={{ duration: 0.2 }}
					className={menuClasses}
					style={{
						top: coords.top + 10,
						// left: coords.left,
						right: 32, // TODO: Make this more dynamic to suite different positions
						transform: position === "center" ? "translateX(-50%)" : undefined,
						borderRadius: getBorderRadius(theme.borderRadius, 8),
						position: "absolute",
					}}
					ref={containerRef}
				>
					{items.map((item, index) => {
						const renderedItem = itemRenderer({
							item,
							index,
							handleSelect() {
								handleItemClick(item);
							},
						});

						// Clone the element and add the key prop
						return React.cloneElement(renderedItem as React.ReactElement, {
							key: item.id || index,
						});
					})}
				</motion.div>
			)}
		</AnimatePresence>
	);
	return (
		<div className={containerClasses}>
			<div
				ref={triggerRef}
				onKeyDown={() => {}}
				onKeyUp={() => {}}
				onClick={handleToggle}
				className={twMerge(
					"bn-cursor-pointer",
					disabled && "bn-cursor-not-allowed bn-opacity-50",
				)}
			>
				{trigger}
			</div>

			{bungeeRootRef.current &&
				ReactDOM.createPortal(menu, bungeeRootRef.current)}
		</div>
	);
};
