/**
 * Shared utilities for stream processing and HITL suspend/resume.
 * Eliminates duplication across agent tools and the service layer.
 */

import { isRecord } from '@n8n/utils/is-record';

/** Parsed suspension data from a `tool-call-suspended` chunk. */
export interface SuspensionInfo {
	toolCallId: string;
	requestId: string;
	toolName?: string;
	/** Run id of the (sub-)agent run that suspended, from the `tool-call-suspended` chunk. */
	runId?: string;
	/** The raw suspend payload as passed to `ctx.suspend()` by the inner tool. */
	suspendPayload: Record<string, unknown>;
}

/** Extract suspension info from a stream chunk. */
export function parseSuspension(chunk: unknown): SuspensionInfo | null {
	if (!isRecord(chunk) || chunk.type !== 'tool-call-suspended') return null;

	const sp = isRecord(chunk.payload) ? chunk.payload : chunk;
	const suspPayload = isRecord(sp.suspendPayload) ? sp.suspendPayload : {};
	const tcId = typeof sp.toolCallId === 'string' ? sp.toolCallId : '';
	const reqId =
		typeof suspPayload.requestId === 'string' && suspPayload.requestId
			? suspPayload.requestId
			: tcId;
	const toolName = typeof sp.toolName === 'string' ? sp.toolName : undefined;
	const runId = typeof sp.runId === 'string' ? sp.runId : undefined;

	if (!reqId || !tcId) return null;
	return { toolCallId: tcId, requestId: reqId, toolName, runId, suspendPayload: suspPayload };
}

export interface Resumable {
	resume?: (
		method: 'stream',
		data: Record<string, unknown>,
		options: Record<string, unknown>,
	) => Promise<unknown>;
}

/** Cast an agent to Resumable for suspend/resume operations. */
export function asResumable(agent: unknown): Resumable {
	return agent as Resumable;
}

export async function resumeAgentStream(
	agent: unknown,
	data: Record<string, unknown>,
	options: Record<string, unknown>,
): Promise<unknown> {
	if (!isRecord(agent)) {
		throw new Error('Agent does not support stream resume');
	}

	const resumable = asResumable(agent);

	if (typeof resumable.resume === 'function') {
		return await resumable.resume('stream', data, options);
	}

	throw new Error('Agent does not support stream resume');
}
