/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-f1f3d22 | Cube Dev Team */
import { COLOR_FUNCS, RE_HEX, RE_NUMBER, RE_RAW_UNIT, RE_UNIT_NUM, VALUE_KEYWORDS } from "./const.js";
import { Bucket } from "./types.js";
import { StyleParser } from "./parser.js";
import { getGlobalPredefinedTokens } from "../utils/styles.js";

//#region src/tasty/parser/classify.ts
/**
* Re-parses a value through the parser until it stabilizes (no changes)
* or max iterations reached. This allows units to reference other units.
* Example: { x: '8px', y: '2x' } -> '1y' resolves to '16px'
*/
function resolveUntilStable(value, opts, recurse, maxIterations = 10) {
	let current = value;
	for (let i = 0; i < maxIterations; i++) {
		const unitMatch = current.match(RE_UNIT_NUM);
		if (!unitMatch) break;
		const unitName = unitMatch[1];
		if (!opts.units || !(unitName in opts.units)) break;
		const result = recurse(current);
		if (result.output === current) break;
		current = result.output;
	}
	return current;
}
function classify(raw, opts, recurse) {
	const token = raw.trim();
	if (!token) return {
		bucket: Bucket.Mod,
		processed: ""
	};
	{
		let depth = 0;
		let inQuote = 0;
		for (let i = 0; i < token.length; i++) {
			const ch = token[i];
			if (inQuote) {
				if (ch === inQuote && token[i - 1] !== "\\") inQuote = 0;
				continue;
			}
			if (ch === "\"" || ch === "'") {
				inQuote = ch;
				continue;
			}
			if (ch === "(") depth++;
			else if (ch === ")") depth = Math.max(0, depth - 1);
		}
		if (depth !== 0) {
			console.warn("tasty: skipped invalid function token with unmatched parentheses:", token);
			return {
				bucket: Bucket.Mod,
				processed: ""
			};
		}
	}
	if (token.startsWith("\"") && token.endsWith("\"") || token.startsWith("'") && token.endsWith("'")) return {
		bucket: Bucket.Value,
		processed: token
	};
	if (token.startsWith("$$")) {
		const name = token.slice(2);
		if (/^[a-z_][a-z0-9-_]*$/i.test(name)) return {
			bucket: Bucket.Value,
			processed: `--${name}`
		};
	}
	if (token.startsWith("##")) {
		const name = token.slice(2);
		if (/^[a-z_][a-z0-9-_]*$/i.test(name)) return {
			bucket: Bucket.Value,
			processed: `--${name}-color`
		};
	}
	if (token === "#current") return {
		bucket: Bucket.Color,
		processed: "currentcolor"
	};
	const currentAlphaMatch = token.match(/^#current\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
	if (currentAlphaMatch) {
		const rawAlpha = currentAlphaMatch[1];
		let percentage;
		if (rawAlpha.startsWith("$")) percentage = `calc(var(--${rawAlpha.slice(1)}) * 100%)`;
		else if (rawAlpha === "0") percentage = "0%";
		else percentage = `${parseFloat("." + rawAlpha) * 100}%`;
		return {
			bucket: Bucket.Color,
			processed: `color-mix(in oklab, currentcolor ${percentage}, transparent)`
		};
	}
	if (token[0] === "$" || token[0] === "#") {
		const predefinedTokens = getGlobalPredefinedTokens();
		if (predefinedTokens) {
			if (token in predefinedTokens) {
				const tokenValue = predefinedTokens[token];
				return classify(tokenValue.toLowerCase(), opts, recurse);
			}
			if (token[0] === "#") {
				const alphaMatch = token.match(/^(#[a-z0-9-]+)\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
				if (alphaMatch) {
					const [, baseToken, rawAlpha] = alphaMatch;
					if (baseToken in predefinedTokens) {
						const resolvedValue = predefinedTokens[baseToken];
						if (resolvedValue.startsWith("#")) return classify(`${resolvedValue.toLowerCase()}.${rawAlpha}`, opts, recurse);
						const funcMatch = resolvedValue.match(/^(rgba?|hsla?|hwb|oklab|oklch|lab|lch|color|okhsl|device-cmyk|gray|color-mix|color-contrast)\((.+)\)$/i);
						if (funcMatch) {
							const [, funcName, args] = funcMatch;
							let alpha;
							if (rawAlpha.startsWith("$")) alpha = `var(--${rawAlpha.slice(1)})`;
							else alpha = rawAlpha === "0" ? "0" : `.${rawAlpha}`;
							const normalizedFunc = funcName.replace(/a$/i, "").toLowerCase();
							const normalizeArgs = (a) => {
								let result = "";
								let depth = 0;
								for (let i = 0; i < a.length; i++) {
									const c = a[i];
									if (c === "(") {
										depth++;
										result += c;
									} else if (c === ")") {
										depth = Math.max(0, depth - 1);
										result += c;
									} else if (c === "," && depth === 0) {
										while (i + 1 < a.length && /\s/.test(a[i + 1])) i++;
										result += " ";
									} else result += c;
								}
								return result;
							};
							const findLastTopLevel = (str, ch) => {
								let depth = 0;
								for (let i = str.length - 1; i >= 0; i--) {
									const c = str[i];
									if (c === ")") depth++;
									else if (c === "(") depth = Math.max(0, depth - 1);
									else if (c === ch && depth === 0) return i;
								}
								return -1;
							};
							const slashIdx = findLastTopLevel(args, "/");
							const hasModernAlpha = slashIdx !== -1;
							let topLevelCommaCount = 0;
							let lastTopLevelComma = -1;
							{
								let depth = 0;
								for (let i = 0; i < args.length; i++) {
									const c = args[i];
									if (c === "(") depth++;
									else if (c === ")") depth = Math.max(0, depth - 1);
									else if (c === "," && depth === 0) {
										topLevelCommaCount++;
										lastTopLevelComma = i;
									}
								}
							}
							const hasLegacyAlpha = !hasModernAlpha && /a$/i.test(funcName) && topLevelCommaCount === 3;
							if (hasModernAlpha || hasLegacyAlpha) {
								const withoutAlpha = hasModernAlpha ? args.slice(0, slashIdx).trim() : args.slice(0, lastTopLevelComma).trim();
								return {
									bucket: Bucket.Color,
									processed: `${normalizedFunc}(${normalizeArgs(withoutAlpha)} / ${alpha})`
								};
							}
							return {
								bucket: Bucket.Color,
								processed: `${normalizedFunc}(${normalizeArgs(args)} / ${alpha})`
							};
						}
						return classify(`${resolvedValue}.${rawAlpha}`, opts, recurse);
					}
				}
			}
		}
	}
	if (token.match(/^var\(--([a-z0-9-]+)-color\)$/)) return {
		bucket: Bucket.Color,
		processed: token
	};
	if (token.startsWith("url(")) return {
		bucket: Bucket.Value,
		processed: token
	};
	if (token[0] === "$") {
		const identMatch = token.match(/^\$([a-z_][a-z0-9-_]*)$/);
		if (identMatch) {
			const name = identMatch[1];
			const processed = `var(--${name})`;
			return {
				bucket: name.endsWith("-color") ? Bucket.Color : Bucket.Value,
				processed
			};
		}
	}
	if (token[0] === "#" && token.length > 1) {
		const alphaMatch = token.match(/^#([a-z0-9-]+)\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
		if (alphaMatch) {
			const [, base, rawAlpha] = alphaMatch;
			let alpha;
			if (rawAlpha.startsWith("$")) alpha = `var(--${rawAlpha.slice(1)})`;
			else if (rawAlpha === "0") alpha = "0";
			else alpha = `.${rawAlpha}`;
			return {
				bucket: Bucket.Color,
				processed: `rgb(var(--${base}-color-rgb) / ${alpha})`
			};
		}
		const name = token.slice(1);
		if (RE_HEX.test(name)) return {
			bucket: Bucket.Color,
			processed: `var(--${name}-color, #${name})`
		};
		return {
			bucket: Bucket.Color,
			processed: `var(--${name}-color)`
		};
	}
	const openIdx = token.indexOf("(");
	if (openIdx > 0 && token.endsWith(")")) {
		const fname = token.slice(0, openIdx);
		const inner = token.slice(openIdx + 1, -1);
		if (COLOR_FUNCS.has(fname)) {
			const argProcessed = recurse(inner).output.replace(/,\s+/g, ",");
			return {
				bucket: Bucket.Color,
				processed: `${fname}(${argProcessed})`
			};
		}
		if (opts.funcs && fname in opts.funcs) {
			const tmp = new StyleParser(opts).process(inner);
			return classify(opts.funcs[fname](tmp.groups), {
				...opts,
				funcs: void 0
			}, recurse);
		}
		const argProcessed = recurse(inner).output;
		return {
			bucket: Bucket.Value,
			processed: `${fname}(${argProcessed})`
		};
	}
	if (token.startsWith("(") && token.endsWith(")")) {
		const colorMatch = token.slice(1, -1).match(/^#([a-z0-9-]+)\s*,\s*(.*)$/i);
		if (colorMatch) {
			const [, name, fallback] = colorMatch;
			const processedFallback = recurse(fallback).output;
			return {
				bucket: Bucket.Color,
				processed: `var(--${name}-color, ${processedFallback})`
			};
		}
	}
	if (token.startsWith("(") && token.endsWith(")")) {
		const match = token.slice(1, -1).match(/^\$([a-z_][a-z0-9-_]*)\s*,\s*(.*)$/);
		if (match) {
			const [, name, fallback] = match;
			const processedFallback = recurse(fallback).output;
			return {
				bucket: name.endsWith("-color") ? Bucket.Color : Bucket.Value,
				processed: `var(--${name}, ${processedFallback})`
			};
		}
	}
	if (token[0] === "(" && token[token.length - 1] === ")") {
		const innerProcessed = recurse(token.slice(1, -1)).output;
		return {
			bucket: Bucket.Value,
			processed: `calc(${innerProcessed})`
		};
	}
	const um = token.match(RE_UNIT_NUM);
	if (um) {
		const unit = um[1];
		const numericPart = parseFloat(token.slice(0, -unit.length));
		const handler = opts.units && opts.units[unit];
		if (handler) if (typeof handler === "string") {
			const rawMatch = handler.match(RE_RAW_UNIT);
			if (rawMatch) {
				const [, baseNum, cssUnit] = rawMatch;
				const resolved = resolveUntilStable(`${numericPart * parseFloat(baseNum)}${cssUnit}`, opts, recurse);
				return {
					bucket: Bucket.Value,
					processed: resolved
				};
			}
			const base = handler;
			if (numericPart === 1) return {
				bucket: Bucket.Value,
				processed: base
			};
			return {
				bucket: Bucket.Value,
				processed: `calc(${numericPart} * ${base})`
			};
		} else {
			const inner = handler(numericPart);
			return {
				bucket: Bucket.Value,
				processed: inner
			};
		}
	}
	if (/^[+-]?(?:\d*\.\d+|\d+)[a-z%]+$/.test(token)) return {
		bucket: Bucket.Value,
		processed: token
	};
	if (RE_NUMBER.test(token)) return {
		bucket: Bucket.Value,
		processed: token
	};
	if (VALUE_KEYWORDS.has(token)) return {
		bucket: Bucket.Value,
		processed: token
	};
	if (token === "transparent" || token === "currentcolor") return {
		bucket: Bucket.Color,
		processed: token
	};
	return {
		bucket: Bucket.Mod,
		processed: token
	};
}

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