'use no memo'; // composition props usually invalidate the generated wrapper caches

import type { ComponentType, ReactElement, ReactNode, RefObject, SVGProps } from 'react';

import { Menu as BaseMenu } from '@base-ui/react/menu';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { clsx } from 'clsx';

import { useConstant } from '#/lib/hooks/use-constant';

import * as styles from '#/components/Menu/Menu.css';
import { Text } from '#/components/Text';

import CheckmarkIcon from '#/icons/central/Checkmark2_round_outlined_radius1_stroke2.svg';

// a dropdown menu built on Base UI's Menu. Root + Trigger associate the menu with its anchor; pass a
// `handle` (from `createHandle`/`useMenuHandle`) to drive a detached Trigger or open it imperatively.
export const Root = BaseMenu.Root;
export const Trigger = BaseMenu.Trigger;
export const Group = BaseMenu.Group;

/** Creates a detached handle to associate a Trigger with a Root or open/close the menu imperatively. */
export const createHandle = BaseMenu.createHandle;

/** A detached handle for associating a Trigger with a Root or opening/closing a Menu */
export type MenuHandle<T = void> = BaseMenu.Handle<T>;

/** Component-local menu handle. */
export function useMenuHandle<T = void>(): MenuHandle<T> {
	const handle = useConstant(createHandle<T>);
	return handle;
}

/** Portalled positioner + themed popup card. Put `Item`/`Group`/`Separator` inside. */
export function Popup({
	children,
	label,
	align = 'start',
	side = 'bottom',
	minWidth,
	container,
}: {
	children: ReactNode;
	/** Accessible name for the menu. */
	label?: string;
	align?: BaseMenu.Positioner.Props['align'];
	side?: BaseMenu.Positioner.Props['side'];
	/** Floor on the popup width, so a short item list still reads as a menu rather than a tooltip. */
	minWidth?: number;
	/** portal target, such as the fullscreen container. */
	container?: RefObject<HTMLElement | null>;
}) {
	return (
		<BaseMenu.Portal className={styles.portal} container={container}>
			<BaseMenu.Positioner align={align} side={side} sideOffset={5} collisionPadding={5}>
				<BaseMenu.Popup
					aria-label={label}
					className={styles.popup}
					style={
						minWidth !== undefined ? assignInlineVars({ [styles.minWidthVar]: `${minWidth}px` }) : undefined
					}
				>
					{children}
				</BaseMenu.Popup>
			</BaseMenu.Positioner>
		</BaseMenu.Portal>
	);
}

export function Item({
	children,
	label,
	onClick,
	onMouseEnter,
	destructive = false,
	disabled,
	render,
}: {
	children: ReactNode;
	/** Overrides the label used for keyboard typeahead (defaults to the item's text). */
	label?: string;
	onClick?: () => void;
	/** Hover hook, e.g. to prefetch the data a dialog opened by the item will need. */
	onMouseEnter?: () => void;
	destructive?: boolean;
	disabled?: boolean;
	/** renders the item as a custom element, keeping menu-item styling, keyboard navigation, and close-on-click */
	render?: ReactElement;
}) {
	return (
		<BaseMenu.Item
			className={clsx(styles.item, destructive && styles.itemDestructive)}
			label={label}
			onClick={onClick}
			onMouseEnter={onMouseEnter}
			disabled={disabled}
			render={render}
		>
			{children}
		</BaseMenu.Item>
	);
}

/**
 * renders a menu checkbox without closing the menu.
 *
 * @param props the item props
 * @returns the checkbox item
 */
export function CheckboxItem({
	children,
	label,
	checked,
	onCheckedChange,
	disabled,
}: {
	children: ReactNode;
	label?: string;
	checked: boolean;
	onCheckedChange: (checked: boolean) => void;
	disabled?: boolean;
}) {
	return (
		<BaseMenu.CheckboxItem
			className={styles.item}
			label={label}
			checked={checked}
			onCheckedChange={onCheckedChange}
			disabled={disabled}
		>
			{children}
		</BaseMenu.CheckboxItem>
	);
}

/** @returns a checkbox indicator */
export function ItemCheckbox() {
	return (
		<span className={styles.itemCheckbox}>
			<BaseMenu.CheckboxItemIndicator>
				<CheckmarkIcon className={styles.itemCheckboxMark} />
			</BaseMenu.CheckboxItemIndicator>
		</span>
	);
}

export function ItemText({ children }: { children: ReactNode }) {
	return (
		<Text size="md_sub" color="textContrastHigh" weight="medium" className={styles.itemText}>
			{children}
		</Text>
	);
}

export function ItemIcon({
	icon: Icon,
	position = 'left',
}: {
	icon: ComponentType<SVGProps<SVGSVGElement>>;
	position?: 'left' | 'right';
}) {
	return <Icon className={clsx(styles.itemIcon, position === 'right' && styles.itemIconRight)} />;
}

/** A radio/selection indicator for an item — an outlined circle, filled when `selected`. */
export function ItemRadio({ selected }: { selected: boolean }) {
	return <span className={styles.itemRadio}>{selected && <span className={styles.itemRadioDot} />}</span>;
}

export function LabelText({ children, maxWidth }: { children: ReactNode; maxWidth?: number }) {
	return (
		<BaseMenu.GroupLabel
			className={styles.groupLabel}
			style={maxWidth !== undefined ? assignInlineVars({ [styles.maxWidthVar]: `${maxWidth}px` }) : undefined}
		>
			{children}
		</BaseMenu.GroupLabel>
	);
}

export function Separator() {
	return <BaseMenu.Separator className={styles.separator} />;
}
