/**
 * Anthropic multi-turn tool execution loop.
 *
 * Sends a prompt to the Anthropic Messages API with MCP tools attached.
 * When the model calls a tool, executes it via the MCP client, feeds
 * the result back, and continues until the model produces a final text
 * response or maxToolRounds is exhausted.
 */
// ---------------------------------------------------------------------------
// Tool loop implementation
// ---------------------------------------------------------------------------
/**
 * Run a multi-turn tool loop using the Anthropic Messages API.
 *
 * The loop:
 * 1. Sends the prompt with available tools to Claude
 * 2. If Claude calls tools → executes them via MCP, sends results back
 * 3. Repeats until Claude produces a text-only response or maxToolRounds is hit
 * 4. On the last round, omits tools to force a synthesis response
 */
export async function runAnthropicToolLoop(config) {
    const { prompt, tools, callTool, maxToolRounds, model, temperature, maxTokens, apiKey, } = config;
    const anthropicTools = tools.map((t) => ({
        name: t.name,
        description: t.description || `MCP tool: ${t.name}`,
        input_schema: t.inputSchema || { type: "object", properties: {} },
    }));
    const systemPrompt = "You are an AI assistant with access to tools provided by an MCP server. " +
        "Use the available tools to complete the task. Call tools with correct parameters, " +
        "interpret responses, and provide a complete answer.";
    const messages = [{ content: prompt, role: "user" }];
    let inputTokens = 0;
    let outputTokens = 0;
    const startTime = Date.now();
    const toolCallLog = [];
    for (let round = 0; round <= maxToolRounds; round++) {
        const isLastRound = round === maxToolRounds;
        // On the last round, omit tools to force a text-only response.
        // Anthropic doesn't support tool_choice: "none" — the way to disable
        // tools is to simply not include them in the request.
        if (isLastRound) {
            const lastMsg = messages[messages.length - 1];
            const synthesisText = "You've used the tools available. Based on the information gathered, " +
                "provide your complete, final answer now.";
            if (lastMsg?.role === "user" && Array.isArray(lastMsg.content)) {
                ;
                lastMsg.content.push({
                    type: "text",
                    text: synthesisText,
                });
            }
            else {
                messages.push({ content: synthesisText, role: "user" });
            }
        }
        const body = {
            max_tokens: maxTokens,
            messages,
            model,
            system: systemPrompt,
            temperature,
        };
        if (!isLastRound) {
            body.tools = anthropicTools;
        }
        const response = await fetch("https://api.anthropic.com/v1/messages", {
            body: JSON.stringify(body),
            headers: {
                "anthropic-version": "2023-06-01",
                "Content-Type": "application/json",
                "x-api-key": apiKey,
            },
            method: "POST",
        });
        const data = (await response.json());
        if (data.error) {
            throw new Error(data.error.message ??
                `Anthropic API error: ${JSON.stringify(data.error)}`);
        }
        inputTokens += data.usage?.input_tokens ?? 0;
        outputTokens += data.usage?.output_tokens ?? 0;
        if (!data.content?.length) {
            return {
                output: "",
                toolCallLog,
                tokenUsage: {
                    prompt: inputTokens,
                    completion: outputTokens,
                    total: inputTokens + outputTokens,
                },
                toolRounds: round,
                latencyMs: Date.now() - startTime,
            };
        }
        // Add assistant response to history
        messages.push({ content: data.content, role: "assistant" });
        // Check if model wants to use tools
        const toolUseBlocks = data.content.filter((b) => b.type === "tool_use");
        if (data.stop_reason !== "tool_use" || toolUseBlocks.length === 0) {
            // Model is done — extract text
            const textBlocks = data.content.filter((b) => b.type === "text");
            const output = textBlocks.map((b) => b.text || "").join("\n") || "";
            return {
                output,
                toolCallLog,
                tokenUsage: {
                    prompt: inputTokens,
                    completion: outputTokens,
                    total: inputTokens + outputTokens,
                },
                toolRounds: round,
                latencyMs: Date.now() - startTime,
            };
        }
        // Execute each tool call via MCP
        const toolResults = [];
        for (const toolUse of toolUseBlocks) {
            const toolName = toolUse.name;
            const toolInput = (toolUse.input || {});
            try {
                const result = await callTool(toolName, toolInput);
                const content = result.error
                    ? JSON.stringify({ error: result.error })
                    : result.content;
                toolCallLog.push({ name: toolName, input: toolInput, output: content });
                toolResults.push({
                    content,
                    tool_use_id: toolUse.id,
                    type: "tool_result",
                });
            }
            catch (err) {
                const errMsg = err instanceof Error ? err.message : String(err);
                toolCallLog.push({
                    name: toolName,
                    input: toolInput,
                    output: `Error: ${errMsg}`,
                });
                toolResults.push({
                    content: JSON.stringify({ error: errMsg }),
                    tool_use_id: toolUse.id,
                    type: "tool_result",
                });
            }
        }
        // Add tool results to conversation
        messages.push({ content: toolResults, role: "user" });
    }
    // Exhausted rounds — extract last text
    const lastAssistant = [...messages]
        .reverse()
        .find((m) => m.role === "assistant");
    let lastText = "";
    if (lastAssistant && Array.isArray(lastAssistant.content)) {
        lastText = lastAssistant.content
            .filter((b) => b.type === "text")
            .map((b) => b.text || "")
            .join("\n");
    }
    return {
        output: lastText || "[Exhausted tool rounds without final answer]",
        toolCallLog,
        tokenUsage: {
            prompt: inputTokens,
            completion: outputTokens,
            total: inputTokens + outputTokens,
        },
        toolRounds: maxToolRounds,
        exhaustedRounds: true,
        latencyMs: Date.now() - startTime,
    };
}
