"use client";

import clsx from "clsx/lite";
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
import type React from "react";
import { PopoverContent } from "./popover";

export type MenuItem<
	TExtra extends Record<string, unknown> = Record<string, unknown>,
> = {
	value: string | number;
	label: string;
	icon?: React.ReactNode;
	destructive?: boolean;
	disabled?: boolean;
	action?: () => void;
} & TExtra;

export interface MenuGroup<TMenuItem extends MenuItem = MenuItem> {
	groupId: string | number;
	groupLabel: string;
	items: Array<TMenuItem>;
}

type MenuContent = MenuItem | MenuGroup<MenuItem>;

interface DropdownMenuItemProps {
	onMouseEnter: React.MouseEventHandler<HTMLButtonElement>;
	onMouseLeave: React.MouseEventHandler<HTMLButtonElement>;
}
interface DropdownMenuProps<
	T extends Array<MenuContent>,
	TRenderItemAsChild extends boolean,
> {
	items: T;
	trigger: React.ReactNode;
	renderItemAsChild?: TRenderItemAsChild;
	renderItem?: T[number] extends MenuGroup<infer I>
		? (
				item: I,
				props: DropdownMenuItemProps,
			) => TRenderItemAsChild extends true
				? React.ReactElement
				: React.ReactNode
		: (
				item: T[number],
				props: DropdownMenuItemProps,
			) => TRenderItemAsChild extends true
				? React.ReactElement
				: React.ReactNode;
	onSelect?: T[number] extends MenuGroup<infer I>
		? (event: Event, option: I) => void
		: (event: Event, option: T[number]) => void;
	onItemHover?: T[number] extends MenuGroup<infer I>
		? (item: I, isHovered: boolean) => void
		: (item: T[number], isHovered: boolean) => void;
	widthClassName?: string;
	sideOffset?: DropdownMenuPrimitive.DropdownMenuContentProps["sideOffset"];
	alignOffset?: DropdownMenuPrimitive.DropdownMenuContentProps["alignOffset"];
	align?: DropdownMenuPrimitive.DropdownMenuContentProps["align"];
	open?: DropdownMenuPrimitive.DropdownMenuProps["open"];
	onOpenChange?: DropdownMenuPrimitive.DropdownMenuProps["onOpenChange"];
	modal?: DropdownMenuPrimitive.DropdownMenuProps["modal"];
}

function isGroupItem<T extends MenuItem>(
	option: T | MenuGroup<T>,
): option is MenuGroup<T> {
	return (
		"groupLabel" in option && Array.isArray((option as MenuGroup<T>).items)
	);
}

export function DropdownMenu<
	T extends Array<MenuContent>,
	TRenderItemAsChild extends boolean = false,
>({
	trigger,
	items,
	renderItem,
	renderItemAsChild,
	onSelect,
	onItemHover,
	widthClassName,
	sideOffset,
	align,
	open,
	onOpenChange,
	modal,
}: DropdownMenuProps<T, TRenderItemAsChild>) {
	const renderMenuItem = (item: MenuItem) => (
		<DropdownMenuPrimitive.Item
			asChild={renderItemAsChild}
			key={item.value}
			onSelect={(event) => onSelect?.(event, item)}
			onMouseEnter={() => onItemHover?.(item, true)}
			onMouseLeave={() => onItemHover?.(item, false)}
			disabled={item.disabled}
			className={
				renderItemAsChild
					? ""
					: clsx(
							"text-text outline-none cursor-pointer",
							item.destructive
								? "hover:bg-destructive-hover-bg"
								: "hover:bg-ghost-element-hover",
							"rounded-[4px] px-[8px] py-[6px] text-[14px]",
							"flex items-center justify-between gap-[4px]",
							item.disabled && "opacity-50 cursor-not-allowed",
						)
			}
		>
			{renderItem ? (
				renderItem(item, {
					onMouseEnter: () => onItemHover?.(item, true),
					onMouseLeave: () => onItemHover?.(item, false),
				})
			) : item.icon ? (
				<div className="flex items-center gap-2">
					<span className="h-4 w-4">{item.icon}</span>
					{item.label}
				</div>
			) : (
				item.label
			)}
		</DropdownMenuPrimitive.Item>
	);

	return (
		<DropdownMenuPrimitive.Root
			open={open}
			onOpenChange={onOpenChange}
			modal={modal}
		>
			<DropdownMenuPrimitive.Trigger asChild>
				{trigger}
			</DropdownMenuPrimitive.Trigger>
			<DropdownMenuPrimitive.Portal>
				<DropdownMenuPrimitive.Content
					sideOffset={sideOffset}
					align={align}
					className={clsx("z-50 overflow-hidden flex flex-col", widthClassName)}
					style={{ maxHeight: "85vh" }}
				>
					<PopoverContent>
						{items.map((option, index) => {
							if (isGroupItem(option)) {
								return (
									<div key={option.groupId}>
										<DropdownMenuPrimitive.Group className="mb-[4px]">
											<DropdownMenuPrimitive.Label className="text-text-muted px-[8px] py-[3px] text-[11px] font-medium mt-[4px]">
												{option.groupLabel}
											</DropdownMenuPrimitive.Label>
											{option.items.map(renderMenuItem)}
										</DropdownMenuPrimitive.Group>
										{index < items.length - 1 && (
											<DropdownMenuPrimitive.Separator className="bg-text/20 h-px" />
										)}
									</div>
								);
							}
							return renderMenuItem(option);
						})}
					</PopoverContent>
				</DropdownMenuPrimitive.Content>
			</DropdownMenuPrimitive.Portal>
		</DropdownMenuPrimitive.Root>
	);
}
