/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-b859c78 | Cube Dev Team */
import { Lru } from "../parser/lru.js";
import { camelToKebab } from "../utils/case-converter.js";
import { expandDimensionShorthands, expandTastyUnits, findTopLevelComma, resolvePredefinedState } from "../states/index.js";
import { and, createContainerDimensionCondition, createContainerRawCondition, createContainerStyleCondition, createMediaDimensionCondition, createMediaFeatureCondition, createMediaTypeCondition, createModifierCondition, createOwnCondition, createPseudoCondition, createRootCondition, createStartingCondition, createSupportsCondition, not, or, trueCondition } from "./conditions.js";

//#region src/tasty/pipeline/parseStateKey.ts
/**
* State Key Parser
*
* Parses state notation strings (like 'hovered & !disabled', '@media(w < 768px)')
* into ConditionNode trees for processing in the pipeline.
*/
const parseCache = new Lru(5e3);
/**
* Pattern for tokenizing state notation.
* Matches: operators, parentheses, @-prefixed states, value mods, boolean mods,
* pseudo-classes, class selectors, and attribute selectors.
*
* Note: For @supports and @(...) container queries we need to handle nested parentheses
* like @supports($, :has(*)) or @(scroll-state(stuck: top)).
* We use a pattern that allows one level of nesting: [^()]*(?:\([^)]*\))?[^)]*
*/
const STATE_TOKEN_PATTERN = /([&|!^])|([()])|(@media:[a-z]+)|(@media\([^)]+\))|(@supports\([^()]*(?:\([^)]*\))?[^)]*\))|(@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-]+)|(\[[^\]]+\])/gi;
/**
* Tokenize a state notation string
*/
function tokenize(stateKey) {
	const tokens = [];
	let match;
	const normalized = replaceCommasOutsideParens(stateKey);
	STATE_TOKEN_PATTERN.lastIndex = 0;
	while ((match = STATE_TOKEN_PATTERN.exec(normalized)) !== null) {
		const fullMatch = match[0];
		if (match[1]) switch (fullMatch) {
			case "&":
				tokens.push({
					type: "AND",
					value: "&",
					raw: fullMatch
				});
				break;
			case "|":
				tokens.push({
					type: "OR",
					value: "|",
					raw: fullMatch
				});
				break;
			case "!":
				tokens.push({
					type: "NOT",
					value: "!",
					raw: fullMatch
				});
				break;
			case "^":
				tokens.push({
					type: "XOR",
					value: "^",
					raw: fullMatch
				});
				break;
		}
		else if (match[2]) if (fullMatch === "(") tokens.push({
			type: "LPAREN",
			value: "(",
			raw: fullMatch
		});
		else tokens.push({
			type: "RPAREN",
			value: ")",
			raw: fullMatch
		});
		else tokens.push({
			type: "STATE",
			value: fullMatch,
			raw: fullMatch
		});
	}
	return tokens;
}
/**
* Replace commas with | only outside of parentheses
*/
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;
}
/**
* Parser state
*/
var Parser = class {
	tokens;
	pos = 0;
	options;
	constructor(tokens, options) {
		this.tokens = tokens;
		this.options = options;
	}
	parse() {
		if (this.tokens.length === 0) return trueCondition();
		return this.parseExpression();
	}
	current() {
		return this.tokens[this.pos];
	}
	advance() {
		return this.tokens[this.pos++];
	}
	match(type) {
		if (this.current()?.type === type) {
			this.advance();
			return true;
		}
		return false;
	}
	/**
	* Parse expression with operator precedence:
	* ! (NOT) > ^ (XOR) > | (OR) > & (AND)
	*/
	parseExpression() {
		return this.parseAnd();
	}
	parseAnd() {
		let left = this.parseOr();
		while (this.current()?.type === "AND") {
			this.advance();
			const right = this.parseOr();
			left = and(left, right);
		}
		return left;
	}
	parseOr() {
		let left = this.parseXor();
		while (this.current()?.type === "OR") {
			this.advance();
			const right = this.parseXor();
			left = or(left, right);
		}
		return left;
	}
	parseXor() {
		let left = this.parseUnary();
		while (this.current()?.type === "XOR") {
			this.advance();
			const right = this.parseUnary();
			left = or(and(left, not(right)), and(not(left), right));
		}
		return left;
	}
	parseUnary() {
		if (this.match("NOT")) return not(this.parseUnary());
		return this.parsePrimary();
	}
	parsePrimary() {
		if (this.match("LPAREN")) {
			const expr = this.parseExpression();
			this.match("RPAREN");
			return expr;
		}
		const token = this.current();
		if (token?.type === "STATE") {
			this.advance();
			return this.parseStateToken(token.value);
		}
		return trueCondition();
	}
	/**
	* Parse a state token into a ConditionNode
	*/
	parseStateToken(value) {
		if (value === "@starting") return createStartingCondition(false, value);
		if (value.startsWith("@media:")) return createMediaTypeCondition(value.slice(7), false, value);
		if (value.startsWith("@media(")) return this.parseMediaQuery(value);
		if (value.startsWith("@supports(")) return this.parseSupportsQuery(value);
		if (value.startsWith("@root(")) return this.parseRootState(value);
		if (value.startsWith("@own(")) return this.parseOwnState(value);
		if (value.startsWith("@(")) return this.parseContainerQuery(value);
		if (value.startsWith("@") && /^@[A-Za-z][A-Za-z0-9-]*$/.test(value)) return this.parsePredefinedState(value);
		if (value.startsWith(":")) return createPseudoCondition(value, false, value);
		if (value.startsWith(".")) return createPseudoCondition(value, false, value);
		if (value.startsWith("[")) return createPseudoCondition(value, false, value);
		if (value.includes("=")) return this.parseValueModifier(value);
		return this.parseBooleanModifier(value);
	}
	/**
	* Parse @media(...) query
	*/
	parseMediaQuery(raw) {
		const content = raw.slice(7, -1);
		if (!content.trim()) return trueCondition();
		let condition = expandDimensionShorthands(content);
		condition = expandTastyUnits(condition);
		if (condition.includes(":") && !condition.includes("<") && !condition.includes(">") && !condition.includes("=")) {
			const colonIdx = condition.indexOf(":");
			return createMediaFeatureCondition(condition.slice(0, colonIdx).trim(), condition.slice(colonIdx + 1).trim(), false, raw);
		}
		if (!condition.includes("<") && !condition.includes(">") && !condition.includes("=")) return createMediaFeatureCondition(condition.trim(), void 0, false, raw);
		const { dimension, lowerBound, upperBound } = this.parseDimensionCondition(condition);
		if (!dimension) return createPseudoCondition(raw, false, raw);
		return createMediaDimensionCondition(dimension, lowerBound, upperBound, false, raw);
	}
	/**
	* Parse dimension condition string (e.g., "width < 768px", "600px <= width < 1200px")
	*/
	parseDimensionCondition(condition) {
		const rangeMatch = condition.match(/^(.+?)\s*(<=|<)\s*(width|height|inline-size|block-size)\s*(<=|<)\s*(.+)$/);
		if (rangeMatch) {
			const [, lowerValue, lowerOp, dimension, upperOp, upperValue] = rangeMatch;
			return {
				dimension,
				lowerBound: {
					value: lowerValue.trim(),
					valueNumeric: parseNumericValue(lowerValue.trim()),
					inclusive: lowerOp === "<="
				},
				upperBound: {
					value: upperValue.trim(),
					valueNumeric: parseNumericValue(upperValue.trim()),
					inclusive: upperOp === "<="
				}
			};
		}
		const simpleMatch = condition.match(/^(width|height|inline-size|block-size)\s*(<=|>=|<|>|=)\s*(.+)$/);
		if (simpleMatch) {
			const [, dimension, operator, value] = simpleMatch;
			const numeric = parseNumericValue(value.trim());
			if (operator === "<" || operator === "<=") return {
				dimension,
				upperBound: {
					value: value.trim(),
					valueNumeric: numeric,
					inclusive: operator === "<="
				}
			};
			else if (operator === ">" || operator === ">=") return {
				dimension,
				lowerBound: {
					value: value.trim(),
					valueNumeric: numeric,
					inclusive: operator === ">="
				}
			};
			else if (operator === "=") return {
				dimension,
				lowerBound: {
					value: value.trim(),
					valueNumeric: numeric,
					inclusive: true
				},
				upperBound: {
					value: value.trim(),
					valueNumeric: numeric,
					inclusive: true
				}
			};
		}
		const reversedMatch = condition.match(/^(.+?)\s*(<=|>=|<|>|=)\s*(width|height|inline-size|block-size)$/);
		if (reversedMatch) {
			const [, value, operator, dimension] = reversedMatch;
			const numeric = parseNumericValue(value.trim());
			if (operator === "<" || operator === "<=") return {
				dimension,
				lowerBound: {
					value: value.trim(),
					valueNumeric: numeric,
					inclusive: operator === "<="
				}
			};
			else if (operator === ">" || operator === ">=") return {
				dimension,
				upperBound: {
					value: value.trim(),
					valueNumeric: numeric,
					inclusive: operator === ">="
				}
			};
		}
		return {};
	}
	/**
	* Parse @root(...) state
	*/
	parseRootState(raw) {
		const content = raw.slice(6, -1);
		if (!content.trim()) return trueCondition();
		return createRootCondition(buildRootSelector(content), false, raw);
	}
	/**
	* Parse @supports(...) query
	*
	* Syntax:
	*   @supports(display: grid)     → @supports (display: grid)
	*   @supports($, :has(*))        → @supports selector(:has(*))
	*/
	parseSupportsQuery(raw) {
		const content = raw.slice(10, -1);
		if (!content.trim()) return trueCondition();
		if (content.startsWith("$,")) return createSupportsCondition("selector", content.slice(2).trim(), false, raw);
		return createSupportsCondition("feature", content, false, raw);
	}
	/**
	* Parse @own(...) state
	*/
	parseOwnState(raw) {
		const content = raw.slice(5, -1);
		if (!content.trim()) return trueCondition();
		return createOwnCondition(parseStateKey(content, this.options), false, raw);
	}
	/**
	* Parse @(...) container query
	*/
	parseContainerQuery(raw) {
		const content = raw.slice(2, -1);
		if (!content.trim()) return trueCondition();
		const commaIdx = findTopLevelComma(content);
		let containerName;
		let condition;
		if (commaIdx !== -1) {
			containerName = content.slice(0, commaIdx).trim();
			condition = content.slice(commaIdx + 1).trim();
		} else condition = content.trim();
		if (condition.startsWith("$")) {
			const styleQuery = condition.slice(1);
			const eqIdx = styleQuery.indexOf("=");
			if (eqIdx === -1) return createContainerStyleCondition(styleQuery, void 0, containerName, false, raw);
			const property = styleQuery.slice(0, eqIdx).trim();
			let propertyValue = styleQuery.slice(eqIdx + 1).trim();
			if (propertyValue.startsWith("\"") && propertyValue.endsWith("\"") || propertyValue.startsWith("'") && propertyValue.endsWith("'")) propertyValue = propertyValue.slice(1, -1);
			return createContainerStyleCondition(property, propertyValue, containerName, false, raw);
		}
		if (/^[a-zA-Z][\w-]*\s*\(/.test(condition)) return createContainerRawCondition(condition, containerName, false, raw);
		let expandedCondition = expandDimensionShorthands(condition);
		expandedCondition = expandTastyUnits(expandedCondition);
		const { dimension, lowerBound, upperBound } = this.parseDimensionCondition(expandedCondition);
		if (!dimension) return createPseudoCondition(raw, false, raw);
		return createContainerDimensionCondition(dimension, lowerBound, upperBound, containerName, false, raw);
	}
	/**
	* Parse predefined state (@mobile, @dark, etc.)
	*/
	parsePredefinedState(raw) {
		const ctx = this.options.context;
		if (!ctx) return createPseudoCondition(raw, false, raw);
		const resolved = resolvePredefinedState(raw, ctx);
		if (!resolved) return createModifierCondition(`data-${camelToKebab(raw.slice(1))}`, void 0, "=", false, raw);
		return parseStateKey(resolved, this.options);
	}
	/**
	* Parse value modifier (e.g., theme=danger, size^=sm)
	*/
	parseValueModifier(raw) {
		const opMatch = raw.match(/^([a-z][a-z0-9-]*)(\^=|\$=|\*=|=)(.+)$/i);
		if (!opMatch) return createModifierCondition(`data-${camelToKebab(raw)}`, void 0, "=", false, raw);
		const [, key, operator, value] = opMatch;
		let cleanValue = value;
		if (cleanValue.startsWith("\"") && cleanValue.endsWith("\"") || cleanValue.startsWith("'") && cleanValue.endsWith("'")) cleanValue = cleanValue.slice(1, -1);
		return createModifierCondition(`data-${camelToKebab(key)}`, cleanValue, operator, false, raw);
	}
	/**
	* Parse boolean modifier (e.g., hovered, disabled)
	*/
	parseBooleanModifier(raw) {
		return createModifierCondition(`data-${camelToKebab(raw)}`, void 0, "=", false, raw);
	}
};
/**
* Parse a numeric value from a CSS value string
*/
function parseNumericValue(value) {
	const match = value.match(/^(\d+(?:\.\d+)?)(px|em|rem|vh|vw|%)?$/);
	if (match) return parseFloat(match[1]);
	return null;
}
/**
* Build a root state selector from a condition string
*/
function buildRootSelector(condition) {
	if (condition.startsWith(".")) return condition;
	if (condition.startsWith("[")) return condition;
	if (condition.includes("=")) {
		const [key, value] = condition.split("=");
		return `[data-${camelToKebab(key.trim())}="${value.trim()}"]`;
	}
	return `[data-${camelToKebab(condition)}]`;
}
/**
* Parse a state key string into a ConditionNode
*/
function parseStateKey(stateKey, options = {}) {
	if (!stateKey || !stateKey.trim()) return trueCondition();
	const trimmed = stateKey.trim();
	const ctx = options.context;
	const localStatesKey = ctx && Object.keys(ctx.localPredefinedStates).length > 0 ? JSON.stringify(ctx.localPredefinedStates) : "";
	const cacheKey = JSON.stringify([
		trimmed,
		options.isSubElement,
		localStatesKey
	]);
	const cached = parseCache.get(cacheKey);
	if (cached) return cached;
	const result = new Parser(tokenize(trimmed), options).parse();
	parseCache.set(cacheKey, result);
	return result;
}

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