/**
 * ============================================================
 *  src/context/tokenCounter.ts —— Token 估算
 * ------------------------------------------------------------
 *  我们用一个**粗略估算**（chars / 4）作为 token 数。
 *
 *  为何不用 tiktoken / @anthropic-ai/tokenizer？
 *    - 真实 tokenizer 对每个模型都不同，且会增加几 MB 的依赖
 *    - 我们只用估算来决定"要不要触发压缩"，误差 ±20% 完全可以接受
 *    - chars/4 对英文很准；对中文偏低（中文 1 char ≈ 1 token）
 *
 *  如何换成精确版？
 *    1. npm i tiktoken
 *    2. 把 countTokens() 实现替换为 tiktoken 调用
 *    3. 不改外部签名即可
 * ============================================================
 */
/**
 * 粗略估算一段文本的 token 数。
 *
 * 经验值：
 *   - GPT 系列英文: 1 token ≈ 4 chars
 *   - 中文：1 char ≈ 1~1.5 tokens（因此本函数对中文低估）
 *
 * 我们选保守的 chars/4 + 中文加权：
 *   ASCII 部分按 /4，非 ASCII（主要是中文）按 *1.0。
 */
export function countTextTokens(text) {
    if (!text)
        return 0;
    let asciiChars = 0;
    let nonAsciiChars = 0;
    for (let i = 0; i < text.length; i++) {
        const code = text.charCodeAt(i);
        if (code < 128)
            asciiChars++;
        else
            nonAsciiChars++;
    }
    return Math.ceil(asciiChars / 4) + nonAsciiChars;
}
/**
 * 估算「工具 schema」本身占的 token。每轮请求都会把 ALL_TOOLS 的
 * name + description + parameters JSON 发给 LLM，但 countMessagesTokens 只数历史消息、
 * 漏了这部分 → 系统性低估。自动压缩判定阈值时必须把它加上才准。
 *
 * 每个工具的 token 组成：
 *   - 8 token 固定开销（type/function 包裹等结构体）
 *   - name 字段的文本 token
 *   - description 字段的文本 token（支持字符串或函数两种形式）
 *   - parameters JSON 序列化后的文本 token
 */
export function estimateToolsTokens(tools) {
    let total = 0;
    for (const t of tools) {
        total += 8; // 每个工具的结构固定开销（type/function 包裹）
        total += countTextTokens(t.name);
        // description 可以是静态字符串，也可以是注入运行时信息的函数
        const desc = typeof t.description === 'function' ? t.description() : t.description;
        total += countTextTokens(desc);
        total += countTextTokens(JSON.stringify(t.parameters));
    }
    return total;
}
/** 估算整段历史的 token 数（包含 role / 工具调用结构的开销） */
export function countMessagesTokens(messages) {
    let total = 0;
    for (const m of messages) {
        // 每条消息至少有 ~4 token 的固定开销（role + 边界标记）
        total += 4;
        if (typeof m.content === 'string') {
            total += countTextTokens(m.content);
        }
        else if (m.content === null) {
            // assistant 的 content 可以是 null（只有 tool_calls 时）
        }
        if (m.role === 'assistant' && m.tool_calls) {
            for (const tc of m.tool_calls) {
                total += 8; // tool_call 结构开销
                total += countTextTokens(tc.function.name);
                total += countTextTokens(tc.function.arguments);
            }
        }
        if (m.role === 'tool') {
            total += countTextTokens(m.tool_call_id);
        }
    }
    return total;
}
