import { logger } from '@pellux/goodvibes-sdk/platform/utils/logger';
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils/error-display';
/** Convert internal ToolDefinitions to OpenAI tools array. */
export function toOpenAITools(tools) {
    return tools.map((t) => ({
        type: 'function',
        function: {
            name: t.name,
            description: t.description,
            parameters: t.parameters,
        },
    }));
}
/** Parse OpenAI tool_calls from a response into internal ToolCall[]. */
export function fromOpenAIToolCalls(calls) {
    return calls.map((c) => ({
        id: c.id,
        name: c.function.name,
        arguments: parseJson(c.function.arguments),
    }));
}
/** Convert internal ProviderMessages to OpenAI message array. */
export function toOpenAIMessages(messages, systemPrompt) {
    const result = [];
    if (systemPrompt) {
        result.push({ role: 'system', content: systemPrompt });
    }
    for (const msg of messages) {
        if (msg.role === 'user') {
            if (Array.isArray(msg.content)) {
                // ContentPart[] — convert to OpenAI multimodal parts
                const parts = msg.content.map((part) => {
                    if (part.type === 'text')
                        return { type: 'text', text: part.text };
                    return { type: 'image_url', image_url: { url: `data:${part.mediaType};base64,${part.data}` } };
                });
                result.push({ role: 'user', content: parts });
            }
            else {
                result.push({ role: 'user', content: msg.content });
            }
        }
        else if (msg.role === 'assistant') {
            const m = { role: 'assistant', content: msg.content ?? '' };
            if (msg.toolCalls && msg.toolCalls.length > 0) {
                m.tool_calls = msg.toolCalls.map((tc) => ({
                    id: tc.id,
                    type: 'function',
                    function: { name: tc.name, arguments: JSON.stringify(tc.arguments) },
                }));
            }
            result.push(m);
        }
        else if (msg.role === 'tool') {
            result.push({
                role: 'tool',
                content: msg.content,
                tool_call_id: msg.callId,
            });
        }
    }
    return result;
}
/** Convert internal ToolDefinitions to Anthropic tools array. */
export function toAnthropicTools(tools) {
    return tools.map((t) => ({
        name: t.name,
        description: t.description,
        input_schema: t.parameters,
    }));
}
/** Parse Anthropic response content blocks into text + ToolCall[]. */
export function fromAnthropicContent(content) {
    let text = '';
    const toolCalls = [];
    for (const block of content) {
        if (block.type === 'text') {
            text += block.text;
        }
        else if (block.type === 'tool_use') {
            toolCalls.push({ id: block.id, name: block.name, arguments: block.input });
        }
    }
    return { text, toolCalls };
}
/** Convert internal ProviderMessages to Anthropic message array. */
export function toAnthropicMessages(messages) {
    const result = [];
    // Collect consecutive tool results to merge into one user message
    let pendingToolResults = [];
    function flushToolResults() {
        if (pendingToolResults.length > 0) {
            result.push({ role: 'user', content: pendingToolResults });
            pendingToolResults = [];
        }
    }
    for (const msg of messages) {
        if (msg.role === 'user') {
            flushToolResults();
            if (Array.isArray(msg.content)) {
                // ContentPart[] — convert to Anthropic content blocks
                const blocks = msg.content.map((part) => {
                    if (part.type === 'text')
                        return { type: 'text', text: part.text };
                    return {
                        type: 'image',
                        source: { type: 'base64', media_type: part.mediaType, data: part.data },
                    };
                });
                result.push({ role: 'user', content: blocks });
            }
            else {
                result.push({ role: 'user', content: msg.content });
            }
        }
        else if (msg.role === 'assistant') {
            flushToolResults();
            if (msg.toolCalls && msg.toolCalls.length > 0) {
                const blocks = [];
                if (msg.content)
                    blocks.push({ type: 'text', text: msg.content });
                for (const tc of msg.toolCalls) {
                    blocks.push({ type: 'tool_use', id: tc.id, name: tc.name, input: tc.arguments });
                }
                result.push({ role: 'assistant', content: blocks });
            }
            else {
                result.push({ role: 'assistant', content: msg.content });
            }
        }
        else if (msg.role === 'tool') {
            pendingToolResults.push({
                type: 'tool_result',
                tool_use_id: msg.callId,
                content: msg.content,
            });
        }
    }
    flushToolResults();
    return result;
}
/** Strip properties that Gemini's API doesn't support (e.g. additionalProperties). */
function stripUnsupportedProperties(obj) {
    if (Array.isArray(obj))
        return obj.map(stripUnsupportedProperties);
    if (obj !== null && typeof obj === 'object') {
        const result = {};
        for (const [key, value] of Object.entries(obj)) {
            if (key === 'additionalProperties')
                continue;
            result[key] = stripUnsupportedProperties(value);
        }
        return result;
    }
    return obj;
}
/** Convert internal ToolDefinitions to Gemini functionDeclarations. */
export function toGeminiFunctionDeclarations(tools) {
    return tools.map((t) => ({
        name: t.name,
        description: t.description,
        parameters: stripUnsupportedProperties(t.parameters),
    }));
}
/** Parse Gemini response parts into text + ToolCall[]. */
export function fromGeminiParts(parts) {
    let text = '';
    const toolCalls = [];
    for (const part of parts) {
        if (part.text) {
            text += part.text;
        }
        else if (part.functionCall) {
            toolCalls.push({
                id: crypto.randomUUID(),
                name: part.functionCall.name,
                arguments: part.functionCall.args,
            });
        }
    }
    return { text, toolCalls };
}
/** Convert internal ProviderMessages to Gemini contents array. */
export function toGeminiContents(messages, systemPrompt) {
    const contents = [];
    const systemInstruction = systemPrompt
        ? { parts: [{ text: systemPrompt }] }
        : undefined;
    let pendingFunctionResponses = [];
    function flushFunctionResponses() {
        if (pendingFunctionResponses.length > 0) {
            contents.push({ role: 'user', parts: pendingFunctionResponses });
            pendingFunctionResponses = [];
        }
    }
    for (const msg of messages) {
        if (msg.role === 'user') {
            flushFunctionResponses();
            if (Array.isArray(msg.content)) {
                // ContentPart[] — convert to Gemini parts
                const parts = msg.content.map((part) => {
                    if (part.type === 'text')
                        return { text: part.text };
                    return { inlineData: { mimeType: part.mediaType, data: part.data } };
                });
                contents.push({ role: 'user', parts });
            }
            else {
                contents.push({ role: 'user', parts: [{ text: msg.content }] });
            }
        }
        else if (msg.role === 'assistant') {
            flushFunctionResponses();
            const parts = [];
            if (msg.content)
                parts.push({ text: msg.content });
            if (msg.toolCalls) {
                for (const tc of msg.toolCalls) {
                    parts.push({ functionCall: { name: tc.name, args: tc.arguments } });
                }
            }
            contents.push({ role: 'model', parts });
        }
        else if (msg.role === 'tool') {
            pendingFunctionResponses.push({
                functionResponse: {
                    name: msg.name ?? msg.callId,
                    response: { content: msg.content },
                },
            });
        }
    }
    flushFunctionResponses();
    return { contents, systemInstruction };
}
// ---------------------------------------------------------------------------
// Text-based tool call extraction (for models that emit tool calls as tokens)
// ---------------------------------------------------------------------------
/**
 * Extracts tool calls from raw text content for models (e.g. kimi-k2-thinking
 * via ollama-cloud) that emit tool calls as text tokens instead of using the
 * OpenAI function-calling wire format.
 *
 * Supports two delimiter formats (both emitted by kimi variants):
 *   Without underscores: <|toolcallbegin|>functions.TOOLNAME:INDEX<|toolcallargumentbegin|>JSON_ARGS<|toolcallend|>
 *   With underscores:    <|tool_call_begin|>functions.TOOLNAME:INDEX<|tool_call_argument_begin|>JSON_ARGS<|tool_call_end|>
 *
 * A trailing <|tool_calls_section_end|> or <|toolcallssectionend|> is also
 * stripped from the cleaned content.
 *
 * @returns Extracted ToolCall array and the content with all tool-call tokens
 * removed (trimmed). Returns empty array when no text-based calls are found.
 */
