import { A as isRich, j as theme, x as info } from "./registry-Sbac4a4z.js";
import { o as normalizeAgentId } from "./session-key-DUSb7CCb.js";
import { l as resolveDefaultAgentId } from "./agent-scope-DOrZjOnW.js";
import { i as loadConfig } from "./config-B4jkreCN.js";
import { t as isTruthyEnvValue } from "./env-BRnPI9sO.js";
import { n as formatErrorMessage } from "./errors-bI7Tdx75.js";
import { o as loadSessionStore } from "./sessions-Dxf5Kjig.js";
import { n as listChannelPlugins, t as getChannelPlugin } from "./plugins-CF5skkHh.js";
import { c as resolveStorePath } from "./paths-BtvQ7JvU.js";
import { n as callGateway, t as buildGatewayConnectionDetails } from "./call-AZ9vNhST.js";
import { r as resolvePreferredAccountId, t as buildChannelAccountBindings } from "./bindings-DIhsYGa_.js";
import { n as withProgress } from "./progress-swt2y17x.js";
import { t as resolveChannelDefaultAccountId } from "./helpers-G6zuddmF.js";

//#region src/terminal/health-style.ts
function styleHealthChannelLine(line, rich) {
	if (!rich) return line;
	const colon = line.indexOf(":");
	if (colon === -1) return line;
	const label = line.slice(0, colon + 1);
	const detail = line.slice(colon + 1).trimStart();
	const normalized = detail.toLowerCase();
	const applyPrefix = (prefix, color) => `${label} ${color(detail.slice(0, prefix.length))}${detail.slice(prefix.length)}`;
	if (normalized.startsWith("failed")) return applyPrefix("failed", theme.error);
	if (normalized.startsWith("ok")) return applyPrefix("ok", theme.success);
	if (normalized.startsWith("linked")) return applyPrefix("linked", theme.success);
	if (normalized.startsWith("configured")) return applyPrefix("configured", theme.success);
	if (normalized.startsWith("not linked")) return applyPrefix("not linked", theme.warn);
	if (normalized.startsWith("not configured")) return applyPrefix("not configured", theme.muted);
	if (normalized.startsWith("unknown")) return applyPrefix("unknown", theme.warn);
	return line;
}

