import { regEx } from "../../../util/regex.js";
import { logger } from "../../../logger/index.js";
import { getSiblingFileName, localPathExists } from "../../../util/fs/index.js";
import { isValid as isVersion } from "../../versioning/semver/index.js";
import { NugetDatasource } from "../../datasource/nuget/index.js";
import { getDep } from "../dockerfile/extract.js";
import { applyRegistries, findVersion, getConfiguredRegistries, isXmlElement } from "./util.js";
import { extractMsbuildGlobalManifest } from "./extract/global-manifest.js";
import { extractPackagesFromSingleCsharpFile } from "./extract/single-csharp-file.js";
import { isNonEmptyStringAndNotWhitespace, isString } from "@sindresorhus/is";
import { XmlDocument } from "xmldoc";
//#region lib/modules/manager/nuget/extract.ts
/**
* https://learn.microsoft.com/nuget/concepts/package-versioning
* This article mentions that  Nuget 3.x and later tries to restore the lowest possible version
* regarding to given version range.
* 1.3.4 equals [1.3.4,)
*/
const elemNames = /* @__PURE__ */ new Set([
	"PackageReference",
	"PackageVersion",
	"DotNetCliToolReference",
	"GlobalPackageReference"
]);
function extractDepsFromXml(xmlNode) {
	const results = [];
	const vars = /* @__PURE__ */ new Map();
	const todo = [xmlNode];
	while (todo.length) {
		const child = todo.pop();
		const { name, attr } = child;
		if (name === "ContainerBaseImage") {
			const { depName, ...dep } = getDep(child.val, true);
			if (isNonEmptyStringAndNotWhitespace(depName)) results.push({
				...dep,
				depName,
				depType: "docker"
			});
		} else if (elemNames.has(name)) {
			const depName = attr?.Include || attr?.Update;
			if (!depName) continue;
			const dep = {
				datasource: NugetDatasource.id,
				depType: "nuget",
				depName
			};
			let currentValue = attr?.Version ?? attr?.version ?? child.valueWithPath("Version") ?? attr?.VersionOverride ?? child.valueWithPath("VersionOverride");
			if (!isNonEmptyStringAndNotWhitespace(currentValue)) dep.skipReason = "invalid-version";
			let sharedVariableName;
			currentValue = currentValue?.trim()?.replace(/^\$\((\w+)\)$/, (match, key) => {
				sharedVariableName = key;
				const val = vars.get(key);
				if (val) return val;
				return match;
			});
			if (sharedVariableName) if (currentValue === `$(${sharedVariableName})`) dep.skipReason = "contains-variable";
			else dep.sharedVariableName = sharedVariableName;
			dep.currentValue = currentValue;
			results.push(dep);
		} else if (name === "Sdk") {
			const depName = attr?.Name;
			const version = attr?.Version;
			if (depName && version) results.push({
				depName,
				currentValue: version,
				depType: "msbuild-sdk",
				datasource: NugetDatasource.id
			});
		} else if (name === "Import") {
			const depName = attr?.Sdk;
			const version = attr?.Version;
			if (depName && version) results.push({
				depName,
				currentValue: version,
				depType: "msbuild-sdk",
				datasource: NugetDatasource.id
			});
		} else {
			if (name === "Project") {
				if (attr?.Sdk) {
					const [name, version] = (attr?.Sdk).split("/");
					if (name && version) results.push({
						depName: name,
						depType: "msbuild-sdk",
						currentValue: version,
						datasource: NugetDatasource.id
					});
				}
				const propertyGroup = child.childNamed("PropertyGroup");
				if (propertyGroup) {
					for (const propChild of propertyGroup.children) if (isXmlElement(propChild)) {
						const { name, val } = propChild;
						if (!["Version", "TargetFramework"].includes(name)) vars.set(name, val);
					}
				}
			}
			todo.push(...child.children.filter(isXmlElement));
		}
	}
	return results;
}
async function extractPackageFile(content, packageFile, _config) {
	logger.trace(`nuget.extractPackageFile(${packageFile})`);
	const registries = await getConfiguredRegistries(packageFile);
	if (packageFile.endsWith("dotnet-tools.json")) {
		const deps = [];
		let manifest;
		try {
			manifest = JSON.parse(content);
		} catch {
			logger.debug({ packageFile }, `Invalid JSON`);
			return null;
		}
		if (manifest.version !== 1) {
			logger.debug({ packageFile }, "Unsupported dotnet tools version");
			return null;
		}
		for (const depName of Object.keys(manifest.tools ?? {})) {
			const currentValue = manifest.tools[depName].version;
			const dep = {
				depType: "nuget",
				depName,
				currentValue,
				datasource: NugetDatasource.id
			};
			if (isString(currentValue) && isVersion(currentValue)) dep.versioning = "semver";
			applyRegistries(dep, registries);
			deps.push(dep);
		}
		return deps.length ? { deps } : null;
	}
	if (packageFile.endsWith("global.json")) return extractMsbuildGlobalManifest(content, packageFile, registries);
	if (packageFile.endsWith(".cs")) return extractPackagesFromSingleCsharpFile(content, packageFile, registries);
	if (!regEx(/^\s*<.+>$/m).test(content.trim())) {
		logger.debug(`NuGet: Skipping ${packageFile} as it is not XML`);
		return null;
	}
	let deps = [];
	let packageFileVersion;
	try {
		const parsedXml = new XmlDocument(content);
		deps = extractDepsFromXml(parsedXml).map((dep) => applyRegistries(dep, registries));
		packageFileVersion = findVersion(parsedXml)?.val;
	} catch (err) {
		logger.debug({
			err,
			packageFile
		}, `Failed to parse XML`);
	}
	if (!deps.length) return null;
	const res = {
		deps,
		packageFileVersion
	};
	const lockFileName = getSiblingFileName(packageFile, "packages.lock.json");
	if (await localPathExists(lockFileName)) res.lockFiles = [lockFileName];
	return res;
}
//#endregion
export { extractPackageFile };

//# sourceMappingURL=extract.js.map