import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions.mjs'

/**
 * A tool call whose `arguments` string is not valid JSON (typically a stream
 * cut mid-arguments) must never reach a provider: the whole request is
 * rejected, and once persisted in the session history every follow-up request
 * fails the same way.
 *
 * Empty arguments count as valid: some providers stream '' for no-arg tool
 * calls, and flagging those would wrongly report the tool as not executed.
 * Callers persisting arguments must normalize '' to '{}' themselves.
 */
export function hasValidToolCallArguments(args: string | undefined): boolean {
	if (!args) {
		return true
	}
	try {
		JSON.parse(args)
		return true
	} catch {
		return false
	}
}

// '' is valid per hasValidToolCallArguments but JSON.parse('') still throws
// provider-side, so replaying history additionally requires non-empty args.
function isReplayableArguments(args: string | undefined): boolean {
	return !!args && hasValidToolCallArguments(args)
}

/**
 * Replaces unparseable or empty assistant tool_call arguments with '{}' in the
 * outgoing copy of the history, so a session whose persisted history contains
 * a truncated tool call recovers instead of failing every request. The paired
 * tool result already tells the model the call failed.
 */
export function sanitizeToolCallArguments(
	messages: ChatCompletionMessageParam[]
): ChatCompletionMessageParam[] {
	return messages.map((m) => {
		if (
			m.role !== 'assistant' ||
			!m.tool_calls?.some(
				(t) => t.type === 'function' && !isReplayableArguments(t.function.arguments)
			)
		) {
			return m
		}
		return {
			...m,
			tool_calls: m.tool_calls.map((t) =>
				t.type === 'function' && !isReplayableArguments(t.function.arguments)
					? { ...t, function: { ...t.function, arguments: '{}' } }
					: t
			)
		}
	})
}
