import { ModelRegistry } from "../../models/ModelRegistry.js";
import { logger } from "../../utils/logger.js";
import { handleMistralError } from "./Errors.js";
import { mapSystemMessages } from "../utils.js";
import { fetchWithTimeout } from "../../utils/fetch.js";
export class MistralChat {
    baseUrl;
    apiKey;
    constructor(baseUrl, apiKey) {
        this.baseUrl = baseUrl;
        this.apiKey = apiKey;
    }
    async execute(request) {
        const { model, messages, tools, max_tokens, response_format, thinking: _thinking, headers: _headers, requestTimeout, signal: _signal, ...rest } = request;
        const mappedMessages = mapSystemMessages(messages, false);
        // Sanitize messages - remove internal properties that Mistral API doesn't accept
        const sanitizedMessages = mappedMessages.map((msg) => {
            const m = msg;
            const { role, content, tool_calls, tool_call_id, name } = m;
            const clean = { role, content };
            if (tool_calls)
                clean.tool_calls = tool_calls;
            if (tool_call_id)
                clean.tool_call_id = tool_call_id;
            if (name)
                clean.name = name;
            return clean;
        });
        const body = {
            model,
            messages: sanitizedMessages,
            ...rest
        };
        if (max_tokens)
            body.max_tokens = max_tokens;
        if (tools && tools.length > 0)
            body.tools = tools;
        // Handle structured output for Mistral
        if (response_format) {
            if (response_format.type === "json_schema") {
                // Mistral supports JSON mode
                body.response_format = { type: "json_object" };
                // Append schema instructions to the system prompt
                const schema = response_format.json_schema;
                const schemaString = JSON.stringify(schema?.schema, null, 2);
                const instruction = `\n\nIMPORTANT: You must output strictly valid JSON conforming to the following schema:\n${schemaString}\n\nOutput only the JSON object.`;
                const messagesList = body.messages;
                const systemMessage = messagesList.find((m) => m.role === "system");
                if (systemMessage) {
                    systemMessage.content += instruction;
                }
                else {
                    messagesList.unshift({
                        role: "system",
                        content: "You are a helpful assistant." + instruction
                    });
                }
            }
            else {
                body.response_format = response_format;
            }
        }
        const url = `${this.baseUrl}/chat/completions`;
        logger.logRequest("Mistral", "POST", url, body);
        const response = await fetchWithTimeout(url, {
            method: "POST",
            headers: {
                Authorization: `Bearer ${this.apiKey}`,
                "Content-Type": "application/json",
                Accept: "application/json",
                ...request.headers
            },
            body: JSON.stringify(body)
        }, requestTimeout);
        if (!response.ok) {
            await handleMistralError(response, model);
        }
        const json = (await response.json());
        logger.logResponse("Mistral", response.status, response.statusText, json);
        const choice = json.choices?.[0];
        const message = choice?.message;
        // Parse content - magistral models return array with thinking and text parts
        let textContent = null;
        let reasoningText = null;
        if (Array.isArray(message?.content)) {
            // Magistral models: content is array of parts
            for (const part of message.content) {
                if (part.type === "thinking" && Array.isArray(part.thinking)) {
                    // thinking is an array of {type: "text", text: "..."} objects
                    for (const thinkingPart of part.thinking) {
                        if (thinkingPart.type === "text" && thinkingPart.text) {
                            reasoningText = (reasoningText || "") + thinkingPart.text;
                        }
                    }
                }
                else if (part.type === "text" && part.text) {
                    textContent = (textContent || "") + part.text;
                }
            }
        }
        else {
            textContent = message?.content ?? null;
        }
        // Map tool calls to standard format
        const toolCalls = message?.tool_calls?.map((tc) => ({
            id: tc.id,
            type: "function",
            function: {
                name: tc.function.name,
                arguments: tc.function.arguments
            }
        }));
        // Calculate cost if pricing available
        const pricing = ModelRegistry.find(model, "mistral")?.pricing;
        let cost;
        let inputCost;
        let outputCost;
        if (pricing?.text_tokens?.standard && json.usage) {
            const inputPrice = pricing.text_tokens.standard.input_per_million ?? 0;
            const outputPrice = pricing.text_tokens.standard.output_per_million ?? 0;
            inputCost = (json.usage.prompt_tokens / 1_000_000) * inputPrice;
            outputCost = (json.usage.completion_tokens / 1_000_000) * outputPrice;
            cost = inputCost + outputCost;
        }
        const usage = {
            input_tokens: json.usage?.prompt_tokens ?? 0,
            output_tokens: json.usage?.completion_tokens ?? 0,
            total_tokens: json.usage?.total_tokens ?? 0,
            cost,
            input_cost: inputCost,
            output_cost: outputCost
        };
        // Build thinking result for reasoning models
        const thinkingResult = reasoningText
            ? {
                text: reasoningText,
                tokens: undefined
            }
            : undefined;
        return {
            content: textContent,
            tool_calls: toolCalls,
            usage,
            finish_reason: choice?.finish_reason ?? null,
            thinking: thinkingResult,
            reasoning: reasoningText
        };
    }
}
