function flattenContent(content, depth = 0) {
    if (depth > 4 || content == null) {
        return '';
    }
    if (typeof content === 'string') {
        return content;
    }
    if (Array.isArray(content)) {
        return content.map((entry) => flattenContent(entry, depth + 1)).join('');
    }
    if (typeof content === 'object') {
        const record = content;
        if (typeof record.text === 'string') {
            return record.text;
        }
        if (record.content !== undefined) {
            return flattenContent(record.content, depth + 1);
        }
    }
    return '';
}
const GLM_CUSTOM_TAG = /<tool_call(?:\s+name="([^"]+)")?>([\s\S]*?)<\/tool_call>/gi;
const GLM_TAGGED_SEQUENCE = /<tool_call(?:\s+name="([^"]+)")?\s*>([\s\S]*?)(?:<\/tool_call>|(?=<tool_call)|$)/gi;
const GLM_TAGGED_BLOCK = /<arg_key>([\s\S]*?)<\/arg_key>\s*<arg_value>([\s\S]*?)<\/arg_value>/gi;
const GLM_INLINE_NAME = /^[\s\r\n]*([A-Za-z0-9_.:-]+)/;
const GENERIC_PATTERNS = [
    [
        /```(?:tool|function|tool_call|function_call)?\s*([\s\S]*?)\s*```/gi,
        (match) => ({ body: match[1] ?? '' })
    ],
    [
        /\[(tool_call|function_call)(?:\s+name="([^"]+)")?\]([\s\S]*?)\[\/\1\]/gi,
        (match) => ({ body: match[3] ?? '', nameHint: match[2] })
    ],
    [
        /(tool_call|function_call)\s*[:=]\s*({[\s\S]+?})/gi,
        (match) => ({ body: match[2] ?? '' })
    ]
];
export function extractGlmToolMarkup(root) {
    const choicesRaw = root?.choices;
    const choices = Array.isArray(choicesRaw) ? choicesRaw : [];
    choices.forEach((choice, index) => {
        if (!choice || typeof choice !== 'object') {
            return;
        }
        const message = choice.message;
        if (!message || typeof message !== 'object') {
            return;
        }
        const msgRecord = message;
        const contentField = msgRecord.content;
        const reasoningField = msgRecord.reasoning_content;
        const text = contentField !== undefined
            ? flattenContent(contentField)
            : typeof reasoningField === 'string'
                ? reasoningField
                : '';
        if (!text) {
            return;
        }
        const extraction = extractToolCallsFromText(text, index + 1);
        if (!extraction) {
            return;
        }
        if (extraction.toolCalls.length) {
            msgRecord.tool_calls = extraction.toolCalls;
            if (contentField !== undefined) {
                msgRecord.content = null;
            }
        }
        if (extraction.reasoningText) {
            msgRecord.reasoning_content = extraction.reasoningText;
        }
        else if ('reasoning_content' in msgRecord) {
            delete msgRecord.reasoning_content;
        }
    });
}
function extractToolCallsFromText(text, choiceIndex) {
    const matches = [];
    const applyPattern = (pattern, factory) => {
        pattern.lastIndex = 0;
        let exec;
        while ((exec = pattern.exec(text))) {
            const payload = factory(exec);
            if (!payload)
                continue;
            const parsed = parseToolCall(payload.body, payload.nameHint);
            if (!parsed)
                continue;
            matches.push({
                start: exec.index,
                end: exec.index + exec[0].length,
                call: parsed
            });
        }
    };
    applyPattern(GLM_CUSTOM_TAG, (match) => ({ body: match[2] ?? '', nameHint: match[1] }));
    for (const [pattern, factory] of GENERIC_PATTERNS) {
        applyPattern(pattern, factory);
    }
    applyTaggedArgPatterns(text, matches);
    if (!matches.length && typeof text === 'string' && text.includes('<arg_key>')) {
        GLM_INLINE_NAME.lastIndex = 0;
        const inline = GLM_INLINE_NAME.exec(text);
        GLM_INLINE_NAME.lastIndex = 0;
        if (inline && inline[1]) {
            const name = inline[1].trim();
            const block = text.slice(inline[0].length);
            const argsRecord = parseTaggedArgBlock(block);
            if (name && argsRecord) {
                matches.push({
                    start: 0,
                    end: text.length,
                    call: {
                        name,
                        args: safeStringify(argsRecord)
                    }
                });
            }
        }
    }
    matches.sort((a, b) => a.start - b.start);
    const toolCalls = matches.map((entry, idx) => ({
        id: `glm_tool_${choiceIndex}_${idx + 1}`,
        type: 'function',
        function: {
            name: entry.call.name,
            arguments: entry.call.args
        }
    }));
    matches.sort((a, b) => b.start - a.start);
    let cleaned = text;
    for (const entry of matches) {
        cleaned = cleaned.slice(0, entry.start) + cleaned.slice(entry.end);
    }
    const reasoningText = cleaned.trim();
    return {
        toolCalls,
        reasoningText: reasoningText.length ? reasoningText : undefined
    };
}
function parseToolCall(body, nameHint) {
    if (!body || typeof body !== 'string') {
        return null;
    }
    const trimmed = body.trim();
    if (!trimmed.length) {
        return null;
    }
    try {
        const parsed = JSON.parse(trimmed);
        if (!parsed || typeof parsed !== 'object') {
            return null;
        }
        const record = parsed;
        const candidateName = (typeof record.name === 'string' && record.name.trim().length
            ? record.name.trim()
            : undefined) ??
            (typeof record.tool_name === 'string' && record.tool_name.trim().length
                ? record.tool_name.trim()
                : undefined) ??
            (typeof record.tool === 'string' && record.tool.trim().length
                ? record.tool.trim()
                : undefined) ??
            (nameHint && nameHint.trim().length ? nameHint.trim() : undefined);
        if (!candidateName) {
            return null;
        }
        const argsSource = record.arguments ??
            record.input ??
            record.params ??
            record.parameters ??
            record.payload ??
            {};
        let args = '{}';
        if (typeof argsSource === 'string' && argsSource.trim().length) {
            args = argsSource.trim();
        }
        else {
            try {
                args = JSON.stringify(argsSource ?? {});
            }
            catch {
                args = '{}';
            }
        }
        return { name: candidateName, args };
    }
    catch {
        return null;
    }
}
function applyTaggedArgPatterns(text, matches) {
    if (!text || typeof text !== 'string') {
        return;
    }
    GLM_TAGGED_SEQUENCE.lastIndex = 0;
    let exec;
    while ((exec = GLM_TAGGED_SEQUENCE.exec(text))) {
        let name = typeof exec[1] === 'string' ? exec[1].trim() : '';
        let block = exec[2] ?? '';
        if (!name) {
            const inline = GLM_INLINE_NAME.exec(block);
            if (inline && inline[1]) {
                name = inline[1].trim();
                block = block.slice(inline[0].length);
            }
        }
        if (!name) {
            continue;
        }
        const argsRecord = parseTaggedArgBlock(block);
        if (!argsRecord) {
            continue;
        }
        matches.push({
            start: exec.index,
            end: exec.index + exec[0].length,
            call: {
                name,
                args: safeStringify(argsRecord)
            }
        });
    }
}
function parseTaggedArgBlock(block) {
    if (!block || typeof block !== 'string') {
        return null;
    }
    const record = {};
    GLM_TAGGED_BLOCK.lastIndex = 0;
    let exec;
    while ((exec = GLM_TAGGED_BLOCK.exec(block))) {
        const key = typeof exec[1] === 'string' ? exec[1].trim() : '';
        if (!key) {
            continue;
        }
        const rawValue = typeof exec[2] === 'string' ? exec[2].trim() : '';
        record[key] = coerceTaggedValue(rawValue);
    }
    return Object.keys(record).length ? record : null;
}
function coerceTaggedValue(raw) {
    if (!raw) {
        return '';
    }
    const trimmed = raw.trim();
    try {
        return JSON.parse(trimmed);
    }
    catch {
        return trimmed;
    }
}
function safeStringify(value) {
    try {
        return JSON.stringify(value ?? {});
    }
    catch {
        return '{}';
    }
}
