/** @license MIT | @cube-dev/ui-kit v0.123.0 | Cube Dev Team */
import { useLayoutEffect as useLayoutEffect$1 } from "../../../utils/react/useLayoutEffect.js";
import { useEvent } from "../../../_internal/hooks/use-event.js";
import { DisplayTransition } from "../../helpers/DisplayTransition/DisplayTransition.js";
import { Portal } from "../../portal/Portal.js";
import { ToastItem } from "../Toast/ToastItem.js";
import { NotificationItem } from "./NotificationItem.js";
import { tasty } from "@tenphi/tasty";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { jsx } from "react/jsx-runtime";

//#region src/components/overlays/Notifications/OverlayContainer.tsx
const COLLAPSE_VISIBLE_HEIGHT = 10;
const CONTAINER_OFFSET = 16;
const ITEM_GAP = 8;
const DEFAULT_ITEM_HEIGHT = 56;
const OverlayContainerElement = tasty({ styles: {
	position: "fixed",
	top: "2x",
	left: "50%",
	transform: "translateX(-50%)",
	zIndex: 100,
	padding: "1x",
	height: "0",
	pointerEvents: "none"
} });
const OverlayItemWrapper = tasty({ styles: {
	position: "absolute",
	top: "0",
	left: "50%",
	transform: {
		"": "translateX(-50%) translateY(-50%)",
		"isMeasured & isShown": "translateX(-50%) translateY(0)"
	},
	width: "max-content 50x",
	pointerEvents: "auto",
	transition: {
		"": "opacity $transition ease-in, transform $transition ease-in",
		isMeasured: "top $transition ease-in, opacity $transition ease-in, transform $transition ease-in"
	},
	opacity: {
		"": 0,
		isShown: 1
	}
} });
function getItemId(item) {
	return item.data.internalId;
}
function isItemExiting(item) {
	return item.data.isExiting === true;
}
function getItemCreatedAt(item) {
	return item.data.createdAt;
}
/**
* Manages height measurement, position calculation, and ref tracking
* for overlay items. Extracted from OverlayContainer for readability.
*/
function useItemPositions(visibleItems) {
	const [heights, setHeights] = useState({});
	const itemRefs = useRef(/* @__PURE__ */ new Map());
	const lastPositionsRef = useRef(/* @__PURE__ */ new Map());
	const [settledIds, setSettledIds] = useState(/* @__PURE__ */ new Set());
	const rafRef = useRef(null);
	useLayoutEffect$1(() => {
		const newHeights = {};
		let hasChanges = false;
		itemRefs.current.forEach((el, id) => {
			const height = el.offsetHeight || DEFAULT_ITEM_HEIGHT;
			newHeights[id] = height;
			if (heights[id] !== height) hasChanges = true;
		});
		for (const id of Object.keys(heights)) if (!itemRefs.current.has(id)) hasChanges = true;
		if (hasChanges) setHeights(newHeights);
	});
	useEffect(() => {
		const heightKeys = Object.keys(heights);
		const newIds = heightKeys.filter((id) => !settledIds.has(id));
		const hasStaleIds = settledIds.size > 0 && [...settledIds].some((id) => !(id in heights));
		if (newIds.length === 0 && !hasStaleIds) return;
		if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
		if (newIds.length === 0 && hasStaleIds) {
			setSettledIds((prev) => new Set(heightKeys.filter((id) => prev.has(id))));
			return;
		}
		rafRef.current = requestAnimationFrame(() => {
			rafRef.current = null;
			setSettledIds((prev) => {
				const next = /* @__PURE__ */ new Set();
				for (const id of heightKeys) if (prev.has(id)) next.add(id);
				for (const id of newIds) next.add(id);
				return next;
			});
		});
		return () => {
			if (rafRef.current != null) {
				cancelAnimationFrame(rafRef.current);
				rafRef.current = null;
			}
		};
	}, [heights]);
	const createRefCallback = useCallback((itemId, displayRef) => (el) => {
		displayRef(el);
		if (el) itemRefs.current.set(itemId, el);
		else itemRefs.current.delete(itemId);
	}, []);
	const positions = useMemo(() => {
		const posMap = /* @__PURE__ */ new Map();
		let currentTop = 0;
		for (const item of visibleItems) {
			const id = getItemId(item);
			posMap.set(id, currentTop);
			const height = heights[id] ?? DEFAULT_ITEM_HEIGHT;
			currentTop += height + ITEM_GAP;
		}
		return posMap;
	}, [visibleItems, heights]);
	useEffect(() => {
		positions.forEach((pos, id) => {
			lastPositionsRef.current.set(id, pos);
		});
	}, [positions]);
	return {
		heights,
		settledIds,
		positions,
		lastPositionsRef,
		itemRefs,
		createRefCallback
	};
}
/**
* Manages collapse/expand behavior and pause state for the overlay container.
* Extracted from OverlayContainer for readability.
*/
function useCollapseState(canCollapse, allItems, itemRefs, onPauseChange) {
	const [isCollapsed, setIsCollapsed] = useState(false);
	const containerRef = useRef(null);
	const boundsRef = useRef(null);
	const updateBounds = useCallback(() => {
		const refs = itemRefs.current;
		if (!refs || refs.size === 0) {
			boundsRef.current = null;
			return;
		}
		let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
		refs.forEach((el) => {
			const rect = el.getBoundingClientRect();
			minX = Math.min(minX, rect.left);
			minY = Math.min(minY, rect.top);
			maxX = Math.max(maxX, rect.right);
			maxY = Math.max(maxY, rect.bottom);
		});
		if (typeof DOMRect !== "undefined") boundsRef.current = new DOMRect(minX, minY, maxX - minX, maxY - minY);
		else boundsRef.current = {
			x: minX,
			y: minY,
			width: maxX - minX,
			height: maxY - minY,
			top: minY,
			right: maxX,
			bottom: maxY,
			left: minX,
			toJSON: () => ({})
		};
	}, [itemRefs]);
	useEffect(() => {
		if (!isCollapsed) return;
		const handleMouseMove = (e) => {
			const bounds = boundsRef.current;
			if (!bounds) {
				setIsCollapsed(false);
				onPauseChange(false);
				return;
			}
			const padding = 20;
			if (!(e.clientX >= bounds.left - padding && e.clientX <= bounds.right + padding && e.clientY >= bounds.top - padding && e.clientY <= bounds.bottom + padding)) {
				setIsCollapsed(false);
				onPauseChange(false);
			}
		};
		window.addEventListener("mousemove", handleMouseMove);
		return () => window.removeEventListener("mousemove", handleMouseMove);
	}, [isCollapsed, onPauseChange]);
	useEffect(() => {
		updateBounds();
	}, [allItems, updateBounds]);
	return {
		isCollapsed,
		handleMouseEnter: useCallback(() => {
			updateBounds();
			onPauseChange(true);
			if (canCollapse) setIsCollapsed(true);
		}, [
			updateBounds,
			canCollapse,
			onPauseChange
		]),
		handleMouseLeave: useCallback(() => {
			if (!isCollapsed) onPauseChange(false);
		}, [isCollapsed, onPauseChange]),
		handleFocus: useCallback(() => {
			onPauseChange(true);
		}, [onPauseChange]),
		handleBlur: useCallback((e) => {
			if (containerRef.current && !containerRef.current.contains(e.relatedTarget)) onPauseChange(false);
		}, [onPauseChange]),
		containerRef
	};
}
function OverlayContainer({ toasts, notifications, onToastExitComplete, onNotificationExitComplete, onNotificationDismiss, onNotificationRestore, onPauseChange }) {
	const allItems = useMemo(() => {
		const items = [...toasts.map((t) => ({
			kind: "toast",
			data: t
		})), ...notifications.map((n) => ({
			kind: "notification",
			data: n
		}))];
		items.sort((a, b) => getItemCreatedAt(a) - getItemCreatedAt(b));
		return items;
	}, [toasts, notifications]);
	const visibleItems = useMemo(() => allItems.filter((item) => !isItemExiting(item)), [allItems]);
	const hasNotifications = notifications.some((n) => !n.isExiting);
	const hasActionableToasts = toasts.some((t) => !t.isExiting && t.actions);
	const canCollapse = !hasNotifications && !hasActionableToasts;
	const { heights, settledIds, positions, lastPositionsRef, itemRefs, createRefCallback } = useItemPositions(visibleItems);
	const { isCollapsed, handleMouseEnter, handleMouseLeave, handleFocus, handleBlur, containerRef } = useCollapseState(canCollapse, allItems, itemRefs, onPauseChange);
	const handleNotificationDismiss = useEvent((id, reason) => {
		onNotificationDismiss(id, reason);
		onPauseChange(false);
	});
	const handleExitComplete = useEvent((item) => {
		const id = getItemId(item);
		lastPositionsRef.current?.delete(id);
		if (item.kind === "toast") onToastExitComplete(item.data.internalId);
		else onNotificationExitComplete(item.data.internalId);
	});
	const getItemStyle = useCallback((item, index, total) => {
		const id = getItemId(item);
		const baseTop = positions.get(id) ?? lastPositionsRef.current?.get(id) ?? 0;
		const height = heights[id] ?? DEFAULT_ITEM_HEIGHT;
		if (!isCollapsed || !canCollapse) return { top: `${baseTop}px` };
		const isNewest = index === total - 1;
		return {
			top: `${COLLAPSE_VISIBLE_HEIGHT - CONTAINER_OFFSET - height}px`,
			zIndex: index,
			opacity: isNewest ? 1 : 0,
			pointerEvents: "none"
		};
	}, [
		isCollapsed,
		canCollapse,
		positions,
		heights,
		lastPositionsRef
	]);
	const visibleIndexMap = useMemo(() => {
		const map = /* @__PURE__ */ new Map();
		visibleItems.forEach((item, index) => {
			map.set(getItemId(item), index);
		});
		return map;
	}, [visibleItems]);
	if (allItems.length === 0) return null;
	return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(OverlayContainerElement, {
		ref: containerRef,
		onFocus: handleFocus,
		onBlur: handleBlur,
		children: allItems.map((item) => {
			const itemId = getItemId(item);
			const visibleIndex = visibleIndexMap.get(itemId) ?? 0;
			return /* @__PURE__ */ jsx(DisplayTransition, {
				animateOnMount: true,
				isShown: !isItemExiting(item),
				onRest: (transition) => {
					if (transition === "exit") handleExitComplete(item);
				},
				children: ({ isShown, ref }) => /* @__PURE__ */ jsx(OverlayItemWrapper, {
					ref: createRefCallback(itemId, ref),
					mods: {
						isShown,
						isMeasured: settledIds.has(itemId)
					},
					style: getItemStyle(item, visibleIndex, visibleItems.length),
					onMouseEnter: handleMouseEnter,
					onMouseLeave: handleMouseLeave,
					children: item.kind === "toast" ? /* @__PURE__ */ jsx(ToastItem, {
						...item.data.itemProps,
						title: item.data.title,
						description: item.data.description,
						theme: item.data.theme,
						icon: item.data.icon,
						isLoading: item.data.isLoading,
						actions: item.data.actions
					}) : /* @__PURE__ */ jsx(NotificationItem, {
						notification: item.data,
						onDismiss: handleNotificationDismiss,
						onRestore: onNotificationRestore
					})
				})
			}, itemId);
		})
	}) });
}

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