import { D as joinWith } from "./utils-nQU7WCM9.mjs";
import { t as isRuleGroup } from "./isRuleGroup-DztIOOKa.mjs";
import { n as fieldIsValidUtil, r as getFieldsArray, t as prepareRuleGroup } from "./prepareQueryObjects-BBayjIn2.mjs";
import { SpelExpressionEvaluator } from "spel2js";

//#region src/utils/parseSpEL/utils.ts
const isSpELPropertyNode = (expr) => {
	return expr.getType() === "property" || expr.getType() === "variable";
};
const isSpELCompoundNode = (expr) => {
	return expr.getType() === "compound" && expr.getChildren().every((c) => isSpELPropertyNode(c));
};
const isSpELListNode = (expr) => {
	return expr.getType() === "list";
};
const isSpELOpAnd = (expr) => expr.type === "op-and";
const isSpELOpOr = (expr) => expr.type === "op-or";
const isSpELOpMatches = (expr) => expr.type === "matches" && (isSpELIdentifier(expr.children[0]) && isSpELStringLiteral(expr.children[1]) || isSpELIdentifier(expr.children[1]) && isSpELStringLiteral(expr.children[0]) || isSpELIdentifier(expr.children[0]) && isSpELIdentifier(expr.children[1]));
const isSpELIdentifier = (expr) => expr.type === "property" || expr.type === "variable" || expr.type === "compound";
const isSpELStringLiteral = (expr) => expr.type === "string";
const isSpELNumericLiteral = (expr) => expr.type === "number";
const isSpELBooleanLiteral = (expr) => expr.type === "boolean";
const isSpELNullLiteral = (expr) => expr.type === "null";
const isSpELRelationOp = (expr) => expr.type === "op-eq" || expr.type === "op-ne" || expr.type === "op-gt" || expr.type === "op-ge" || expr.type === "op-lt" || expr.type === "op-le";
const isSpELPrimitive = (expr) => isSpELNumericLiteral(expr) || isSpELStringLiteral(expr) || isSpELBooleanLiteral(expr) || isSpELNullLiteral(expr);
const isSpELBetweenValues = (expr) => expr.type === "between" && isSpELIdentifier(expr.children[0]) && expr.children[1].type === "list" && expr.children[1].children.length >= 2 && expr.children[1].children.every((c) => isSpELPrimitive(c));
const isSpELBetweenFields = (expr) => expr.type === "between" && isSpELIdentifier(expr.children[0]) && expr.children[1].type === "list" && expr.children[1].children.length >= 2 && expr.children[1].children.every((c) => isSpELIdentifier(c));
const processCompiledExpression = (ce) => {
	const type = ce.getType();
	const identifier = isSpELCompoundNode(ce) ? ce.getChildren().map((p) => isSpELPropertyNode(p) ? p.getRaw() : "").join(".") : isSpELPropertyNode(ce) ? ce.getRaw() : null;
	const children = type === "compound" ? [] : (isSpELListNode(ce) ? ce.getRaw : ce.getChildren)().map((c) => processCompiledExpression(c));
	const startPosition = ce.getStartPosition();
	const endPosition = ce.getEndPosition();
	const value = ce.getValue.length === 0 ? ce.getValue() : "N/A";
	return {
		type: type === "compound" && !identifier ? "invalid" : type,
		children,
		startPosition,
		endPosition,
		value,
		identifier
	};
};
const normalizeOperator = (opType, flip) => {
	if (flip) {
		if (opType === "op-lt") return ">";
		if (opType === "op-le") return ">=";
		if (opType === "op-gt") return "<";
		if (opType === "op-ge") return "<=";
	}
	return {
		"op-eq": "=",
		"op-ge": ">=",
		"op-gt": ">",
		"op-le": "<=",
		"op-lt": "<",
		"op-ne": "!="
	}[opType];
};
const generateFlatAndOrList = (expr) => {
	const combinator = expr.type.slice(3);
	const [left, right] = expr.children;
	if (left.type === "op-and" || left.type === "op-or") return [
		...generateFlatAndOrList(left),
		combinator,
		right
	];
	return [
		left,
		combinator,
		right
	];
};
const generateMixedAndOrList = (expr) => {
	const arr = generateFlatAndOrList(expr);
	const returnArray = [];
	let startIndex = 0;
	for (let i = 0; i < arr.length; i += 2) if (arr[i + 1] === "and") {
		startIndex = i;
		let j = 1;
		while (arr[startIndex + j] === "and") {
			i += 2;
			j += 2;
		}
		const tempAndArray = arr.slice(startIndex, i + 1);
		returnArray.push(tempAndArray);
		i -= 2;
	} else if (arr[i + 1] === "or") if (i === 0 || i === arr.length - 3) {
		if (i === 0 || arr[i - 1] === "or") returnArray.push(arr[i]);
		returnArray.push(arr[i + 1]);
		if (i === arr.length - 3) returnArray.push(arr[i + 2]);
	} else if (arr[i - 1] === "and") returnArray.push(arr[i + 1]);
	else returnArray.push(arr[i], arr[i + 1]);
	if (returnArray.length === 1 && Array.isArray(returnArray[0])) return returnArray[0];
	return returnArray;
};

