/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-af8d194 | Cube Dev Team */
import { extractStyles } from "../../../tasty/utils/styles.js";
import { BASE_STYLES, COLOR_STYLES, OUTER_STYLES } from "../../../tasty/styles/list.js";
import { tasty } from "../../../tasty/tasty.js";
import { filterBaseProps } from "../../../tasty/utils/filter-base-props.js";
import { useEventBus } from "../../../utils/react/useEventBus.js";
import { useEvent } from "../../../_internal/hooks/use-event.js";
import { _Text } from "../../content/Text.js";
import { LoadingIcon } from "../../../icons/LoadingIcon.js";
import { useWarn } from "../../../_internal/hooks/use-warn.js";
import { useProviderProps } from "../../../provider.js";
import { ItemAction } from "../../actions/ItemAction/ItemAction.js";
import { CloseIcon } from "../../../icons/CloseIcon.js";
import { DirectionIcon } from "../../../icons/DirectionIcon.js";
import { _ItemButton } from "../../actions/ItemButton/ItemButton.js";
import { generateRandomId } from "../../../utils/random.js";
import { useFormProps } from "../../form/Form/Form.js";
import { useFieldProps } from "../../form/Form/use-field/use-field-props.js";
import { wrapWithField } from "../../form/wrapper.js";
import { Dialog } from "../../overlays/Dialog/Dialog.js";
import { DialogTrigger } from "../../overlays/Dialog/DialogTrigger.js";
import { ListBox } from "../ListBox/ListBox.js";
import { FilterListBox } from "../FilterListBox/FilterListBox.js";
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
import { FocusScope, useKeyboard } from "react-aria";
import { Section, useListState } from "react-stately";

