/*! © 2026 Yahoo, Inc. UDS Tailwind and Purger v0.0.0-development */
const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
let node_fs = require("node:fs");
node_fs = require_runtime.__toESM(node_fs);
let node_path = require("node:path");
node_path = require_runtime.__toESM(node_path);
let ts_morph = require("ts-morph");

//#region src/purger/optimized/ast/expressions.ts
const importedSourceProject = new ts_morph.Project({ useInMemoryFileSystem: true });
const importedSourceFileCache = /* @__PURE__ */ new Map();
const tsConfigPathCache = /* @__PURE__ */ new Map();
const compilerOptionsCache = /* @__PURE__ */ new Map();
const hasImportQuery = (moduleSpecifier) => moduleSpecifier.includes("?");
const isExistingFile = (candidate) => node_fs.default.existsSync(candidate) && node_fs.default.statSync(candidate).isFile();
const resolveRelativeImportPath = (sourceFilePath, moduleSpecifier) => {
	if (!node_path.default.isAbsolute(sourceFilePath) || !moduleSpecifier.startsWith(".") || hasImportQuery(moduleSpecifier)) return null;
	const basePath = node_path.default.resolve(node_path.default.dirname(sourceFilePath), moduleSpecifier);
	return [
		basePath,
		`${basePath}.ts`,
		`${basePath}.tsx`,
		`${basePath}.js`,
		`${basePath}.jsx`,
		node_path.default.join(basePath, "index.ts"),
		node_path.default.join(basePath, "index.tsx"),
		node_path.default.join(basePath, "index.js"),
		node_path.default.join(basePath, "index.jsx")
	].find((candidate) => isExistingFile(candidate)) ?? null;
};
const findNearestTypeScriptConfig = (sourceFilePath) => {
	const sourceDir = node_path.default.dirname(sourceFilePath);
	const cached = tsConfigPathCache.get(sourceDir);
	if (cached !== void 0) return cached;
	let currentDir = sourceDir;
	while (true) {
		const tsConfigPath = node_path.default.join(currentDir, "tsconfig.json");
		if (isExistingFile(tsConfigPath)) {
			tsConfigPathCache.set(sourceDir, tsConfigPath);
			return tsConfigPath;
		}
		const jsConfigPath = node_path.default.join(currentDir, "jsconfig.json");
		if (isExistingFile(jsConfigPath)) {
			tsConfigPathCache.set(sourceDir, jsConfigPath);
			return jsConfigPath;
		}
		const parentDir = node_path.default.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_morph.ts.readConfigFile(configPath, ts_morph.ts.sys.readFile);
	if (readResult.error) {
		compilerOptionsCache.set(configPath, null);
		return null;
	}
	const parsed = ts_morph.ts.parseJsonConfigFileContent(readResult.config, ts_morph.ts.sys, node_path.default.dirname(configPath));
	const compilerOptions = parsed.options.paths && !parsed.options.baseUrl ? {
		...parsed.options,
		baseUrl: node_path.default.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_morph.ts.resolveModuleName(moduleSpecifier, sourceFilePath, compilerOptions, ts_morph.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 (!node_fs.default.existsSync(filePath)) return null;
	const sourceFile = importedSourceProject.createSourceFile(filePath, node_fs.default.readFileSync(filePath, "utf-8"), { overwrite: true });
	importedSourceFileCache.set(filePath, sourceFile);
	return sourceFile;
};
const extractImportedIdentifierValues = (node, visited) => {
	if (!ts_morph.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 (ts_morph.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);
	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 (ts_morph.Node.isStringLiteral(node) || ts_morph.Node.isNoSubstitutionTemplateLiteral(node)) return [node.getLiteralValue()];
	return null;
};
const extractTemplateExpressionValues = (node, visited) => {
	if (!ts_morph.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 (!ts_morph.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 (ts_morph.Node.isParenthesizedExpression(node) || ts_morph.Node.isAsExpression(node)) return extractStringLiterals(node.getExpression(), visited);
	return null;
};
const extractPropertyAccessValues = (node, visited) => {
	if (!ts_morph.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) => {
	if (!ts_morph.Node.isElementAccessExpression(node)) return null;
	return extractLiteralValuesFromType(node);
};
const extractCallExpressionValues = (node, visited) => {
	if (!ts_morph.Node.isCallExpression(node)) return null;
	const expression = node.getExpression();
	const fromDefinitions = ts_morph.Node.isIdentifier(expression) ? expression.getDefinitionNodes().flatMap((definition) => {
		if (ts_morph.Node.isFunctionDeclaration(definition)) return definition.getDescendantsOfKind(ts_morph.SyntaxKind.ReturnStatement).flatMap((returnStmt) => {
			const expression = returnStmt.getExpression();
			return expression ? extractStringLiterals(expression, visited) : [];
		});
		if (ts_morph.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 extractIdentifierValues = (node, visited) => {
	if (!ts_morph.Node.isIdentifier(node)) return null;
	const values = node.getDefinitionNodes().flatMap((definition) => {
		if (ts_morph.Node.isVariableDeclaration(definition) || ts_morph.Node.isParameterDeclaration(definition) || ts_morph.Node.isBindingElement(definition)) {
			const initializer = definition.getInitializer();
			return initializer ? extractStringLiterals(initializer, visited) : [];
		}
		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 (ts_morph.Node.isArrowFunction(node)) {
		const body = node.getBody();
		if (ts_morph.Node.isBlock(body)) body.getDescendantsOfKind(ts_morph.SyntaxKind.ReturnStatement).forEach((returnStmt) => {
			const returnExpr = returnStmt.getExpression();
			if (returnExpr) values.push(...extractStringLiterals(returnExpr, visited));
		});
		else values.push(...extractStringLiterals(body, visited));
	} else if (ts_morph.Node.isFunctionExpression(node)) node.getDescendantsOfKind(ts_morph.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 (ts_morph.Node.isObjectLiteralExpression(node)) {
		const obj = {};
		node.getProperties().forEach((prop) => {
			if (ts_morph.Node.isPropertyAssignment(prop)) {
				const name = prop.getName();
				const init = prop.getInitializer();
				if (init && ts_morph.Node.isStringLiteral(init)) obj[name] = init.getLiteralValue();
				else if (init && ts_morph.Node.isObjectLiteralExpression(init)) {
					const nestedValues = extractObjectValues(init, visited);
					if (nestedValues.length > 0) obj[name] = nestedValues[0];
				}
			}
		});
		values.push(obj);
		return values;
	}
	if (ts_morph.Node.isIdentifier(node)) {
		node.getDefinitionNodes().forEach((definition) => {
			if (ts_morph.Node.isVariableDeclaration(definition)) {
				const initializer = definition.getInitializer();
				if (initializer) if (ts_morph.Node.isAsExpression(initializer)) {
					const inner = initializer.getExpression();
					values.push(...extractObjectValues(inner, visited));
				} else values.push(...extractObjectValues(initializer, visited));
			}
		});
		return values;
	}
	if (ts_morph.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 literal values from a TypeScript type (for union types like 'brand' | 'secondary')
*/
const extractLiteralValuesFromType = (node) => {
	const values = [];
	const nodeType = node.getType();
	if (nodeType.isUnion()) nodeType.getUnionTypes().forEach((unionMember) => {
		if (unionMember.isStringLiteral()) values.push(unionMember.getLiteralValue());
	});
	else if (nodeType.isStringLiteral()) values.push(nodeType.getLiteralValue());
	return values;
};

//#endregion
exports.extractStringLiterals = extractStringLiterals;