/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
import path from "node:path";
import fs from "node:fs";
import { Node, Project, SyntaxKind, ts } from "ts-morph";
//#region ../css/dist/purger/optimized/ast/expressions.mjs
/*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
const importedSourceProject = new Project({ useInMemoryFileSystem: true });
const importedSourceFileCache = /* @__PURE__ */ new Map();
const tsConfigPathCache = /* @__PURE__ */ new Map();
const compilerOptionsCache = /* @__PURE__ */ new Map();
const hasImportQuery = (moduleSpecifier) => moduleSpecifier.includes("?");
const getLocalDefinitionNodes = (node) => {
	if (!Node.isIdentifier(node)) return [];
	const name = node.getText();
	const nodeStart = node.getStart();
	const sourceFile = node.getSourceFile();
	return [
		...sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration).filter((definition) => definition.getName() === name && definition.getStart() <= nodeStart),
		...sourceFile.getDescendantsOfKind(SyntaxKind.Parameter).filter((definition) => definition.getName() === name && definition.getStart() <= nodeStart),
		...sourceFile.getDescendantsOfKind(SyntaxKind.BindingElement).filter((definition) => definition.getName() === name && definition.getStart() <= nodeStart),
		...sourceFile.getFunctions().filter((definition) => definition.getName() === name && definition.getStart() <= nodeStart)
	].sort((left, right) => right.getStart() - left.getStart()).slice(0, 1);
};
const getDefinitionNodesSafe = (node) => {
	return getLocalDefinitionNodes(node);
};
const isExistingFile = (candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isFile();
const resolveRelativeImportPath = (sourceFilePath, moduleSpecifier) => {
	if (!path.isAbsolute(sourceFilePath) || !moduleSpecifier.startsWith(".") || hasImportQuery(moduleSpecifier)) return null;
	const basePath = path.resolve(path.dirname(sourceFilePath), moduleSpecifier);
	return [
		basePath,
		`${basePath}.ts`,
		`${basePath}.tsx`,
		`${basePath}.mjs`,
		`${basePath}.cjs`,
		`${basePath}.js`,
		`${basePath}.jsx`,
		path.join(basePath, "index.ts"),
		path.join(basePath, "index.tsx"),
		path.join(basePath, "index.mjs"),
		path.join(basePath, "index.cjs"),
		path.join(basePath, "index.js"),
		path.join(basePath, "index.jsx")
	].find((candidate) => isExistingFile(candidate)) ?? null;
};
const findNearestTypeScriptConfig = (sourceFilePath) => {
	const sourceDir = path.dirname(sourceFilePath);
	const cached = tsConfigPathCache.get(sourceDir);
	if (cached !== void 0) return cached;
	let currentDir = sourceDir;
	while (true) {
		const tsConfigPath = path.join(currentDir, "tsconfig.json");
		if (isExistingFile(tsConfigPath)) {
			tsConfigPathCache.set(sourceDir, tsConfigPath);
			return tsConfigPath;
		}
		const jsConfigPath = path.join(currentDir, "jsconfig.json");
		if (isExistingFile(jsConfigPath)) {
			tsConfigPathCache.set(sourceDir, jsConfigPath);
			return jsConfigPath;
		}
		const parentDir = path.dirname(currentDir);
		if (parentDir === currentDir) {
			tsConfigPathCache.set(sourceDir, null);
			return null;
		}
		currentDir = parentDir;
	}
};
const getCompilerOptionsForSourceFile = (sourceFilePath) => {
	const configPath = findNearestTypeScriptConfig(sourceFilePath);
	if (!configPath) return null;
	const cached = compilerOptionsCache.get(configPath);
	if (cached !== void 0) return cached;
	const readResult = ts.readConfigFile(configPath, ts.sys.readFile);
	if (readResult.error) {
		compilerOptionsCache.set(configPath, null);
		return null;
	}
	const parsed = ts.parseJsonConfigFileContent(readResult.config, ts.sys, path.dirname(configPath));
	const compilerOptions = parsed.options.paths && !parsed.options.baseUrl ? {
		...parsed.options,
		baseUrl: path.dirname(configPath)
	} : parsed.options;
	compilerOptionsCache.set(configPath, compilerOptions);
	return compilerOptions;
};
const resolveConfigImportPath = (sourceFilePath, moduleSpecifier) => {
	if (hasImportQuery(moduleSpecifier)) return null;
	const compilerOptions = getCompilerOptionsForSourceFile(sourceFilePath);
	if (!compilerOptions) return null;
	const resolvedModule = ts.resolveModuleName(moduleSpecifier, sourceFilePath, compilerOptions, ts.sys).resolvedModule;
	if (!resolvedModule) return null;
	return isExistingFile(resolvedModule.resolvedFileName) ? resolvedModule.resolvedFileName : null;
};
const resolveImportPath = (sourceFilePath, moduleSpecifier) => moduleSpecifier.startsWith(".") ? resolveRelativeImportPath(sourceFilePath, moduleSpecifier) : resolveConfigImportPath(sourceFilePath, moduleSpecifier);
const getImportedSourceFile = (filePath) => {
	const cached = importedSourceFileCache.get(filePath);
	if (cached) return cached;
	if (!fs.existsSync(filePath)) return null;
	const sourceFile = importedSourceProject.createSourceFile(filePath, fs.readFileSync(filePath, "utf-8"), { overwrite: true });
	importedSourceFileCache.set(filePath, sourceFile);
	return sourceFile;
};
const extractImportedIdentifierValues = (node, visited) => {
	if (!Node.isIdentifier(node)) return [];
	const sourceFile = node.getSourceFile();
	const sourceFilePath = sourceFile.getFilePath();
	const importMatch = sourceFile.getImportDeclarations().find((importDecl) => importDecl.getNamedImports().some((namedImport) => (namedImport.getAliasNode()?.getText() ?? namedImport.getName()) === node.getText()));
	if (!importMatch) return [];
	const namedImport = importMatch.getNamedImports().find((candidate) => (candidate.getAliasNode()?.getText() ?? candidate.getName()) === node.getText());
	if (!namedImport) return [];
	const resolvedPath = resolveImportPath(sourceFilePath, importMatch.getModuleSpecifierValue());
	if (!resolvedPath) return [];
	const importedSourceFile = getImportedSourceFile(resolvedPath);
	if (!importedSourceFile) return [];
	const initializer = importedSourceFile.getVariableDeclaration(namedImport.getName())?.getInitializer();
	return initializer ? extractStringLiterals(initializer, visited) : [];
};
/**
* Extracts string literal values from an expression.
*
* Handles:
* - Direct string literals: 'value'
* - Ternary expressions: condition ? 'a' : 'b' -> ['a', 'b']
* - Variable references: traces back to initializer
* - Logical expressions: a || 'fallback', a ?? 'default'
* - Function calls: traces to return statements
* - Template literals: `value`
* - As expressions: value as Type
*
* @param node The expression node to extract values from
* @param visited Set of visited nodes to prevent infinite recursion
* @returns Array of extracted string literal values
*/
const extractStringLiterals = (node, visited = /* @__PURE__ */ new Set()) => {
	if (visited.has(node)) return [];
	visited.add(node);
	const directValues = extractDirectLiteralValues(node);
	if (directValues) return directValues;
	const templateValues = extractTemplateExpressionValues(node, visited);
	if (templateValues) return templateValues;
	if (Node.isConditionalExpression(node)) return [...extractStringLiterals(node.getWhenTrue(), visited), ...extractStringLiterals(node.getWhenFalse(), visited)];
	const binaryValues = extractBinaryExpressionValues(node, visited);
	if (binaryValues) return binaryValues;
	const wrappedExpressionValues = extractWrappedExpressionValues(node, visited);
	if (wrappedExpressionValues) return wrappedExpressionValues;
	const propertyAccessValues = extractPropertyAccessValues(node, visited);
	if (propertyAccessValues) return propertyAccessValues;
	const elementAccessValues = extractElementAccessValues(node, visited);
	if (elementAccessValues) return elementAccessValues;
	const callExpressionValues = extractCallExpressionValues(node, visited);
	if (callExpressionValues) return callExpressionValues;
	const identifierValues = extractIdentifierValues(node, visited);
	if (identifierValues) return identifierValues;
	return [];
};
const extractDirectLiteralValues = (node) => {
	if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) return [node.getLiteralValue()];
	return null;
};
const extractTemplateExpressionValues = (node, visited) => {
	if (!Node.isTemplateExpression(node)) return null;
	const parts = [[node.getHead().getLiteralText()]];
	node.getTemplateSpans().forEach((span) => {
		const exprValues = extractStringLiterals(span.getExpression(), visited);
		const literalText = span.getLiteral().getLiteralText();
		parts.push(exprValues.length > 0 ? exprValues.map((value) => `${value}${literalText}`) : ["", literalText]);
	});
	return parts.reduce((acc, segment) => acc.flatMap((base) => segment.map((frag) => `${base}${frag}`)), [""]).filter((value) => value.length > 0);
};
const extractBinaryExpressionValues = (node, visited) => {
	if (!Node.isBinaryExpression(node)) return null;
	const operator = node.getOperatorToken().getText();
	if (operator === "+") {
		const left = extractStringLiterals(node.getLeft(), visited);
		const right = extractStringLiterals(node.getRight(), visited);
		const combined = left.flatMap((l) => right.map((r) => `${l}${r}`));
		return combined.length > 0 ? combined : [];
	}
	if (operator === "||" || operator === "??") return [...extractStringLiterals(node.getLeft(), visited), ...extractStringLiterals(node.getRight(), visited)];
	return [];
};
const extractWrappedExpressionValues = (node, visited) => {
	if (Node.isParenthesizedExpression(node) || Node.isAsExpression(node)) return extractStringLiterals(node.getExpression(), visited);
	return null;
};
const extractPropertyAccessValues = (node, visited) => {
	if (!Node.isPropertyAccessExpression(node)) return null;
	const typeValues = extractLiteralValuesFromType(node);
	if (typeValues.length > 0) return typeValues;
	const propertyName = node.getName();
	return extractObjectValues(node.getExpression(), visited).filter((value) => typeof value === "object" && value !== null && propertyName in value).map((value) => value[propertyName]).filter((value) => typeof value === "string");
};
const extractElementAccessValues = (node, visited) => {
	if (!Node.isElementAccessExpression(node)) return null;
	const typeValues = extractLiteralValuesFromType(node);
	if (typeValues.length > 0) return typeValues;
	return extractObjectValues(node.getExpression(), visited).flatMap((value) => {
		if (typeof value === "string") return [value];
		if (typeof value === "object" && value !== null) return Object.values(value).filter((entry) => typeof entry === "string");
		return [];
	});
};
const extractCallExpressionValues = (node, visited) => {
	if (!Node.isCallExpression(node)) return null;
	const expression = node.getExpression();
	const fromDefinitions = Node.isIdentifier(expression) ? getDefinitionNodesSafe(expression).flatMap((definition) => {
		if (Node.isFunctionDeclaration(definition)) return definition.getDescendantsOfKind(SyntaxKind.ReturnStatement).flatMap((returnStmt) => {
			const expression = returnStmt.getExpression();
			return expression ? extractStringLiterals(expression, visited) : [];
		});
		if (Node.isVariableDeclaration(definition)) {
			const initializer = definition.getInitializer();
			return initializer ? extractFromFunctionLike(initializer, visited) : [];
		}
		return [];
	}) : [];
	const fromArguments = node.getArguments().flatMap((arg) => extractStringLiterals(arg, visited));
	return [...fromDefinitions, ...fromArguments];
};
const extractArrayElementValues = (node, visited) => {
	if (Node.isArrayLiteralExpression(node)) return node.getElements().flatMap((element) => extractStringLiterals(element, visited)).filter((value, index, values) => values.indexOf(value) === index);
	if (Node.isParenthesizedExpression(node)) return extractArrayElementValues(node.getExpression(), visited);
	if (Node.isAsExpression(node) || Node.isTypeAssertion(node) || Node.isSatisfiesExpression(node)) return extractArrayElementValues(node.getExpression(), visited);
	if (Node.isIdentifier(node)) return getLocalDefinitionNodes(node).flatMap((definition) => {
		if (Node.isVariableDeclaration(definition)) {
			const initializer = definition.getInitializer();
			return initializer ? extractArrayElementValues(initializer, visited) : [];
		}
		return [];
	}).filter((value, index, values) => values.indexOf(value) === index);
	return [];
};
const extractMappedParameterValues = (definition, visited) => {
	if (!Node.isParameterDeclaration(definition)) return [];
	const callback = definition.getParentIfKind(SyntaxKind.ArrowFunction);
	if (!callback) return [];
	const callExpression = callback.getParentIfKind(SyntaxKind.CallExpression);
	if (!callExpression) return [];
	const expression = callExpression.getExpression();
	if (!Node.isPropertyAccessExpression(expression)) return [];
	if (!["map", "flatMap"].includes(expression.getName())) return [];
	if (callback.getParameters().findIndex((parameter) => parameter.getNameNode().getText() === definition.getNameNode().getText()) !== 0) return [];
	return extractArrayElementValues(expression.getExpression(), visited);
};
const extractIdentifierValues = (node, visited) => {
	if (!Node.isIdentifier(node)) return null;
	const values = getDefinitionNodesSafe(node).flatMap((definition) => {
		const mappedParameterValues = extractMappedParameterValues(definition, visited);
		if (mappedParameterValues.length > 0) return mappedParameterValues;
		if (Node.isVariableDeclaration(definition) || Node.isParameterDeclaration(definition) || Node.isBindingElement(definition)) {
			const initializer = definition.getInitializer();
			if (initializer) return extractStringLiterals(initializer, visited);
			if (Node.isBindingElement(definition)) {
				const bindingPattern = definition.getParent();
				if (Node.isObjectBindingPattern(bindingPattern)) {
					const parentDecl = bindingPattern.getParent();
					if (Node.isVariableDeclaration(parentDecl)) {
						const parentInit = parentDecl.getInitializer();
						if (parentInit) {
							const propName = definition.getNameNode().getText();
							const propValues = extractObjectValues(parentInit, new Set(visited)).filter((obj) => typeof obj === "object" && obj !== null && propName in obj).map((obj) => obj[propName]).filter((v) => typeof v === "string");
							if (propValues.length > 0) return propValues;
						}
					}
				}
			}
			return [];
		}
		return [];
	});
	if (values.length > 0) return values;
	const importedValues = extractImportedIdentifierValues(node, visited);
	if (importedValues.length > 0) return importedValues;
	return extractLiteralValuesFromType(node);
};
/**
* Extract string literals from arrow functions or function expressions
*/
const extractFromFunctionLike = (node, visited) => {
	const values = [];
	if (Node.isArrowFunction(node)) {
		const body = node.getBody();
		if (Node.isBlock(body)) body.getDescendantsOfKind(SyntaxKind.ReturnStatement).forEach((returnStmt) => {
			const returnExpr = returnStmt.getExpression();
			if (returnExpr) values.push(...extractStringLiterals(returnExpr, visited));
		});
		else values.push(...extractStringLiterals(body, visited));
	} else if (Node.isFunctionExpression(node)) node.getDescendantsOfKind(SyntaxKind.ReturnStatement).forEach((returnStmt) => {
		const returnExpr = returnStmt.getExpression();
		if (returnExpr) values.push(...extractStringLiterals(returnExpr, visited));
	});
	return values;
};
/**
* Extract object literal values from an expression.
* Returns an array of all possible object values (for union types or indexed access).
*/
const extractObjectValues = (node, visited) => {
	if (visited.has(node)) return [];
	visited.add(node);
	const values = [];
	if (Node.isObjectLiteralExpression(node)) {
		const obj = {};
		node.getProperties().forEach((prop) => {
			if (Node.isPropertyAssignment(prop)) {
				const name = prop.getName();
				const init = prop.getInitializer();
				if (init && Node.isStringLiteral(init)) obj[name] = init.getLiteralValue();
				else if (init && Node.isObjectLiteralExpression(init)) {
					const nestedValues = extractObjectValues(init, visited);
					if (nestedValues.length > 0) obj[name] = nestedValues[0];
				}
			}
		});
		values.push(obj);
		return values;
	}
	if (Node.isIdentifier(node)) {
		getDefinitionNodesSafe(node).forEach((definition) => {
			if (Node.isVariableDeclaration(definition)) {
				const initializer = definition.getInitializer();
				if (initializer) if (Node.isAsExpression(initializer)) {
					const inner = initializer.getExpression();
					values.push(...extractObjectValues(inner, visited));
				} else values.push(...extractObjectValues(initializer, visited));
			}
		});
		return values;
	}
	if (Node.isCallExpression(node)) {
		const expression = node.getExpression();
		if (Node.isIdentifier(expression)) getDefinitionNodesSafe(expression).forEach((definition) => {
			if (Node.isFunctionDeclaration(definition)) definition.getDescendantsOfKind(SyntaxKind.ReturnStatement).forEach((returnStmt) => {
				const returnExpr = returnStmt.getExpression();
				if (returnExpr) values.push(...extractObjectValues(returnExpr, visited));
			});
			if (Node.isVariableDeclaration(definition)) {
				const initializer = definition.getInitializer();
				if (initializer) {
					const extractFromFn = (fn) => {
						if (Node.isArrowFunction(fn)) {
							const body = fn.getBody();
							if (Node.isBlock(body)) body.getDescendantsOfKind(SyntaxKind.ReturnStatement).forEach((returnStmt) => {
								const returnExpr = returnStmt.getExpression();
								if (returnExpr) values.push(...extractObjectValues(returnExpr, visited));
							});
							else values.push(...extractObjectValues(body, visited));
						} else if (Node.isFunctionExpression(fn)) fn.getDescendantsOfKind(SyntaxKind.ReturnStatement).forEach((returnStmt) => {
							const returnExpr = returnStmt.getExpression();
							if (returnExpr) values.push(...extractObjectValues(returnExpr, visited));
						});
					};
					extractFromFn(initializer);
				}
			}
		});
		return values;
	}
	if (Node.isElementAccessExpression(node)) {
		extractObjectValues(node.getExpression(), visited).forEach((obj) => {
			if (typeof obj === "object" && obj !== null) Object.values(obj).forEach((value) => values.push(value));
		});
		return values;
	}
	return values;
};
/**
* Extract string literal values from a TypeScript type annotation AST node.
* Pure AST traversal — avoids expensive TS Language Service `getType()` calls.
*/
const extractStringLiteralsFromTypeNode = (typeNode) => {
	if (Node.isLiteralTypeNode(typeNode)) {
		const literal = typeNode.getLiteral();
		if (Node.isStringLiteral(literal)) return [literal.getLiteralValue()];
		return [];
	}
	if (Node.isUnionTypeNode(typeNode)) return typeNode.getTypeNodes().flatMap(extractStringLiteralsFromTypeNode);
	return [];
};
/**
* Extract literal values from a TypeScript type annotation (for union types like 'brand' | 'secondary').
* Uses pure AST traversal instead of the TS type checker to avoid expensive LS initialization.
*/
const extractLiteralValuesFromType = (node) => {
	if (Node.isIdentifier(node)) {
		const definitions = getLocalDefinitionNodes(node);
		for (const def of definitions) {
			if (Node.isParameterDeclaration(def) || Node.isVariableDeclaration(def) || Node.isPropertyDeclaration(def) || Node.isPropertySignature(def)) {
				const typeNode = def.getTypeNode?.();
				if (typeNode) {
					const values = extractStringLiteralsFromTypeNode(typeNode);
					if (values.length > 0) return values;
				}
			}
			if (Node.isBindingElement(def)) {
				const bindingPattern = def.getParent();
				if (bindingPattern) {
					const parentDecl = bindingPattern.getParent();
					if (parentDecl && Node.isVariableDeclaration(parentDecl)) {
						const parentType = parentDecl.getTypeNode();
						if (parentType && Node.isTypeLiteral(parentType)) {
							const propName = def.getNameNode().getText();
							for (const member of parentType.getMembers()) if (Node.isPropertySignature(member) && member.getName() === propName) {
								const memberType = member.getTypeNode();
								if (memberType) {
									const values = extractStringLiteralsFromTypeNode(memberType);
									if (values.length > 0) return values;
								}
							}
						}
					}
				}
			}
		}
	}
	if (Node.isPropertyAccessExpression(node) || Node.isElementAccessExpression(node)) return [];
	return [];
};
//#endregion
export { extractStringLiterals };
