/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-29d58ba | Cube Dev Team */
import { stringifyStyles } from "../utils/styles.js";
import { extractLocalProperties, hasLocalProperties } from "../properties/index.js";
import { getGlobalKeyframes, hasGlobalKeyframes } from "../config.js";
import { CHUNK_NAMES, categorizeStyleKeys } from "../chunks/definitions.js";
import { generateChunkCacheKey } from "../chunks/cacheKey.js";
import { renderStylesForChunk } from "../chunks/renderChunk.js";
import { allocateClassName, inject, keyframes, property } from "../injector/index.js";
import { extractAnimationNamesFromStyles, extractLocalKeyframes, filterUsedKeyframes, hasLocalKeyframes, mergeKeyframes, replaceAnimationNames } from "../keyframes/index.js";
import { resolveRecipes } from "../utils/resolve-recipes.js";
import { useInsertionEffect, useMemo, useRef } from "react";

//#region src/tasty/hooks/useStyles.ts
/**
* Check if styles contain @starting-style rules.
*
* @starting-style CSS cannot be applied via multiple class names because
* of cascade - later rules override earlier ones. When @starting is detected,
* we combine top-level styles into a single chunk but keep sub-element styles
* in their own chunk for better caching.
*/
function containsStartingStyle(styleKey) {
	return styleKey.includes("@starting");
}
/**
* Render, cache-key, and allocate a className for a single chunk.
* Returns a ProcessedChunk, or null if the chunk produces no CSS rules.
*/
function processChunk(styles, chunkName, styleKeys) {
	if (styleKeys.length === 0) return null;
	const renderResult = renderStylesForChunk(styles, chunkName, styleKeys);
	if (renderResult.rules.length === 0) return null;
	const cacheKey = generateChunkCacheKey(styles, chunkName, styleKeys);
	const { className } = allocateClassName(cacheKey);
	return {
		name: chunkName,
		styleKeys,
		cacheKey,
		renderResult,
		className
	};
}
/**
* Merge chunk map entries for @starting-style partial chunking.
*
* All non-subcomponent chunks are merged into a single COMBINED entry,
* while SUBCOMPONENTS stays separate. This preserves CSS cascade for
* @starting-style while still allowing sub-element styles to cache independently.
*/
function mergeChunksForStartingStyle(chunkMap) {
	const merged = /* @__PURE__ */ new Map();
	const combinedKeys = [];
	for (const [chunkName, keys] of chunkMap) if (chunkName === CHUNK_NAMES.SUBCOMPONENTS) merged.set(CHUNK_NAMES.SUBCOMPONENTS, keys);
	else combinedKeys.push(...keys);
	if (combinedKeys.length > 0) {
		const result = /* @__PURE__ */ new Map();
		result.set(CHUNK_NAMES.COMBINED, combinedKeys);
		for (const [k, v] of merged) result.set(k, v);
		return result;
	}
	return merged;
}
/**
* Get keyframes that are actually used in styles.
* Returns null if no keyframes are used (fast path for zero overhead).
*
* Optimization order:
* 1. Check if any keyframes are defined (local or global) - if not, return null
* 2. Extract animation names from styles - if none, return null
* 3. Merge and filter keyframes to only used ones
*/
function getUsedKeyframes(styles) {
	const hasLocal = hasLocalKeyframes(styles);
	const hasGlobal = hasGlobalKeyframes();
	if (!hasLocal && !hasGlobal) return null;
	const usedNames = extractAnimationNamesFromStyles(styles);
	if (usedNames.size === 0) return null;
	return filterUsedKeyframes(mergeKeyframes(hasLocal ? extractLocalKeyframes(styles) : null, hasGlobal ? getGlobalKeyframes() : null), usedNames);
}
/**
* Hook to generate CSS classes from Tasty styles.
* Handles style rendering, className allocation, and CSS injection.
*
* Uses chunking to split styles into logical groups for better caching
* and CSS reuse across components.
*
* @example
* ```tsx
* function MyComponent() {
*   const { className } = useStyles({
*     padding: '2x',
*     fill: '#purple',
*     radius: '1r',
*   });
*
*   return <div className={className}>Styled content</div>;
* }
* ```
*/
function useStyles(styles) {
	const disposeRef = useRef([]);
	const stylesRef = useRef({
		key: "",
		styles: void 0
	});
	const resolvedStyles = useMemo(() => {
		if (!styles) return styles;
		return resolveRecipes(styles);
	}, [styles]);
	const styleKey = useMemo(() => {
		if (!resolvedStyles || Object.keys(resolvedStyles).length === 0) return "";
		return stringifyStyles(resolvedStyles);
	}, [resolvedStyles]);
	if (stylesRef.current.key !== styleKey) stylesRef.current = {
		key: styleKey,
		styles: resolvedStyles
	};
	const processedChunks = useMemo(() => {
		const currentStyles = stylesRef.current.styles;
		if (!styleKey || !currentStyles) return [];
		let chunkMap = categorizeStyleKeys(currentStyles);
		if (containsStartingStyle(styleKey)) chunkMap = mergeChunksForStartingStyle(chunkMap);
		const chunks = [];
		for (const [chunkName, chunkStyleKeys] of chunkMap) {
			const chunk = processChunk(currentStyles, chunkName, chunkStyleKeys);
			if (chunk) chunks.push(chunk);
		}
		return chunks;
	}, [styleKey]);
	useInsertionEffect(() => {
		disposeRef.current.forEach((dispose) => dispose?.());
		disposeRef.current = [];
		if (processedChunks.length === 0) return;
		const currentStyles = stylesRef.current.styles;
		const usedKeyframes = currentStyles ? getUsedKeyframes(currentStyles) : null;
		let nameMap = null;
		if (usedKeyframes) {
			nameMap = /* @__PURE__ */ new Map();
			for (const [name, steps] of Object.entries(usedKeyframes)) {
				const result = keyframes(steps, { name });
				const injectedName = result.toString();
				if (injectedName !== name) nameMap.set(name, injectedName);
				disposeRef.current.push(result.dispose);
			}
			if (nameMap.size === 0) nameMap = null;
		}
		if (currentStyles && hasLocalProperties(currentStyles)) {
			const localProperties = extractLocalProperties(currentStyles);
			if (localProperties) for (const [token, definition] of Object.entries(localProperties)) property(token, definition);
		}
		for (const chunk of processedChunks) if (chunk.renderResult.rules.length > 0) {
			const { dispose } = inject(nameMap ? chunk.renderResult.rules.map((rule) => ({
				...rule,
				declarations: replaceAnimationNames(rule.declarations, nameMap)
			})) : chunk.renderResult.rules, { cacheKey: chunk.cacheKey });
			disposeRef.current.push(dispose);
		}
		return () => {
			disposeRef.current.forEach((dispose) => dispose?.());
			disposeRef.current = [];
		};
	}, [processedChunks]);
	return { className: useMemo(() => {
		return processedChunks.map((chunk) => chunk.className).join(" ");
	}, [processedChunks]) };
}

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