/**
 * Copyright IBM Corp. 2020, 2026
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

//#region src/components/PageHeader/next/overflowHandler.ts
/**
* Copyright IBM Corp. 2025
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* This file has test coverage in @carbon/utilities. This is a copy of the utility
* that adds margin calculations to an element's size. This should ideally be
* merged back into the utility.
*/
/* istanbul ignore file */
/**
* Calculates the size (width or height) of a given HTML element.
*
* This function performs an expensive calculation by temporarily changing the
* display style of the element if it is not currently visible. It then uses
* `getBoundingClientRect` to retrieve the size of the element.
*
* @param el - The HTML element whose size is to be calculated.
* @param dimension - The dimension to measure ('width' or 'height').
* @returns The size of the element in pixels. Returns 0 if the element is not provided.
*/
function getSize(el, dimension) {
	if (!el) return 0;
	const originalDisplay = el.style.display;
	if (!el.offsetParent && getComputedStyle(el).display === "none") el.style.display = "inline-block";
	let size = el.getBoundingClientRect()[dimension];
	el.style.display = originalDisplay;
	const computedStyles = getComputedStyle(el);
	size = dimension === "width" ? size + parseInt(computedStyles.marginLeft) + parseInt(computedStyles.marginRight) : size + parseInt(computedStyles.marginTop) + parseInt(computedStyles.marginBottom);
	return size;
}
/**
* Updates the overflow handler by determining which items should be visible and which should be hidden.
*
* @param options - Configuration options for updating the overflow handler.
* @param options.container - Container for overflowing
* @param options.items - Child elements within container
* @param options.offset - Children with data-offset attribute
* @param options.sizes - Sizes of child elements
* @param options.fixedSizes - Fixed sizes of child elements with data-fixed attribute
* @param options.offsetSize - Total offset size
* @param options.maxVisibleItems - Max visible items
* @param options.dimension - width | height used to measure overflow
* @param options.onChange - onChange callback
* @param options.previousHiddenItems - Array of previously hidden items
* @returns An array of hidden items after the update.
*/
function updateOverflowHandler({ container, items, offset, sizes, fixedSizes, offsetSize, maxVisibleItems, dimension, onChange, previousHiddenItems = [] }) {
	const containerSize = dimension === "width" ? container.clientWidth : container.clientHeight;
	let visibleItems = [];
	let hiddenItems = [];
	const totalSize = sizes.reduce((sum, size) => sum + size, 0);
	const totalFixedSize = fixedSizes.reduce((sum, size) => sum + size, 0);
	if (totalSize + totalFixedSize <= containerSize) {
		visibleItems = maxVisibleItems ? items.slice(0, maxVisibleItems) : [...items];
		hiddenItems = maxVisibleItems ? items.slice(maxVisibleItems) : [];
	} else {
		const available = containerSize - offsetSize;
		let accumulated = 0;
		for (let i = 0; i < items.length; i++) {
			const size = sizes[i];
			if (accumulated + size + totalFixedSize <= available && (!maxVisibleItems || visibleItems.length < maxVisibleItems)) {
				visibleItems.push(items[i]);
				accumulated += size;
			} else hiddenItems.push(items[i]);
		}
	}
	if (previousHiddenItems.length === hiddenItems.length && previousHiddenItems.every((item, index) => item === hiddenItems[index])) return previousHiddenItems;
	visibleItems.forEach((item) => item.removeAttribute("data-hidden"));
	hiddenItems.forEach((item) => item.setAttribute("data-hidden", ""));
	if (offset) offset.toggleAttribute("data-hidden", hiddenItems.length === 0);
	onChange(visibleItems, hiddenItems);
	return hiddenItems;
}
function createOverflowHandler({ container, maxVisibleItems, onChange, dimension = "width" }) {
	if (!(container instanceof HTMLElement)) throw new Error("container must be an HTMLElement");
	if (typeof onChange !== "function") throw new Error("onChange must be a function");
	if (maxVisibleItems !== void 0 && (!Number.isInteger(maxVisibleItems) || maxVisibleItems <= 0)) throw new Error("maxVisibleItems must be a positive integer");
	const children = Array.from(container.children);
	const offset = children.find((item) => item.hasAttribute("data-offset"));
	const fixedItems = children.filter((item) => item.hasAttribute("data-fixed"));
	const items = children.filter((item) => item !== offset && !fixedItems.includes(item));
	const fixedSizes = fixedItems.map((item) => getSize(item, dimension));
	const sizes = items.map((item) => getSize(item, dimension));
	const offsetSize = getSize(offset, dimension);
	let previousHiddenItems = [];
	function update() {
		previousHiddenItems = updateOverflowHandler({
			container,
			items,
			offset,
			sizes,
			fixedSizes,
			offsetSize,
			maxVisibleItems,
			dimension,
			onChange,
			previousHiddenItems
		});
	}
	const resizeObserver = new ResizeObserver(() => {
		requestAnimationFrame(update);
	});
	resizeObserver.observe(container);
	requestAnimationFrame(update);
	return { disconnect() {
		resizeObserver.disconnect();
	} };
}
//#endregion
exports.createOverflowHandler = createOverflowHandler;
