/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-1fc5ad1 | Cube Dev Team */
import { StyleParser } from "../parser/parser.js";
import { okhslFunc } from "../plugins/okhsl-plugin.js";
import { cacheWrapper } from "./cache-wrapper.js";
import { okhslToRgb } from "./okhsl-to-rgb.js";
import { hslToRgb } from "./hsl-to-rgb.js";

//#region src/tasty/utils/styles.ts
/**
* Normalize a color token value.
* - Boolean `true` is converted to `'transparent'`
* - Boolean `false` returns `null` (signals the token should be skipped)
* - Other values are returned as-is
*
* @param value - The raw token value
* @returns Normalized value or null if the token should be skipped
*/
function normalizeColorTokenValue(value) {
	if (value === true) return "transparent";
	if (value === false) return null;
	return value;
}
const devMode = process.env.NODE_ENV !== "production";
const COLOR_VAR_PATTERN = /var\(--([a-z0-9-]+)-color/;
const COLOR_VAR_RGB_PATTERN = /var\(--([a-z0-9-]+)-color-rgb/;
const RGB_ALPHA_PATTERN = /\/\s*([0-9.]+)\)/;
const SIMPLE_COLOR_PATTERNS = [
	/^#[0-9a-fA-F]{3,8}$/,
	/^rgb\(/,
	/^hsl\(/,
	/^lch\(/,
	/^oklch\(/,
	/^okhsl\(/,
	/^var\(--[a-z0-9-]+-color/,
	/^currentColor$/,
	/^transparent$/
];
let colorWarningCount = 0;
const MAX_COLOR_WARNINGS = 10;
const CUSTOM_UNITS = {
	r: "6px",
	cr: "10px",
	bw: "1px",
	ow: "3px",
	x: "8px",
	fs: "var(--font-size)",
	lh: "var(--line-height)",
	sf: function sf(num) {
		return `minmax(0, ${num}fr)`;
	}
};
const DIRECTIONS = [
	"top",
	"right",
	"bottom",
	"left"
];
const __tastyParser = new StyleParser({ units: CUSTOM_UNITS });
const __tastyFuncs = { okhsl: okhslFunc };
__tastyParser.setFuncs(__tastyFuncs);
/**
* Get the global StyleParser instance.
* Used by configure() to apply parser configuration.
*/
function getGlobalParser() {
	return __tastyParser;
}
/**
* Get the current custom functions registry.
* Used by configure() to merge with new functions.
*/
function getGlobalFuncs() {
	return __tastyFuncs;
}
/**
* Storage for predefined tokens that are replaced during style parsing.
* Keys are token names (with $ or # prefix), values are pre-processed CSS values.
*/
let __globalPredefinedTokens = null;
/**
* Set global predefined tokens.
* Called from configure() after processing token values.
* Merges with existing tokens (new tokens override existing ones with same key).
* Keys are normalized to lowercase (parser lowercases input before classification).
* @internal
*/
function setGlobalPredefinedTokens(tokens) {
	const normalizedTokens = {};
	for (const [key, value] of Object.entries(tokens)) {
		const lowerKey = key.toLowerCase();
		const lowerValue = value.toLowerCase();
		if (lowerKey.startsWith("#") && lowerValue === "#current") {
			console.warn(`Tasty: Using #current to define color token "${key}" is not supported. The #current token represents currentcolor which cannot be used as a base for other tokens.`);
			continue;
		}
		normalizedTokens[lowerKey] = value;
	}
	__globalPredefinedTokens = __globalPredefinedTokens ? {
		...__globalPredefinedTokens,
		...normalizedTokens
	} : normalizedTokens;
	__tastyParser.clearCache();
}
/**
* Get the current global predefined tokens.
* Returns null if no tokens are configured.
*/
function getGlobalPredefinedTokens() {
	return __globalPredefinedTokens;
}
/**
*
* @param {String} value
* @param {Number} mode
* @returns {Object<String,String|Array>}
*/
function parseStyle(value) {
	let str;
	if (typeof value === "string") str = value;
	else if (typeof value === "number") str = String(value);
	else str = "";
	return __tastyParser.process(str);
}
/**
* Parse color. Find it value, name and opacity.
* Optimized to avoid heavy parseStyle calls for simple color patterns.
*/
function parseColor(val, ignoreError = false) {
	if (typeof val !== "string") val = String(val ?? "");
	val = val.trim();
	if (!val) return {};
	const isSimpleColor = SIMPLE_COLOR_PATTERNS.some((pattern) => pattern.test(val));
	let firstColor;
	if (isSimpleColor) firstColor = val;
	else {
		const extractedColor = parseStyle(val).groups.find((g) => g.colors.length)?.colors[0];
		if (!extractedColor) {
			if (!ignoreError && devMode && colorWarningCount < MAX_COLOR_WARNINGS) {
				console.warn("CubeUIKit: unable to parse color:", val);
				colorWarningCount++;
				if (colorWarningCount === MAX_COLOR_WARNINGS) console.warn("CubeUIKit: color parsing warnings will be suppressed from now on");
			}
			return {};
		}
		firstColor = extractedColor;
	}
	let nameMatch = firstColor.match(COLOR_VAR_PATTERN);
	if (!nameMatch) nameMatch = firstColor.match(COLOR_VAR_RGB_PATTERN);
	let opacity;
	if (firstColor.startsWith("rgb") || firstColor.startsWith("hsl") || firstColor.startsWith("lch") || firstColor.startsWith("oklch") || firstColor.startsWith("okhsl")) {
		const alphaMatch = firstColor.match(RGB_ALPHA_PATTERN);
		if (alphaMatch) {
			const v = parseFloat(alphaMatch[1]);
			if (!isNaN(v)) opacity = v * 100;
		}
	}
	return {
		color: firstColor,
		name: nameMatch ? nameMatch[1] : void 0,
		opacity
	};
}
function strToRgb(color, ignoreAlpha = false) {
	if (!color) return void 0;
	if (color.startsWith("rgb")) return color;
	if (color.startsWith("#")) return hexToRgb(color);
	if (color.startsWith("okhsl(")) return okhslToRgb(color);
	if (color.startsWith("hsl")) return hslToRgb(color);
	return null;
}
/**
* Extract RGB values from an rgb()/rgba() string.
* Supports all modern CSS syntax variations:
* - Comma-separated: rgb(255, 128, 0)
* - Space-separated: rgb(255 128 0)
* - Fractional: rgb(128.5, 64.3, 32.1)
* - Percentages: rgb(50%, 25%, 75%)
* - Slash alpha notation: rgb(255 128 0 / 0.5)
*
* Returns array of RGB values (0-255 range), converting percentages as needed.
*/
function getRgbValuesFromRgbaString(str) {
	const match = str.match(/rgba?\(([^)]+)\)/i);
	if (!match) return [];
	const [colorPart] = match[1].trim().split("/");
	return colorPart.trim().split(/[,\s]+/).filter(Boolean).slice(0, 3).map((part) => {
		part = part.trim();
		if (part.endsWith("%")) return parseFloat(part) / 100 * 255;
		return parseFloat(part);
	});
}
function hexToRgb(hex) {
	const rgba = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, (m, r, g, b) => "#" + r + r + g + g + b + b).substring(1).match(/.{2}/g).map((x, i) => parseInt(x, 16) * (i === 3 ? 1 / 255 : 1));
	if (rgba.some((v) => Number.isNaN(v))) return null;
	if (rgba.length >= 3) return `rgb(${rgba.slice(0, 3).join(" ")}${rgba.length > 3 ? ` / ${rgba[3]}` : ""})`;
	return null;
}
function filterMods(mods, allowedMods) {
	return mods.filter((mod) => allowedMods.includes(mod));
}
const STATES_REGEXP = /([&|!^])|([()])|(@media:[a-z]+)|(@media\([^)]+\))|(@root\([^)]+\))|(@own\([^)]+\))|(@\([^)]+\))|(@starting)|(@[A-Za-z][A-Za-z0-9-]*)|([a-z][a-z0-9-]+=(?:"[^"]*"|'[^']*'|[^\s&|!^()]+))|([a-z][a-z0-9-]+)|(:[a-z][a-z0-9-]+\([^)]+\)|:[a-z][a-z0-9-]+)|(\.[a-z][a-z0-9-]+)|(\[[^\]]+])/gi;
const STATE_OPERATOR_LIST = [
	"!",
	"&",
	"|",
	"^"
];
/**
*
*/
function convertTokensToComputeUnits(tokens) {
	if (tokens.length === 1) return tokens[0];
	STATE_OPERATOR_LIST.forEach((operator) => {
		let i;
		while ((i = tokens.indexOf(operator)) !== -1) {
			const token = tokens[i];
			if (token === "!") if (tokens[i + 1] && tokens[i + 1].length !== 1) tokens.splice(i, 2, ["!", tokens[i + 1]]);
			else tokens.splice(i, 1);
			else if (tokens[i - 1] && tokens[i + 1] && tokens[i - 1].length !== 1 && tokens[i + 1].length !== 1) tokens.splice(i - 1, 3, [
				token,
				tokens[i - 1],
				tokens[i + 1]
			]);
			else tokens.splice(i, 1);
		}
	});
	return tokens.length === 1 ? tokens[0] : tokens;
}
/**
* Replace commas with | only outside of parentheses.
* This preserves commas in advanced states like @(card, w < 600px)
*/
function replaceCommasOutsideParens(str) {
	let result = "";
	let depth = 0;
	for (let i = 0; i < str.length; i++) {
		const char = str[i];
		if (char === "(") {
			depth++;
			result += char;
		} else if (char === ")") {
			depth--;
			result += char;
		} else if (char === "," && depth === 0) result += "|";
		else result += char;
	}
	return result;
}
/**
* Parse state notation and return tokens, modifiers and compute model.
*/
function parseStateNotationInner(notation, value) {
	const tokens = replaceCommasOutsideParens(notation).match(STATES_REGEXP);
	if (!tokens || !tokens.length) return {
		model: void 0,
		mods: [],
		notMods: [],
		tokens: [],
		value
	};
	else if (tokens.length === 1) return {
		model: tokens[0],
		mods: tokens.slice(0),
		notMods: [],
		tokens,
		value
	};
	const mods = [];
	let operations = [[]];
	let list = operations[0];
	let position = 0;
	let operation;
	tokens.forEach((token) => {
		switch (token) {
			case "(":
				operation = [];
				position++;
				list = operations[position] = operation;
				break;
			case ")":
				position--;
				operations[position].push(convertTokensToComputeUnits(list));
				list = operations[position];
				break;
			default:
				if (token.length > 1) {
					if (!mods.includes(token)) mods.push(token);
				}
				list.push(token);
		}
	});
	while (position) {
		position--;
		operations[position].push(convertTokensToComputeUnits(list));
		list = operations[position];
	}
	return {
		tokens,
		mods,
		notMods: [],
		model: convertTokensToComputeUnits(operations[0]),
		value
	};
}
const parseStateNotation = cacheWrapper(parseStateNotationInner);
const _innerCache = /* @__PURE__ */ new WeakMap();
function stringifyStyles(styles) {
	if (styles == null || typeof styles !== "object") return "";
	const keys = Object.keys(styles).sort();
	const parts = [];
	for (let i = 0; i < keys.length; i++) {
		const k = keys[i], v = styles[k];
		if (v === void 0 || typeof v === "function" || typeof v === "symbol") continue;
		const c0 = k.charCodeAt(0);
		const needsInnerSort = (c0 >= 65 && c0 <= 90 || c0 === 38) && v && typeof v === "object" && !Array.isArray(v);
		let sv;
		if (needsInnerSort) {
			sv = _innerCache.get(v);
			if (sv === void 0) {
				const innerKeys = Object.keys(v).sort();
				const innerParts = [];
				for (let j = 0; j < innerKeys.length; j++) {
					const ik = innerKeys[j];
					const ivs = JSON.stringify(v[ik]);
					if (ivs !== void 0) innerParts.push(JSON.stringify(ik) + ":" + ivs);
				}
				sv = "{" + innerParts.join(",") + "}";
				_innerCache.set(v, sv);
			}
		} else sv = JSON.stringify(v);
		parts.push(JSON.stringify(k) + ":" + sv);
	}
	return "{" + parts.join(",") + "}";
}

//#endregion
export { CUSTOM_UNITS, DIRECTIONS, filterMods, getGlobalFuncs, getGlobalParser, getGlobalPredefinedTokens, getRgbValuesFromRgbaString, normalizeColorTokenValue, parseColor, parseStyle, setGlobalPredefinedTokens, strToRgb, stringifyStyles };
//# sourceMappingURL=styles.js.map