//#region src/lib/parsers/go-mod-parser.ts
/**
* Known source-repo hosts and the number of path segments that make a repo URL.
* e.g. github.com/owner/repo → 3 segments.
*/
const HOST_SEGMENTS = {
	"bitbucket.com": 3,
	"bitbucket.org": 3,
	"codeberg.org": 3,
	"git.sr.ht": 3,
	"github.com": 3,
	"gitlab.com": 3
};
/** Derive a repository URL from a Go module path, if on a known host. */
function moduleToRepoUrl(modulePath) {
	const segments = modulePath.split("/");
	const host = segments[0];
	if (!host) return void 0;
	const needed = HOST_SEGMENTS[host];
	if (!needed || segments.length < needed) return void 0;
	let repoPath = segments.slice(0, needed).join("/");
	repoPath = repoPath.replace(/\/v\d+$/, "");
	return `https://${repoPath}`;
}
/** Strip inline comments and trim whitespace. */
function stripComment(line) {
	const index = line.indexOf("//");
	return index === -1 ? line.trim() : line.slice(0, index).trim();
}
/** Check whether a line has an `// indirect` comment. */
function isIndirect(line) {
	return /\/\/\s*indirect/.test(line);
}
/** Parse a require-style line: `module version [// indirect]` */
function parseRequireLine(line) {
	const indirect = isIndirect(line);
	const clean = stripComment(line);
	const match = /^(\S+)\s+(\S+)/.exec(clean);
	if (!match) return void 0;
	const version = match[2].replace(/\+incompatible$/, "");
	return {
		indirect,
		module: match[1],
		version
	};
}
/** Parse a replace-style line: `old [version] => new version` or `old [version] => ./local` */
function parseReplaceLine(line) {
	const parts = stripComment(line).split("=>");
	if (parts.length !== 2) return void 0;
	const left = parts[0].trim().split(/\s+/);
	const right = parts[1].trim().split(/\s+/);
	const from = left[0];
	if (!from || right.length === 0) return void 0;
	const target = right[0];
	if (!target) return void 0;
	if (target.startsWith("./") || target.startsWith("../") || target.startsWith("/")) return {
		from,
		to: "local"
	};
	return {
		from,
		to: {
			module: target,
			version: (right[1] ?? "").replace(/\+incompatible$/, "")
		}
	};
}
/** Parse a tool-style line: just a module path. */
function parseToolLine(line) {
	const clean = stripComment(line).trim();
	if (clean.length === 0) return void 0;
	return clean.split(/\s+/)[0] || void 0;
}
/**
* Parse a go.mod file and return structured metadata.
*
* Extracts module identity, Go version, direct dependencies (skipping
* indirect ones), tool dependencies, and applies replace directives.
*/
function parseGoMod(source) {
	const data = {
		dependencies: [],
		go_version: void 0,
		module: void 0,
		repository_url: void 0,
		tool_dependencies: []
	};
	const directDeps = {};
	const toolDeps = [];
	const replacements = /* @__PURE__ */ new Map();
	let blockState = "none";
	for (const rawLine of source.split("\n")) {
		const line = rawLine.trim();
		if (line === "" || line.startsWith("//") && blockState === "none") continue;
		if (line === ")" || line.startsWith(")")) {
			blockState = "none";
			continue;
		}
		if (blockState !== "none") {
			switch (blockState) {
				case "replace": {
					const rep = parseReplaceLine(line);
					if (rep) replacements.set(rep.from, rep.to);
					break;
				}
				case "require": {
					const dep = parseRequireLine(line);
					if (dep && !dep.indirect) directDeps[dep.module] = dep.version;
					break;
				}
				case "skip": break;
				case "tool": {
					const tool = parseToolLine(line);
					if (tool) toolDeps.push(tool);
					break;
				}
			}
			continue;
		}
		if (line.startsWith("module ")) data.module = line.slice(7).trim();
		else if (line.startsWith("go ")) data.go_version = line.slice(3).trim();
		else if (line.startsWith("require ")) if (line.includes("(")) blockState = "require";
		else {
			const dep = parseRequireLine(line.slice(8));
			if (dep && !dep.indirect) directDeps[dep.module] = dep.version;
		}
		else if (line.startsWith("replace ")) if (line.includes("(")) blockState = "replace";
		else {
			const rep = parseReplaceLine(line.slice(8));
			if (rep) replacements.set(rep.from, rep.to);
		}
		else if (line.startsWith("tool ")) if (line.includes("(")) blockState = "tool";
		else {
			const tool = parseToolLine(line.slice(5));
			if (tool) toolDeps.push(tool);
		}
		else if ((line.startsWith("exclude ") || line.startsWith("retract ") || line.startsWith("godebug ") || line.startsWith("toolchain ")) && line.includes("(")) blockState = "skip";
	}
	for (const [from, to] of replacements) if (from in directDeps) {
		delete directDeps[from];
		if (to !== "local") directDeps[to.module] = to.version;
	}
	data.dependencies = Object.entries(directDeps).map(([module, version]) => ({
		module,
		version
	}));
	data.tool_dependencies = toolDeps;
	if (data.module) data.repository_url = moduleToRepoUrl(data.module);
	return data;
}
//#endregion
export { parseGoMod };
