import { INTERRUPT } from "../constants.js";
import { filterToUserTags } from "./utils/config.js";
import { readChannels } from "./io.js";
import { findSubgraphPregel } from "./utils/subgraph.js";
import { EXCLUDED_METADATA_KEYS } from "@langchain/langgraph-checkpoint";
//#region src/pregel/debug.ts
const COLORS_MAP = {
	blue: {
		start: "\x1B[34m",
		end: "\x1B[0m"
	},
	green: {
		start: "\x1B[32m",
		end: "\x1B[0m"
	},
	yellow: {
		start: "\x1B[33;1m",
		end: "\x1B[0m"
	}
};
/**
* Wrap some text in a color for printing to the console.
*/
const wrap = (color, text) => `${color.start}${text}${color.end}`;
/**
* Build the user-meaningful metadata to forward on a task's stream payload.
*
* Drops langgraph's internal framework keys ({@link EXCLUDED_METADATA_KEYS}) —
* which are redundant with the task's own fields and namespace — while keeping
* keys like `lc_agent_name`, `ls_integration`, and any user-supplied metadata.
* Filtered config tags are folded in under `tags`, mirroring the messages
* stream handler. Returns `undefined` when there is nothing to forward.
*/
function buildTaskMetadata(config) {
	if (config == null) return void 0;
	const metadata = {};
	if (config.metadata != null) {
		for (const [key, value] of Object.entries(config.metadata)) if (!EXCLUDED_METADATA_KEYS.has(key)) metadata[key] = value;
	}
	const filteredTags = filterToUserTags(config.tags);
	if (filteredTags != null) metadata.tags = filteredTags;
	return Object.keys(metadata).length > 0 ? metadata : void 0;
}
function* mapDebugTasks(tasks) {
	for (const { id, name, input, config, triggers, writes } of tasks) {
		if (config?.tags?.includes("langsmith:hidden")) continue;
		const payload = {
			id,
			name,
			input,
			triggers,
			interrupts: writes.filter(([writeId, n]) => {
				return writeId === id && n === "__interrupt__";
			}).map(([, v]) => {
				return v;
			})
		};
		const metadata = buildTaskMetadata(config);
		if (metadata != null) payload.metadata = metadata;
		yield payload;
	}
}
function isMultipleChannelWrite(value) {
	if (typeof value !== "object" || value === null) return false;
	return "$writes" in value && Array.isArray(value.$writes);
}
function mapTaskResultWrites(writes) {
	const result = {};
	for (const [channel, value] of writes) {
		const strChannel = String(channel);
		if (strChannel in result) {
			const channelWrites = isMultipleChannelWrite(result[strChannel]) ? result[strChannel].$writes : [result[strChannel]];
			channelWrites.push(value);
			result[strChannel] = { $writes: channelWrites };
		} else result[strChannel] = value;
	}
	return result;
}
function* mapDebugTaskResults(tasks, streamChannels) {
	for (const [{ id, name, config }, writes] of tasks) {
		if (config?.tags?.includes("langsmith:hidden")) continue;
		yield {
			id,
			name,
			result: mapTaskResultWrites(writes.filter(([channel]) => {
				return Array.isArray(streamChannels) ? streamChannels.includes(channel) : channel === streamChannels;
			})),
			interrupts: writes.filter((w) => w[0] === INTERRUPT).map((w) => w[1])
		};
	}
}
function* mapDebugCheckpoint(config, channels, streamChannels, metadata, tasks, pendingWrites, parentConfig, outputKeys) {
	function formatConfig(config) {
		const pyConfig = {};
		if (config.callbacks != null) pyConfig.callbacks = config.callbacks;
		if (config.configurable != null) pyConfig.configurable = config.configurable;
		if (config.maxConcurrency != null) pyConfig.max_concurrency = config.maxConcurrency;
		if (config.metadata != null) pyConfig.metadata = config.metadata;
		if (config.recursionLimit != null) pyConfig.recursion_limit = config.recursionLimit;
		if (config.runId != null) pyConfig.run_id = config.runId;
		if (config.runName != null) pyConfig.run_name = config.runName;
		if (config.tags != null) pyConfig.tags = config.tags;
		return pyConfig;
	}
	const parentNs = config.configurable?.checkpoint_ns;
	const taskStates = {};
	for (const task of tasks) {
		if (!(task.subgraphs?.length ? task.subgraphs : [task.proc]).find(findSubgraphPregel)) continue;
		let taskNs = `${task.name}:${task.id}`;
		if (parentNs) taskNs = `${parentNs}|${taskNs}`;
		taskStates[task.id] = { configurable: {
			thread_id: config.configurable?.thread_id,
			checkpoint_ns: taskNs
		} };
	}
	yield {
		config: formatConfig(config),
		values: readChannels(channels, streamChannels),
		metadata,
		next: tasks.map((task) => task.name),
		tasks: tasksWithWrites(tasks, pendingWrites, taskStates, outputKeys),
		parentConfig: parentConfig ? formatConfig(parentConfig) : void 0
	};
}
function tasksWithWrites(tasks, pendingWrites, states, outputKeys) {
	return tasks.map((task) => {
		const error = pendingWrites.find(([id, n]) => id === task.id && n === "__error__")?.[2];
		const interrupts = pendingWrites.filter(([id, n]) => id === task.id && n === "__interrupt__").map(([, , v]) => v);
		const result = (() => {
			if (error || interrupts.length || !pendingWrites.length) return void 0;
			const idx = pendingWrites.findIndex(([tid, n]) => tid === task.id && n === "__return__");
			if (idx >= 0) return pendingWrites[idx][2];
			if (typeof outputKeys === "string") return pendingWrites.find(([tid, n]) => tid === task.id && n === outputKeys)?.[2];
			if (Array.isArray(outputKeys)) {
				const results = pendingWrites.filter(([tid, n]) => tid === task.id && outputKeys.includes(n)).map(([, n, v]) => [n, v]);
				if (!results.length) return void 0;
				return mapTaskResultWrites(results);
			}
		})();
		if (error) return {
			id: task.id,
			name: task.name,
			path: task.path,
			error,
			interrupts,
			result
		};
		const taskState = states?.[task.id];
		return {
			id: task.id,
			name: task.name,
			path: task.path,
			interrupts,
			...taskState !== void 0 ? { state: taskState } : {},
			result
		};
	});
}
function printStepCheckpoint(step, channels, whitelist) {
	console.log([
		`${wrap(COLORS_MAP.blue, `[${step}:checkpoint]`)}`,
		`\x1b[1m State at the end of step ${step}:\x1b[0m\n`,
		JSON.stringify(readChannels(channels, whitelist), null, 2)
	].join(""));
}
function printStepTasks(step, nextTasks) {
	const nTasks = nextTasks.length;
	console.log([
		`${wrap(COLORS_MAP.blue, `[${step}:tasks]`)}`,
		`\x1b[1m Starting step ${step} with ${nTasks} task${nTasks === 1 ? "" : "s"}:\x1b[0m\n`,
		nextTasks.map((task) => `- ${wrap(COLORS_MAP.green, String(task.name))} -> ${JSON.stringify(task.input, null, 2)}`).join("\n")
	].join(""));
}
function printStepWrites(step, writes, whitelist) {
	const byChannel = {};
	for (const [channel, value] of writes) if (whitelist.includes(channel)) {
		if (!byChannel[channel]) byChannel[channel] = [];
		byChannel[channel].push(value);
	}
	console.log([
		`${wrap(COLORS_MAP.blue, `[${step}:writes]`)}`,
		`\x1b[1m Finished step ${step} with writes to ${Object.keys(byChannel).length} channel${Object.keys(byChannel).length !== 1 ? "s" : ""}:\x1b[0m\n`,
		Object.entries(byChannel).map(([name, vals]) => `- ${wrap(COLORS_MAP.yellow, name)} -> ${vals.map((v) => JSON.stringify(v)).join(", ")}`).join("\n")
	].join(""));
}
//#endregion
export { mapDebugCheckpoint, mapDebugTaskResults, mapDebugTasks, printStepCheckpoint, printStepTasks, printStepWrites, tasksWithWrites };

//# sourceMappingURL=debug.js.map