//#endregion
//#region src/commands/health.ts
const DEFAULT_TIMEOUT_MS = 1e4;
const debugHealth = (...args) => {
	if (isTruthyEnvValue(process.env.SYMI_DEBUG_HEALTH)) console.warn("[health:debug]", ...args);
};
const resolveAgentOrder = (cfg) => {
	const defaultAgentId = resolveDefaultAgentId(cfg);
	const entries = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
	const seen = /* @__PURE__ */ new Set();
	const ordered = [];
	for (const entry of entries) {
		if (!entry || typeof entry !== "object") continue;
		if (typeof entry.id !== "string" || !entry.id.trim()) continue;
		const id = normalizeAgentId(entry.id);
		if (!id || seen.has(id)) continue;
		seen.add(id);
		ordered.push({
			id,
			name: typeof entry.name === "string" ? entry.name : void 0
		});
	}
	if (!seen.has(defaultAgentId)) ordered.unshift({ id: defaultAgentId });
	if (ordered.length === 0) ordered.push({ id: defaultAgentId });
	return {
		defaultAgentId,
		ordered
	};
};
const buildSessionSummary = (storePath) => {
	const store = loadSessionStore(storePath);
	const sessions = Object.entries(store).filter(([key]) => key !== "global" && key !== "unknown").map(([key, entry]) => ({
		key,
		updatedAt: entry?.updatedAt ?? 0
	})).toSorted((a, b) => b.updatedAt - a.updatedAt);
	const recent = sessions.slice(0, 5).map((s) => ({
		key: s.key,
		updatedAt: s.updatedAt || null,
		age: s.updatedAt ? Date.now() - s.updatedAt : null
	}));
	return {
		path: storePath,
		count: sessions.length,
		recent
	};
};
const isAccountEnabled = (account) => {
	if (!account || typeof account !== "object") return true;
	return account.enabled !== false;
};
const asRecord = (value) => value && typeof value === "object" ? value : null;
const formatProbeLine = (probe, opts = {}) => {
	const record = asRecord(probe);
	if (!record) return null;
	const ok = typeof record.ok === "boolean" ? record.ok : void 0;
	if (ok === void 0) return null;
	const elapsedMs = typeof record.elapsedMs === "number" ? record.elapsedMs : null;
	const status = typeof record.status === "number" ? record.status : null;
	const error = typeof record.error === "string" ? record.error : null;
	const bot = asRecord(record.bot);
	const botUsername = bot && typeof bot.username === "string" ? bot.username : null;
	const webhook = asRecord(record.webhook);
	const webhookUrl = webhook && typeof webhook.url === "string" ? webhook.url : null;
	const usernames = /* @__PURE__ */ new Set();
	if (botUsername) usernames.add(botUsername);
	for (const extra of opts.botUsernames ?? []) if (extra) usernames.add(extra);
	if (ok) {
		let label = "ok";
		if (usernames.size > 0) label += ` (@${Array.from(usernames).join(", @")})`;
		if (elapsedMs != null) label += ` (${elapsedMs}ms)`;
		if (webhookUrl) label += ` - webhook ${webhookUrl}`;
		return label;
	}
	let label = `failed (${status ?? "unknown"})`;
	if (error) label += ` - ${error}`;
	return label;
};
const formatAccountProbeTiming = (summary) => {
	const probe = asRecord(summary.probe);
	if (!probe) return null;
	const elapsedMs = typeof probe.elapsedMs === "number" ? Math.round(probe.elapsedMs) : null;
	const ok = typeof probe.ok === "boolean" ? probe.ok : null;
	if (elapsedMs == null && ok !== true) return null;
	const accountId = summary.accountId || "default";
	const botRecord = asRecord(probe.bot);
	const botUsername = botRecord && typeof botRecord.username === "string" ? botRecord.username : null;
	return `${botUsername ? `@${botUsername}` : accountId}:${accountId}:${elapsedMs != null ? `${elapsedMs}ms` : "ok"}`;
};
const isProbeFailure = (summary) => {
	const probe = asRecord(summary.probe);
	if (!probe) return false;
	return (typeof probe.ok === "boolean" ? probe.ok : null) === false;
};
const formatHealthChannelLines = (summary, opts = {}) => {
	const channels = summary.channels ?? {};
	const channelOrder = summary.channelOrder?.length > 0 ? summary.channelOrder : Object.keys(channels);
	const accountMode = opts.accountMode ?? "default";
	const lines = [];
	for (const channelId of channelOrder) {
		const channelSummary = channels[channelId];
		if (!channelSummary) continue;
		const plugin = getChannelPlugin(channelId);
		const label = summary.channelLabels?.[channelId] ?? plugin?.meta.label ?? channelId;
		const accountSummaries = channelSummary.accounts ?? {};
		const accountIds = opts.accountIdsByChannel?.[channelId];
		const filteredSummaries = accountIds && accountIds.length > 0 ? accountIds.map((accountId) => accountSummaries[accountId]).filter((entry) => Boolean(entry)) : void 0;
		const listSummaries = accountMode === "all" ? Object.values(accountSummaries) : filteredSummaries ?? (channelSummary.accounts ? Object.values(accountSummaries) : []);
		const baseSummary = filteredSummaries && filteredSummaries.length > 0 ? filteredSummaries[0] : channelSummary;
		const botUsernames = listSummaries ? listSummaries.map((account) => {
			const probeRecord = asRecord(account.probe);
			const bot = probeRecord ? asRecord(probeRecord.bot) : null;
			return bot && typeof bot.username === "string" ? bot.username : null;
		}).filter((value) => Boolean(value)) : [];
		const linked = typeof baseSummary.linked === "boolean" ? baseSummary.linked : null;
		if (linked !== null) {
			if (linked) {
				const authAgeMs = typeof baseSummary.authAgeMs === "number" ? baseSummary.authAgeMs : null;
				const authLabel = authAgeMs != null ? ` (auth age ${Math.round(authAgeMs / 6e4)}m)` : "";
				lines.push(`${label}: linked${authLabel}`);
			} else lines.push(`${label}: not linked`);
			continue;
		}
		const configured = typeof baseSummary.configured === "boolean" ? baseSummary.configured : null;
		if (configured === false) {
			lines.push(`${label}: not configured`);
			continue;
		}
		const accountTimings = accountMode === "all" ? listSummaries.map((account) => formatAccountProbeTiming(account)).filter((value) => Boolean(value)) : [];
		const failedSummary = listSummaries.find((summary) => isProbeFailure(summary));
		if (failedSummary) {
			const failureLine = formatProbeLine(failedSummary.probe, { botUsernames });
			if (failureLine) {
				lines.push(`${label}: ${failureLine}`);
				continue;
			}
		}
		if (accountTimings.length > 0) {
			lines.push(`${label}: ok (${accountTimings.join(", ")})`);
			continue;
		}
		const probeLine = formatProbeLine(baseSummary.probe, { botUsernames });
		if (probeLine) {
			lines.push(`${label}: ${probeLine}`);
			continue;
		}
		if (configured === true) {
			lines.push(`${label}: configured`);
			continue;
		}
		lines.push(`${label}: unknown`);
	}
	return lines;
};
async function getHealthSnapshot(params) {
	const timeoutMs = params?.timeoutMs;
	const cfg = loadConfig();
	const { defaultAgentId, ordered } = resolveAgentOrder(cfg);
	const channelBindings = buildChannelAccountBindings(cfg);
	const sessionCache = /* @__PURE__ */ new Map();
	const agents = ordered.map((entry) => {
		const storePath = resolveStorePath(cfg.session?.store, { agentId: entry.id });
		const sessions = sessionCache.get(storePath) ?? buildSessionSummary(storePath);
		sessionCache.set(storePath, sessions);
		return {
			agentId: entry.id,
			name: entry.name,
			isDefault: entry.id === defaultAgentId,
			sessions
		};
	});
	const sessions = (agents.find((agent) => agent.isDefault) ?? agents[0])?.sessions ?? buildSessionSummary(resolveStorePath(cfg.session?.store, { agentId: defaultAgentId }));
	const start = Date.now();
	const cappedTimeout = timeoutMs === void 0 ? DEFAULT_TIMEOUT_MS : Math.max(50, timeoutMs);
	const doProbe = params?.probe !== false;
	const channels = {};
	const channelOrder = listChannelPlugins().map((plugin) => plugin.id);
	const channelLabels = {};
	for (const plugin of listChannelPlugins()) {
		channelLabels[plugin.id] = plugin.meta.label ?? plugin.id;
		const accountIds = plugin.config.listAccountIds(cfg);
		const defaultAccountId = resolveChannelDefaultAccountId({
			plugin,
			cfg,
			accountIds
		});
		const boundAccounts = channelBindings.get(plugin.id)?.get(defaultAgentId) ?? [];
		const preferredAccountId = resolvePreferredAccountId({
			accountIds,
			defaultAccountId,
			boundAccounts
		});
		const boundAccountIdsAll = Array.from(new Set(Array.from(channelBindings.get(plugin.id)?.values() ?? []).flatMap((ids) => ids)));
		const accountIdsToProbe = Array.from(new Set([
			preferredAccountId,
			defaultAccountId,
			...accountIds,
			...boundAccountIdsAll
		].filter((value) => value && value.trim())));
		debugHealth("channel", {
			id: plugin.id,
			accountIds,
			defaultAccountId,
			boundAccounts,
			preferredAccountId,
			accountIdsToProbe
		});
		const accountSummaries = {};
		for (const accountId of accountIdsToProbe) {
			const account = plugin.config.resolveAccount(cfg, accountId);
			const enabled = plugin.config.isEnabled ? plugin.config.isEnabled(account, cfg) : isAccountEnabled(account);
			const configured = plugin.config.isConfigured ? await plugin.config.isConfigured(account, cfg) : true;
			let probe;
			let lastProbeAt = null;
			if (enabled && configured && doProbe && plugin.status?.probeAccount) try {
				probe = await plugin.status.probeAccount({
					account,
					timeoutMs: cappedTimeout,
					cfg
				});
				lastProbeAt = Date.now();
			} catch (err) {
				probe = {
					ok: false,
					error: formatErrorMessage(err)
				};
				lastProbeAt = Date.now();
			}
			const probeRecord = probe && typeof probe === "object" ? probe : null;
			const bot = probeRecord && typeof probeRecord.bot === "object" ? probeRecord.bot : null;
			if (bot?.username) debugHealth("probe.bot", {
				channel: plugin.id,
				accountId,
				username: bot.username
			});
			const snapshot = {
				accountId,
				enabled,
				configured
			};
			if (probe !== void 0) snapshot.probe = probe;
			if (lastProbeAt) snapshot.lastProbeAt = lastProbeAt;
			const summary = plugin.status?.buildChannelSummary ? await plugin.status.buildChannelSummary({
				account,
				cfg,
				defaultAccountId: accountId,
				snapshot
			}) : void 0;
			const record = summary && typeof summary === "object" ? summary : {
				accountId,
				configured,
				probe,
				lastProbeAt
			};
			if (record.configured === void 0) record.configured = configured;
			if (record.lastProbeAt === void 0 && lastProbeAt) record.lastProbeAt = lastProbeAt;
			record.accountId = accountId;
			accountSummaries[accountId] = record;
		}
		const fallbackSummary = accountSummaries[preferredAccountId] ?? accountSummaries[defaultAccountId] ?? accountSummaries[accountIdsToProbe[0] ?? preferredAccountId] ?? accountSummaries[Object.keys(accountSummaries)[0]];
		if (fallbackSummary) channels[plugin.id] = {
			...fallbackSummary,
			accounts: accountSummaries
		};
	}
	return {
		ok: true,
		ts: Date.now(),
		durationMs: Date.now() - start,
		channels,
		channelOrder,
		channelLabels,
		defaultAgentId,
		agents,
		sessions: {
			path: sessions.path,
			count: sessions.count,
			recent: sessions.recent
		}
	};
}
async function healthCommand(opts, runtime) {
	const cfg = opts.config ?? loadConfig();
	const summary = await withProgress({
		label: "Checking gateway health…",
		indeterminate: true,
		enabled: opts.json !== true
	}, async () => await callGateway({
		method: "health",
		params: opts.verbose ? { probe: true } : void 0,
		timeoutMs: opts.timeoutMs,
		config: cfg
	}));
	if (opts.json) runtime.log(JSON.stringify(summary, null, 2));
	else {
		const debugEnabled = isTruthyEnvValue(process.env.SYMI_DEBUG_HEALTH);
		const rich = isRich();
		if (opts.verbose) {
			const details = buildGatewayConnectionDetails({ config: cfg });
			runtime.log(info("Gateway connection:"));
			for (const line of details.message.split("\n")) runtime.log(`  ${line}`);
		}
		const localAgents = resolveAgentOrder(cfg);
		const defaultAgentId = summary.defaultAgentId ?? localAgents.defaultAgentId;
		const agents = Array.isArray(summary.agents) ? summary.agents : [];
		const fallbackAgents = localAgents.ordered.map((entry) => {
			const storePath = resolveStorePath(cfg.session?.store, { agentId: entry.id });
			return {
				agentId: entry.id,
				name: entry.name,
				isDefault: entry.id === localAgents.defaultAgentId,
				sessions: buildSessionSummary(storePath)
			};
		});
		const resolvedAgents = agents.length > 0 ? agents : fallbackAgents;
		const displayAgents = opts.verbose ? resolvedAgents : resolvedAgents.filter((agent) => agent.agentId === defaultAgentId);
		const channelBindings = buildChannelAccountBindings(cfg);
		if (debugEnabled) {
			runtime.log(info("[debug] local channel accounts"));
			for (const plugin of listChannelPlugins()) {
				const accountIds = plugin.config.listAccountIds(cfg);
				const defaultAccountId = resolveChannelDefaultAccountId({
					plugin,
					cfg,
					accountIds
				});
				runtime.log(`  ${plugin.id}: accounts=${accountIds.join(", ") || "(none)"} default=${defaultAccountId}`);
				for (const accountId of accountIds) {
					const account = plugin.config.resolveAccount(cfg, accountId);
					const record = asRecord(account);
					const tokenSource = record && typeof record.tokenSource === "string" ? record.tokenSource : void 0;
					const configured = plugin.config.isConfigured ? await plugin.config.isConfigured(account, cfg) : true;
					runtime.log(`    - ${accountId}: configured=${configured}${tokenSource ? ` tokenSource=${tokenSource}` : ""}`);
				}
			}
			runtime.log(info("[debug] bindings map"));
			for (const [channelId, byAgent] of channelBindings.entries()) {
				const entries = Array.from(byAgent.entries()).map(([agentId, ids]) => `${agentId}=[${ids.join(", ")}]`);
				runtime.log(`  ${channelId}: ${entries.join(" ")}`);
			}
			runtime.log(info("[debug] gateway channel probes"));
			for (const [channelId, channelSummary] of Object.entries(summary.channels ?? {})) {
				const accounts = channelSummary.accounts ?? {};
				const probes = Object.entries(accounts).map(([accountId, accountSummary]) => {
					const probe = asRecord(accountSummary.probe);
					const bot = probe ? asRecord(probe.bot) : null;
					return `${accountId}=${(bot && typeof bot.username === "string" ? bot.username : null) ?? "(no bot)"}`;
				});
				runtime.log(`  ${channelId}: ${probes.join(", ") || "(none)"}`);
			}
		}
		const channelAccountFallbacks = Object.fromEntries(listChannelPlugins().map((plugin) => {
			const accountIds = plugin.config.listAccountIds(cfg);
			const preferred = resolvePreferredAccountId({
				accountIds,
				defaultAccountId: resolveChannelDefaultAccountId({
					plugin,
					cfg,
					accountIds
				}),
				boundAccounts: channelBindings.get(plugin.id)?.get(defaultAgentId) ?? []
			});
			return [plugin.id, [preferred]];
		}));
		const accountIdsByChannel = (() => {
			const entries = displayAgents.length > 0 ? displayAgents : resolvedAgents;
			const byChannel = {};
			for (const [channelId, byAgent] of channelBindings.entries()) {
				const accountIds = [];
				for (const agent of entries) {
					const ids = byAgent.get(agent.agentId) ?? [];
					for (const id of ids) if (!accountIds.includes(id)) accountIds.push(id);
				}
				if (accountIds.length > 0) byChannel[channelId] = accountIds;
			}
			for (const [channelId, fallbackIds] of Object.entries(channelAccountFallbacks)) if (!byChannel[channelId] || byChannel[channelId].length === 0) byChannel[channelId] = fallbackIds;
			return byChannel;
		})();
		const channelLines = Object.keys(accountIdsByChannel).length > 0 ? formatHealthChannelLines(summary, {
			accountMode: opts.verbose ? "all" : "default",
			accountIdsByChannel
		}) : formatHealthChannelLines(summary, { accountMode: opts.verbose ? "all" : "default" });
		for (const line of channelLines) runtime.log(styleHealthChannelLine(line, rich));
		for (const plugin of listChannelPlugins()) {
			const channelSummary = summary.channels?.[plugin.id];
			if (!channelSummary || channelSummary.linked !== true) continue;
			if (!plugin.status?.logSelfId) continue;
			const boundAccounts = channelBindings.get(plugin.id)?.get(defaultAgentId) ?? [];
			const accountIds = plugin.config.listAccountIds(cfg);
			const accountId = resolvePreferredAccountId({
				accountIds,
				defaultAccountId: resolveChannelDefaultAccountId({
					plugin,
					cfg,
					accountIds
				}),
				boundAccounts
			});
			const account = plugin.config.resolveAccount(cfg, accountId);
			plugin.status.logSelfId({
				account,
				cfg,
				runtime,
				includeChannelPrefix: true
			});
		}
		if (resolvedAgents.length > 0) {
			const agentLabels = resolvedAgents.map((agent) => agent.isDefault ? `${agent.agentId} (default)` : agent.agentId);
			runtime.log(info(`Agents: ${agentLabels.join(", ")}`));
		}
		if (displayAgents.length === 0) {
			runtime.log(info(`Session store: ${summary.sessions.path} (${summary.sessions.count} entries)`));
			if (summary.sessions.recent.length > 0) for (const r of summary.sessions.recent) runtime.log(`- ${r.key} (${r.updatedAt ? `${Math.round((Date.now() - r.updatedAt) / 6e4)}m ago` : "no activity"})`);
		} else for (const agent of displayAgents) {
			runtime.log(info(`Session store (${agent.agentId}): ${agent.sessions.path} (${agent.sessions.count} entries)`));
			if (agent.sessions.recent.length > 0) for (const r of agent.sessions.recent) runtime.log(`- ${r.key} (${r.updatedAt ? `${Math.round((Date.now() - r.updatedAt) / 6e4)}m ago` : "no activity"})`);
		}
	}
}

//#endregion
export { styleHealthChannelLine as i, getHealthSnapshot as n, healthCommand as r, formatHealthChannelLines as t };