export function extractTextToolCalls(content) {
    // Fast path: skip regex work when neither sentinel variant is present
    if (!content.includes('<|toolcallbegin|>') && !content.includes('<|tool_call_begin|>')) {
        return { toolCalls: [], cleanedContent: content };
    }
    // Match both delimiter variants using optional underscores.
    // The `.*?` lazy match with /s is safe here: backtracking is bounded by
    // the end delimiter, which appears within kilobytes of the argument-begin
    // delimiter — the span of a single tool call's JSON args.
    // kimi is the known model that emits these delimiter formats.
    const pattern = /<\|tool_?call_?begin\|>functions\.([^:]+):\d+<\|tool_?call_?argument_?begin\|>(.*?)<\|tool_?call_?end\|>/gs;
    const toolCalls = [];
    // Build cleaned content in a single pass by collecting match spans,
    // avoiding a second regex execution over the same string.
    const segments = [];
    let lastEnd = 0;
    let index = 0;
    for (const match of content.matchAll(pattern)) {
        const matchStart = match.index;
        const matchEnd = matchStart + match[0].length;
        // Collect the text before this match
        segments.push(content.slice(lastEnd, matchStart));
        lastEnd = matchEnd;
        const toolName = match[1];
        const rawArgs = match[2];
        toolCalls.push({
            id: `text-call-${index}`,
            name: toolName,
            arguments: parseJson(rawArgs),
        });
        index++;
    }
    // Append any trailing text after the last match
    segments.push(content.slice(lastEnd));
    // Strip section-end sentinels (both variants) and trim
    const cleanedContent = segments
        .join('')
        .replace(/<\|tool_?calls_?section_?end\|>/g, '')
        .trim();
    return { toolCalls, cleanedContent };
}
// ---------------------------------------------------------------------------
// Shared helper
// ---------------------------------------------------------------------------
function parseJson(raw) {
    try {
        return JSON.parse(raw);
    }
    catch (err) {
        logger.warn('tool-formats: failed to parse JSON tool arguments', { error: summarizeError(err) });
        return {};
    }
}