//#region src/components/fields/FilterPicker/FilterPicker.tsx
const PROP_STYLES = [
	...BASE_STYLES,
	...OUTER_STYLES,
	...COLOR_STYLES
];
const FilterPickerWrapper = tasty({
	qa: "FilterPicker",
	styles: {
		display: "inline-grid",
		flow: "column",
		gridRows: "1sf",
		placeContent: "stretch",
		placeItems: "stretch"
	}
});
const FilterPicker = forwardRef(function FilterPicker(props, ref) {
	props = useProviderProps(props);
	props = useFormProps(props);
	props = useFieldProps(props, { valuePropsMapper: ({ value, onChange }) => {
		const fieldProps = {};
		if (props.selectionMode === "multiple") fieldProps.selectedKeys = value || [];
		else fieldProps.selectedKey = value ?? null;
		fieldProps.onSelectionChange = (key) => {
			if (props.selectionMode === "multiple") if (key === "all") onChange("all");
			else onChange(key ? Array.isArray(key) ? key : [key] : []);
			else onChange(Array.isArray(key) ? key[0] : key);
		};
		return fieldProps;
	} });
	let { qa, label, extra, id, icon, rightIcon, prefix, suffix, hotkeys, triggerTooltip, triggerDescription, labelStyles, isRequired, necessityIndicator, validationState, isDisabled, isLoading, message, mods: externalMods, description, descriptionPlacement, placeholder, size = "medium", styles, listBoxStyles, popoverStyles, type = "outline", theme = "default", shape, labelSuffix, shouldFocusWrap, children, shouldFlip = true, containerPadding = 8, selectedKey, defaultSelectedKey, selectedKeys, defaultSelectedKeys, disabledKeys, onSelectionChange, selectionMode = "single", listStateRef, focusOnHover, showSelectAll, selectAllLabel = "All", items, header, footer, headerStyles, footerStyles, triggerStyles, allowsCustomValue, renderSummary, isCheckable, allValueProps, customValueProps, newCustomValueProps, searchPlaceholder, autoFocus, filter, emptyLabel, searchInputStyles, searchInputRef, listStyles, optionStyles, sectionStyles, headingStyles, listRef, disallowEmptySelection, shouldUseVirtualFocus, onEscape, onOptionClick, isClearable, searchValue, onSearchChange, sortSelectedToTop: sortSelectedToTopProp, onOpenChange, form, ...otherProps } = props;
	const sortSelectedToTopExplicit = sortSelectedToTopProp !== void 0;
	const sortSelectedToTop = sortSelectedToTopProp ?? (items ? true : false);
	styles = extractStyles(otherProps, PROP_STYLES, styles);
	const filterPickerId = useMemo(() => generateRandomId(), []);
	const { emit, on } = useEventBus();
	useWarn(isCheckable === false && selectionMode === "single", {
		key: ["filterpicker-checkable-single-mode"],
		args: ["CubeUIKit: isCheckable=false is not recommended in single selection mode as it may confuse users about selection behavior."]
	});
	const [internalSelectedKey, setInternalSelectedKey] = useState(defaultSelectedKey ?? null);
	const [internalSelectedKeys, setInternalSelectedKeys] = useState(defaultSelectedKeys ?? []);
	const [isPopoverOpen, setIsPopoverOpen] = useState(false);
	const cachedItemsOrder = useRef(null);
	const triggerRef = useRef(null);
	useEffect(() => {
		cachedItemsOrder.current = null;
	}, [items]);
	const isControlledSingle = selectedKey !== void 0;
	const isControlledMultiple = selectedKeys !== void 0;
	const effectiveSelectedKey = isControlledSingle ? selectedKey : internalSelectedKey;
	const effectiveSelectedKeys = isControlledMultiple ? selectedKeys : internalSelectedKeys;
	const localCollectionState = useListState({
		children,
		items,
		selectionMode: "none"
	});
	const findCollectionKey = useCallback((lookup) => {
		if (lookup == null) return lookup;
		for (const item of localCollectionState.collection) if (String(item.key) === String(lookup)) return item.key;
		return lookup;
	}, [localCollectionState.collection]);
	const mappedSelectedKey = useMemo(() => {
		if (selectionMode !== "single") return null;
		return effectiveSelectedKey ? findCollectionKey(effectiveSelectedKey) : null;
	}, [
		selectionMode,
		effectiveSelectedKey,
		findCollectionKey
	]);
	const mappedSelectedKeys = useMemo(() => {
		if (selectionMode !== "multiple") return void 0;
		if (effectiveSelectedKeys === "all") return "all";
		if (Array.isArray(effectiveSelectedKeys)) return effectiveSelectedKeys.map((k) => findCollectionKey(k));
		return effectiveSelectedKeys;
	}, [
		selectionMode,
		effectiveSelectedKeys,
		findCollectionKey
	]);
	const processSelectionArray = (iterable) => {
		const resultSet = /* @__PURE__ */ new Set();
		for (const key of iterable) {
			const nKey = String(key);
			if (resultSet.has(nKey)) resultSet.delete(nKey);
			else resultSet.add(nKey);
		}
		return Array.from(resultSet);
	};
	const getSelectedLabels = () => {
		const collection = localCollectionState.collection;
		const collectAllLabels = () => {
			const allLabels = [];
			const traverse = (nodes) => {
				for (const node of nodes) if (node.type === "item") allLabels.push(node.textValue || String(node.key));
				else if (node.childNodes) traverse(node.childNodes);
			};
			traverse(collection);
			return allLabels;
		};
		if (selectionMode === "multiple" && effectiveSelectedKeys === "all") return collectAllLabels();
		const selectedSet = new Set(selectionMode === "multiple" && effectiveSelectedKeys !== "all" ? (effectiveSelectedKeys || []).map((k) => String(k)) : effectiveSelectedKey != null ? [String(effectiveSelectedKey)] : []);
		const labels = [];
		const processedKeys = /* @__PURE__ */ new Set();
		selectedSet.forEach((key) => {
			const item = collection.getItem(key);
			if (item) {
				labels.push(item.textValue || String(item.key));
				processedKeys.add(String(item.key));
			}
		});
		(selectionMode === "multiple" && effectiveSelectedKeys !== "all" ? (effectiveSelectedKeys || []).map(String) : effectiveSelectedKey != null ? [String(effectiveSelectedKey)] : []).forEach((key) => {
			if (!processedKeys.has(String(key))) labels.push(key);
		});
		return labels;
	};
	const selectedLabels = getSelectedLabels();
	const hasSelection = selectedLabels.length > 0;
	const latestSelectionRef = useRef({
		single: effectiveSelectedKey != null ? String(effectiveSelectedKey) : null,
		multiple: effectiveSelectedKeys === "all" ? "all" : (effectiveSelectedKeys ?? []).map(String)
	});
	useEffect(() => {
		latestSelectionRef.current = {
			single: effectiveSelectedKey != null ? String(effectiveSelectedKey) : null,
			multiple: effectiveSelectedKeys === "all" ? "all" : (effectiveSelectedKeys ?? []).map(String)
		};
	}, [effectiveSelectedKey, effectiveSelectedKeys]);
	const selectionsWhenClosed = useRef({
		single: null,
		multiple: []
	});
	useEffect(() => {
		selectionsWhenClosed.current = { ...latestSelectionRef.current };
	}, []);
	const getSortedChildren = useCallback(() => {
		if (sortSelectedToTopExplicit && sortSelectedToTop && !items) console.warn("FilterPicker: sortSelectedToTop only works with the items prop. Sorting will be skipped when using JSX children.");
		return children;
	}, [
		children,
		sortSelectedToTop,
		sortSelectedToTopExplicit,
		items
	]);
	const finalItems = useCallback(() => {
		if (!items) return items;
		if (!sortSelectedToTop) return items;
		if (cachedItemsOrder.current) return cachedItemsOrder.current;
		const selectedSet = /* @__PURE__ */ new Set();
		const addSelected = (key) => {
			if (key != null) selectedSet.add(String(key));
		};
		if (selectionMode === "multiple") {
			if (selectionsWhenClosed.current.multiple === "all") return items;
			selectionsWhenClosed.current.multiple.forEach(addSelected);
		} else if (selectionsWhenClosed.current.single != null) addSelected(selectionsWhenClosed.current.single);
		if (selectedSet.size === 0) return items;
		const getItemKey = (obj) => {
			if (obj == null || typeof obj !== "object") return void 0;
			const item = obj;
			if (item.key != null) return String(item.key);
			if (item.id != null) return String(item.id);
		};
		const sortArray = (arr) => {
			const selectedArr = [];
			const unselectedArr = [];
			arr.forEach((obj) => {
				const item = obj;
				if (obj && Array.isArray(item.children)) {
					const sortedChildren = sortArray(item.children);
					unselectedArr.push({
						...item,
						children: sortedChildren
					});
				} else {
					const key = getItemKey(obj);
					if (key && selectedSet.has(key)) selectedArr.push(obj);
					else unselectedArr.push(obj);
				}
			});
			return [...selectedArr, ...unselectedArr];
		};
		const sorted = sortArray(Array.isArray(items) ? items : Array.from(items));
		if (isPopoverOpen || !cachedItemsOrder.current) cachedItemsOrder.current = sorted;
		return sorted;
	}, [
		items,
		sortSelectedToTop,
		selectionMode,
		isPopoverOpen,
		selectionsWhenClosed.current.multiple,
		selectionsWhenClosed.current.single
	])();
	const finalChildren = getSortedChildren();
	const renderTriggerContent = () => {
		if (typeof renderSummary === "function") {
			if (selectionMode === "single") return renderSummary({
				selectedLabel: selectedLabels[0],
				selectedKey: effectiveSelectedKey ?? null,
				selectedLabels,
				selectedKeys: effectiveSelectedKeys,
				selectionMode: "single"
			});
			return renderSummary({
				selectedLabels,
				selectedKeys: effectiveSelectedKeys,
				selectionMode: "multiple"
			});
		} else if (renderSummary === false) return null;
		let content = "";
		if (!hasSelection) return /* @__PURE__ */ jsx(_Text.Placeholder, { children: placeholder });
		else if (selectionMode === "single") content = selectedLabels[0];
		else if (effectiveSelectedKeys === "all") content = selectAllLabel;
		else content = selectedLabels.join(", ");
		if (!content) return null;
		return content;
	};
	const [shouldUpdatePosition, setShouldUpdatePosition] = useState(true);
	const triggerWidth = triggerRef?.current?.UNSAFE_getDOMNode()?.offsetWidth;
	const renderTrigger = (state) => {
		useEffect(() => {
			return on("popover:open", (data) => {
				if (data.menuId !== filterPickerId && state.isOpen) state.close();
			});
		}, [
			on,
			filterPickerId,
			state
		]);
		useEffect(() => {
			if (state.isOpen) emit("popover:open", { menuId: filterPickerId });
		}, [
			state.isOpen,
			emit,
			filterPickerId
		]);
		useEffect(() => {
			if (state.isOpen !== isPopoverOpen) {
				setIsPopoverOpen(state.isOpen);
				if (!state.isOpen) {
					selectionsWhenClosed.current = { ...latestSelectionRef.current };
					cachedItemsOrder.current = null;
				}
				onOpenChange?.(state.isOpen);
			}
		}, [
			state.isOpen,
			isPopoverOpen,
			onOpenChange
		]);
		const { keyboardProps } = useKeyboard({ onKeyDown: (e) => {
			if ((e.key === "ArrowUp" || e.key === "ArrowDown") && !state.isOpen) {
				e.preventDefault();
				state.open();
			}
		} });
		useEffect(() => {
			if (state.isOpen) {
				setShouldUpdatePosition(true);
				const id = window.setTimeout(() => setShouldUpdatePosition(false), 160);
				return () => window.clearTimeout(id);
			} else setShouldUpdatePosition(true);
		}, [state.isOpen]);
		let showClearButton = isClearable && hasSelection && !isDisabled && !props.isReadOnly;
		let clearValue = useEvent(() => {
			if (selectionMode === "multiple") {
				if (!isControlledMultiple) setInternalSelectedKeys([]);
				onSelectionChange?.([]);
			} else {
				if (!isControlledSingle) setInternalSelectedKey(null);
				onSelectionChange?.(null);
			}
			if (state.isOpen) state.close();
			triggerRef?.current?.focus?.();
			props.onClear?.();
			return false;
		});
		return /* @__PURE__ */ jsx(_ItemButton, {
			ref: triggerRef,
			"data-popover-trigger": true,
			qa: qa || "FilterPicker",
			id,
			type,
			theme: validationState === "invalid" ? "danger" : theme,
			size,
			shape,
			isDisabled: isDisabled || isLoading,
			"data-input-type": "filterpicker",
			mods: {
				placeholder: !hasSelection,
				...externalMods
			},
			icon,
			rightIcon: isLoading ? /* @__PURE__ */ jsx(LoadingIcon, {}) : rightIcon !== void 0 ? rightIcon : showClearButton ? /* @__PURE__ */ jsx(ItemAction, {
				icon: /* @__PURE__ */ jsx(CloseIcon, {}),
				size,
				theme: validationState === "invalid" ? "danger" : void 0,
				qa: "FilterPickerClearButton",
				mods: { pressed: false },
				onPress: clearValue
			}) : /* @__PURE__ */ jsx(DirectionIcon, { to: state.isOpen ? "top" : "bottom" }),
			prefix,
			suffix,
			hotkeys,
			tooltip: triggerTooltip,
			description: triggerDescription,
			descriptionPlacement,
			styles: triggerStyles,
			...keyboardProps,
			"aria-label": `${props["aria-label"] ?? props.label ?? ""}`,
			children: renderTriggerContent()
		});
	};
	return wrapWithField(/* @__PURE__ */ jsx(FilterPickerWrapper, {
		qa: "FilterPickerWrapper",
		styles,
		...filterBaseProps(otherProps, { eventProps: true }),
		children: /* @__PURE__ */ jsxs(DialogTrigger, {
			type: "popover",
			placement: "bottom start",
			containerPadding,
			shouldUpdatePosition,
			shouldFlip: shouldFlip && shouldUpdatePosition,
			isDismissable: true,
			shouldCloseOnInteractOutside: (el) => {
				const menuTriggerEl = el.closest("[data-popover-trigger]");
				if (!menuTriggerEl) return true;
				if (menuTriggerEl === triggerRef?.current) return true;
				return false;
			},
			children: [renderTrigger, (close) => /* @__PURE__ */ jsx(Dialog, {
				qa: "FilterPickerOverlay",
				display: "grid",
				styles: {
					gridRows: "1sf",
					width: "max($overlay-min-width, 30x) max-content 50vw",
					"$overlay-min-width": "30x",
					...popoverStyles
				},
				style: triggerWidth ? { "--overlay-min-width": `${triggerWidth}px` } : void 0,
				children: /* @__PURE__ */ jsx(FocusScope, {
					restoreFocus: true,
					children: /* @__PURE__ */ jsx(FilterListBox, {
						autoFocus: true,
						items: items ? finalItems : void 0,
						"aria-label": `${props["aria-label"] ?? props.label ?? ""} Picker`,
						_internalCollection: localCollectionState.collection,
						selectedKey: selectionMode === "single" ? mappedSelectedKey : void 0,
						selectedKeys: selectionMode === "multiple" ? mappedSelectedKeys : void 0,
						searchPlaceholder,
						filter,
						searchValue,
						listStyles,
						optionStyles,
						sectionStyles,
						headingStyles,
						listRef,
						disallowEmptySelection,
						emptyLabel,
						searchInputStyles,
						searchInputRef,
						disabledKeys,
						focusOnHover,
						shouldFocusWrap,
						allowsCustomValue,
						selectionMode,
						validationState,
						isDisabled,
						isLoading,
						stateRef: listStateRef,
						isCheckable,
						mods: { popover: true },
						size: size === "small" ? "medium" : size,
						showSelectAll,
						selectAllLabel,
						header,
						footer,
						headerStyles,
						footerStyles,
						allValueProps,
						customValueProps,
						newCustomValueProps,
						onSearchChange,
						onEscape: () => close(),
						onOptionClick: (key) => {
							if (selectionMode === "multiple" && isCheckable || key === "__ALL__") close();
						},
						onSelectionChange: (selection) => {
							if (selectionMode === "single") {
								if (!isControlledSingle) setInternalSelectedKey(selection);
							} else if (!isControlledMultiple) {
								let normalized = selection;
								if (selection === "all") normalized = "all";
								else if (Array.isArray(selection)) normalized = processSelectionArray(selection);
								else if (selection && typeof selection === "object" && selection instanceof Set) normalized = processSelectionArray(selection);
								setInternalSelectedKeys(normalized);
							}
							if (selectionMode === "single") latestSelectionRef.current.single = selection != null ? String(selection) : null;
							else if (selection === "all") latestSelectionRef.current.multiple = "all";
							else if (Array.isArray(selection)) latestSelectionRef.current.multiple = Array.from(new Set(processSelectionArray(selection)));
							else if (selection && typeof selection === "object" && selection instanceof Set) latestSelectionRef.current.multiple = Array.from(new Set(processSelectionArray(selection)));
							else latestSelectionRef.current.multiple = selection === "all" ? "all" : Array.isArray(selection) ? selection.map(String) : [];
							onSelectionChange?.(selection);
							if (selectionMode === "single") close();
						},
						children: children ? finalChildren : void 0
					})
				})
			})]
		})
	}), ref, props);
});
FilterPicker.Item = ListBox.Item;
FilterPicker.Section = Section;
Object.defineProperty(FilterPicker, "cubeInputType", {
	value: "FilterPicker",
	enumerable: false,
	configurable: false
});

//#endregion
export { FilterPicker };
//# sourceMappingURL=FilterPicker.js.map