import { splitCommaSeparated } from "../utilities/template-helpers.js";
import { getPythonLanguage, initParser } from "../utilities/tree-sitter-wasm.js";
//#region src/lib/parsers/setup-py-parser.ts
/** Filter nulls from web-tree-sitter's `namedChildren` array. */
function children(node) {
	return node.namedChildren.filter((c) => c !== null);
}
function emptySetupPyData() {
	return {
		author: void 0,
		author_email: void 0,
		classifiers: [],
		description: void 0,
		download_url: void 0,
		extras_require: {},
		install_requires: [],
		keywords: void 0,
		license: void 0,
		long_description: void 0,
		maintainer: void 0,
		maintainer_email: void 0,
		name: void 0,
		project_urls: {},
		python_requires: void 0,
		url: void 0,
		version: void 0
	};
}
/** Extract a string literal value from a tree-sitter node. */
function extractString(node) {
	switch (node.type) {
		case "concatenated_string": {
			const parts = children(node).map((child) => extractString(child)).filter((s) => s !== void 0);
			return parts.length > 0 ? parts.join("") : void 0;
		}
		case "float":
		case "integer": return node.text;
		case "string": {
			const content = children(node).find((c) => c.type === "string_content");
			if (content) return content.text;
			const withoutPrefix = node.text.replace(/^[bfru]*/i, "");
			if (withoutPrefix.startsWith("\"\"\"") || withoutPrefix.startsWith("'''")) return withoutPrefix.slice(3, -3);
			return withoutPrefix.slice(1, -1);
		}
		case "string_content": return node.text;
		default: return;
	}
}
/** Extract a list of strings from an array/list literal node. */
function extractStringList(node) {
	if (node.type === "list") return children(node).map((child) => extractString(child)).filter((s) => s !== void 0);
	if (node.type === "tuple") return children(node).map((child) => extractString(child)).filter((s) => s !== void 0);
	const single = extractString(node);
	return single === void 0 ? [] : [single];
}
/** Extract a dict literal into a Record<string, string>. */
function extractStringDict(node) {
	const result = {};
	if (node.type !== "dictionary") return result;
	for (const pair of children(node)) {
		if (pair.type !== "pair") continue;
		const key = pair.childForFieldName("key");
		const value = pair.childForFieldName("value");
		if (!key || !value) continue;
		const k = extractString(key);
		const v = extractString(value);
		if (k && v) result[k] = v;
	}
	return result;
}
/** Extract a dict of string lists (for extras_require). */
function extractStringListDict(node) {
	const result = {};
	if (node.type !== "dictionary") return result;
	for (const pair of children(node)) {
		if (pair.type !== "pair") continue;
		const key = pair.childForFieldName("key");
		const value = pair.childForFieldName("value");
		if (!key || !value) continue;
		const k = extractString(key);
		if (k) result[k] = extractStringList(value);
	}
	return result;
}
/** Simple string attributes to extract from setup() keyword arguments. */
const STRING_ATTRS = new Set([
	"author",
	"author_email",
	"description",
	"download_url",
	"license",
	"long_description",
	"maintainer",
	"maintainer_email",
	"name",
	"python_requires",
	"url",
	"version"
]);
/**
* Parse a setup.py file and return structured metadata.
*
* Uses tree-sitter with the Python grammar to find the `setup()` call
* and extract keyword arguments. Only statically determinable values
* (string/list literals) are extracted — variables and dynamic expressions
* are skipped.
*/
async function parseSetupPy(source) {
	const parser = await initParser();
	const python = await getPythonLanguage();
	parser.setLanguage(python);
	const tree = parser.parse(source);
	if (!tree) throw new Error("Failed to parse setup.py source");
	const data = emptySetupPyData();
	const setupCall = findSetupCall(tree.rootNode);
	if (!setupCall) return data;
	const argumentChildren = setupCall.childForFieldName("arguments");
	if (!argumentChildren) return data;
	for (const child of children(argumentChildren)) {
		if (child.type !== "keyword_argument") continue;
		const nameNode = child.childForFieldName("name");
		const valueNode = child.childForFieldName("value");
		if (!nameNode || !valueNode) continue;
		const argumentName = nameNode.text;
		if (STRING_ATTRS.has(argumentName)) {
			const value = extractString(valueNode);
			if (value !== void 0) Object.assign(data, { [argumentName]: value });
			continue;
		}
		switch (argumentName) {
			case "classifiers":
				data.classifiers = extractStringList(valueNode);
				break;
			case "extras_require":
				data.extras_require = extractStringListDict(valueNode);
				break;
			case "install_requires":
				data.install_requires = extractStringList(valueNode);
				break;
			case "keywords":
				if (valueNode.type === "list" || valueNode.type === "tuple") data.keywords = extractStringList(valueNode);
				else {
					const string_ = extractString(valueNode);
					if (string_) data.keywords = splitCommaSeparated(string_);
				}
				break;
			case "project_urls":
				data.project_urls = extractStringDict(valueNode);
				break;
		}
	}
	return data;
}
/**
* Recursively find the setup() or setuptools.setup() call in the AST.
*/
function findSetupCall(node) {
	if (node.type === "call") {
		const function_ = node.childForFieldName("function");
		if (function_) {
			if (function_.type === "identifier" && function_.text === "setup") return node;
			if (function_.type === "attribute") {
				if (function_.childForFieldName("attribute")?.text === "setup") return node;
			}
		}
	}
	for (const child of children(node)) {
		const result = findSetupCall(child);
		if (result) return result;
	}
}
//#endregion
export { parseSetupPy };