//#endregion
//#region src/utils/parseSpEL/parseSpEL.ts
function parseSpEL(spel, options = {}) {
	const { fields, independentCombinators, listsAsArrays } = options;
	const ic = !!independentCombinators;
	const fieldsFlat = getFieldsArray(fields);
	const fieldIsValid = (fieldName, operator, subordinateFieldName) => fieldIsValidUtil({
		fieldName,
		fieldsFlat,
		operator,
		subordinateFieldName,
		getValueSources: options?.getValueSources
	});
	const emptyQuery = {
		rules: [],
		...ic ? {} : { combinator: "and" }
	};
	const parseProcessedSpEL = (expr, processOpts = {}) => {
		const { forwardNegation: _forwardedNegation, groupOnlyIfNecessary: _g } = processOpts;
		if (expr.type === "op-not") {
			const negatedExpr = parseProcessedSpEL(expr.children[0]);
			// istanbul ignore else
			if (negatedExpr) {
				if (!isRuleGroup(negatedExpr) && (negatedExpr.operator === "contains" || negatedExpr.operator === "beginsWith" || negatedExpr.operator === "endsWith")) return {
					...negatedExpr,
					operator: `doesNot${negatedExpr.operator[0].toUpperCase()}${negatedExpr.operator.slice(1).replace("s", "")}`
				};
				return ic ? {
					rules: [negatedExpr],
					not: true
				} : {
					combinator: "and",
					rules: [negatedExpr],
					not: true
				};
			}
		} else if (isSpELOpAnd(expr) || isSpELOpOr(expr)) {
			if (ic) {
				const rules$1 = generateFlatAndOrList(expr).map((v) => {
					if (typeof v === "string") return v;
					return parseProcessedSpEL(v);
				});
				if (!rules$1.every(Boolean)) return null;
				return { rules: rules$1 };
			}
			const andOrList = generateMixedAndOrList(expr);
			const combinator = andOrList[1];
			const rules = andOrList.filter((v) => Array.isArray(v) || !!v && typeof v !== "string" && "type" in v).map((v) => Array.isArray(v) ? v.filter((vf) => !!v && typeof vf !== "string" && "type" in vf) : v).map((exp) => {
				if (Array.isArray(exp)) return {
					combinator: "and",
					rules: exp.map((e) => parseProcessedSpEL(e)).filter(Boolean)
				};
				return parseProcessedSpEL(exp);
			}).filter(Boolean);
			// istanbul ignore else
			if (rules.length > 0) return {
				combinator,
				rules
			};
		} else if (isSpELOpMatches(expr)) {
			const [left, right] = expr.children;
			let field = "";
			let regex = "";
			let valueSource = void 0;
			if (isSpELIdentifier(left)) {
				field = left.identifier;
				if (isSpELIdentifier(right)) {
					regex = right.identifier;
					valueSource = "field";
				} else if (isSpELPrimitive(right)) regex = right.value;
			} else if (isSpELIdentifier(right) && isSpELPrimitive(left)) {
				field = right.identifier;
				regex = left.value;
			}
			if (/^(?!\^).*?(?<!\$)$/.test(regex)) {
				// istanbul ignore else
				if (fieldIsValid(field, "contains")) return {
					field,
					operator: "contains",
					value: regex,
					...valueSource ? { valueSource } : {}
				};
			} else if (/^\^.*?(?<!\$)$/.test(regex)) {
				// istanbul ignore else
				if (fieldIsValid(field, "beginsWith")) return {
					field,
					operator: "beginsWith",
					value: regex.replace(/^\^/, "")
				};
			} else if (/^(?!\^).*?\$$/.test(regex)) {
				// istanbul ignore else
				if (fieldIsValid(field, "endsWith")) return {
					field,
					operator: "endsWith",
					value: regex.replace(/\$$/, "")
				};
			}
		} else if (isSpELBetweenValues(expr) || isSpELBetweenFields(expr)) {
			let values = [null, null];
			let valueSource = void 0;
			const [{ identifier: field }, { children: [left, right] }] = expr.children;
			if (isSpELBetweenValues(expr)) values = [left.value, right.value];
			else {
				values = [left.identifier, right.identifier];
				valueSource = "field";
			}
			// istanbul ignore else
			if (field && values.every((v) => fieldIsValid(field, "between", valueSource === "field" ? v : void 0))) {
				const valueArray = values[0] < values[1] || valueSource === "field" ? values : [values[1], values[0]];
				const value = listsAsArrays ? valueArray : joinWith(valueArray, ",");
				return valueSource ? {
					field,
					operator: "between",
					value,
					valueSource
				} : {
					field,
					operator: "between",
					value
				};
			}
		} else if (isSpELRelationOp(expr)) {
			let field = null;
			let value = void 0;
			let valueSource = void 0;
			let flip = false;
			const [left, right] = expr.children;
			if (isSpELIdentifier(left)) {
				field = left.identifier;
				if (isSpELIdentifier(right)) {
					value = right.identifier;
					valueSource = "field";
				} else if (isSpELPrimitive(right)) value = right.value;
			} else if (isSpELIdentifier(right) && isSpELPrimitive(left)) {
				flip = true;
				field = right.identifier;
				value = left.value;
			}
			let operator = normalizeOperator(expr.type, flip);
			if (value === null && (operator === "=" || operator === "!=")) operator = operator === "=" ? "null" : "notNull";
			if (field && fieldIsValid(field, operator, valueSource === "field" ? value : void 0) && value !== void 0) return valueSource ? {
				field,
				operator,
				value,
				valueSource
			} : {
				field,
				operator,
				value
			};
		}
		return null;
	};
	const prepare = options.generateIDs ? prepareRuleGroup : (g) => g;
	let compiledSpEL;
	try {
		compiledSpEL = SpelExpressionEvaluator.compile(spel)._compiledExpression;
	} catch {
		return prepare(emptyQuery);
	}
	const result = parseProcessedSpEL(processCompiledExpression(compiledSpEL));
	if (result) {
		if (isRuleGroup(result)) return prepare(result);
		return prepare({
			rules: [result],
			...ic ? {} : { combinator: "and" }
		});
	}
	return prepare(emptyQuery);
}

//#endregion
export { parseSpEL };
//# sourceMappingURL=parseSpEL.mjs.map