import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { r as formatErrorMessage } from "./errors-Z0lU1pH3.js";
//#region src/shared/node-match.ts
/** Normalizes human node names into stable lookup keys for fuzzy CLI/API matching. */
function normalizeNodeKey(value) {
	return normalizeLowercaseStringOrEmpty(value.normalize("NFC")).replace(/(?=\p{M})\p{Emoji_Component}/gu, "").replace(/(?<![\p{L}\p{M}\p{N}])\p{M}+/gu, "").replace(/[^\p{L}\p{M}\p{N}]+/gu, "-").replace(/^-+/, "").replace(/-+$/, "");
}
function compactNormalizedNodeKey(value) {
	return value.replace(/-/g, "");
}
function listKnownNodes(nodes) {
	return nodes.map((n) => n.displayName || n.remoteIp || n.nodeId).filter(Boolean).join(", ");
}
function formatNodeCandidateLabel(node) {
	const label = node.displayName || node.remoteIp || node.nodeId;
	const details = [`node=${node.nodeId}`];
	const clientId = normalizeOptionalString(node.clientId);
	if (clientId) details.push(`client=${clientId}`);
	return `${label} [${details.join(", ")}]`;
}
function isCurrentOpenClawClient(clientId) {
	return (normalizeOptionalLowercaseString(clientId) ?? "").startsWith("openclaw-");
}
function isLegacyClawdbotClient(clientId) {
	const normalized = normalizeOptionalLowercaseString(clientId) ?? "";
	return normalized.startsWith("clawdbot-") || normalized.startsWith("moldbot-");
}
function pickPreferredLegacyMigrationMatch(matches) {
	const current = matches.filter((match) => isCurrentOpenClawClient(match.clientId));
	if (current.length !== 1) return;
	const legacyCount = matches.filter((match) => isLegacyClawdbotClient(match.clientId)).length;
	if (legacyCount === 0 || current.length + legacyCount !== matches.length) return;
	return current[0];
}
function resolveMatchScore(node, query, queryNormalized, allowCompactDisplayName) {
	if (node.nodeId === query) return 4e3;
	if (typeof node.remoteIp === "string" && node.remoteIp === query) return 3e3;
	const name = typeof node.displayName === "string" ? node.displayName : "";
	const nameNormalized = name ? normalizeNodeKey(name) : "";
	if (nameNormalized && nameNormalized === queryNormalized) return 2e3;
	if (allowCompactDisplayName && nameNormalized && compactNormalizedNodeKey(nameNormalized) === compactNormalizedNodeKey(queryNormalized)) return 1900;
	if (query.length >= 6 && node.nodeId.startsWith(query)) return 1e3;
	return 0;
}
function scoreNodeCandidate(node, matchScore) {
	let score = matchScore;
	if (node.connected === true) score += 100;
	if (isCurrentOpenClawClient(node.clientId)) score += 10;
	else if (isLegacyClawdbotClient(node.clientId)) score -= 10;
	return score;
}
function resolveScoredMatches(nodes, query, allowCompactDisplayName) {
	const trimmed = normalizeOptionalString(query);
	if (!trimmed) return [];
	const normalized = normalizeNodeKey(trimmed);
	return nodes.map((node) => {
		const matchScore = resolveMatchScore(node, trimmed, normalized, allowCompactDisplayName);
		if (matchScore === 0) return null;
		return {
			node,
			matchScore,
			selectionScore: scoreNodeCandidate(node, matchScore)
		};
	}).filter((entry) => entry !== null);
}
/** Resolves a single node id or throws an operator-readable unknown/ambiguous-node error. */
function resolveNodeIdFromCandidates(nodes, query, allowCompactDisplayName = false) {
	const q = query.trim();
	if (!q) throw new Error("node required");
	const rawMatches = resolveScoredMatches(nodes, q, allowCompactDisplayName);
	if (rawMatches.length === 1) return rawMatches[0]?.node.nodeId ?? "";
	if (rawMatches.length === 0) {
		const known = listKnownNodes(nodes);
		throw new Error(`unknown node: ${q}${known ? ` (known: ${known})` : ""}`);
	}
	const topMatchScore = Math.max(...rawMatches.map((match) => match.matchScore));
	const strongestMatches = rawMatches.filter((match) => match.matchScore === topMatchScore);
	if (strongestMatches.length === 1) return strongestMatches[0]?.node.nodeId ?? "";
	const topSelectionScore = Math.max(...strongestMatches.map((match) => match.selectionScore));
	const matches = strongestMatches.filter((match) => match.selectionScore === topSelectionScore);
	if (matches.length === 1) return matches[0]?.node.nodeId ?? "";
	const preferred = pickPreferredLegacyMigrationMatch(matches.map((match) => match.node));
	if (preferred) return preferred.nodeId;
	throw new Error(`ambiguous node: ${q} (matches: ${matches.map((match) => formatNodeCandidateLabel(match.node)).join(", ")})`);
}
//#endregion
//#region src/shared/node-resolve.ts
/** Resolves a user query to a node id, optionally using a caller-defined blank-query default. */
function resolveNodeIdFromNodeList(nodes, query, options = {}) {
	const q = normalizeOptionalString(query) ?? "";
	if (!q) {
		if (options.allowDefault === true && options.pickDefaultNode) {
			const picked = options.pickDefaultNode(nodes);
			if (picked) return picked.nodeId;
		}
		throw new Error("node required");
	}
	return resolveNodeIdFromCandidates(nodes, q, options.allowCompactDisplayName);
}
/** Resolves a full node entry, preserving synthetic defaults returned by the picker. */
function resolveNodeFromNodeList(nodes, query, options = {}) {
	const nodeId = resolveNodeIdFromNodeList(nodes, query, options);
	return nodes.find((node) => node.nodeId === nodeId) ?? { nodeId };
}
function formatNodeIdList(nodes) {
	return nodes.length > 0 ? nodes.map((node) => node.nodeId).toSorted().join(", ") : "none";
}
/**
* Resolves a capability-gated node from the full node list. Exact ids are
* checked before eligible-name resolution so an ineligible id cannot redirect
* to an eligible node that shares its display name.
*/
function resolveEligibleNodeFromList(nodes, query, isEligible, messages) {
	const eligible = nodes.filter(isEligible);
	const trimmed = query?.trim();
	if (trimmed) {
		const eligibleIds = formatNodeIdList(eligible);
		const lowerTrimmed = trimmed.toLowerCase();
		const exactNode = nodes.find((node) => node.nodeId === trimmed) ?? nodes.find((node) => node.nodeId.toLowerCase() === lowerTrimmed);
		if (exactNode) {
			if (!isEligible(exactNode)) throw new Error(messages.ineligibleExact(trimmed, eligibleIds));
			return exactNode;
		}
		try {
			const nodeId = resolveNodeIdFromNodeList(eligible, trimmed);
			const match = eligible.find((node) => node.nodeId === nodeId);
			if (match) return match;
		} catch (error) {
			throw new Error(messages.nameResolveFailed(formatErrorMessage(error), eligibleIds), { cause: error });
		}
		throw new Error(`node not found: ${trimmed}`);
	}
	const only = eligible.length === 1 ? eligible.at(0) : void 0;
	if (only) return only;
	if (eligible.length === 0) throw new Error(messages.noneEligible());
	throw new Error(messages.multipleEligible(eligible));
}
//#endregion
export { resolveNodeFromNodeList as n, resolveNodeIdFromNodeList as r, resolveEligibleNodeFromList as t };
