"""OpenAI 消息与数据库存储之间的序列化。"""

from __future__ import annotations

import json
from typing import Any

# 多模态计费/上下文粗估：业界常见按每张图约 2k–8k token（分辨率与模型有关）
VISION_IMAGE_TOKENS_MIN = 2000
VISION_IMAGE_TOKENS_MAX = 8000
VISION_IMAGE_TOKENS_ESTIMATE = (VISION_IMAGE_TOKENS_MIN + VISION_IMAGE_TOKENS_MAX) // 2


def tool_calls_to_jsonable(tool_calls: Any) -> list[dict[str, Any]]:
    if not tool_calls:
        return []
    out: list[dict[str, Any]] = []
    for tc in tool_calls:
        fn = getattr(tc, "function", None)
        name = getattr(fn, "name", "") if fn else ""
        args = getattr(fn, "arguments", None) if fn else None
        out.append(
            {
                "id": getattr(tc, "id", "") or "",
                "type": getattr(tc, "type", None) or "function",
                "function": {
                    "name": name,
                    "arguments": args if isinstance(args, str) else (args or "{}"),
                },
            }
        )
    return out


def estimate_message_tokens(msg: dict[str, Any]) -> int:
    """
    粗估 token：纯文本约 4 字符/token；含 ``tool_calls`` JSON。
    若 ``content`` 为 OpenAI 多模态列表（``text`` + ``image_url``），
    每张图按 ``VISION_IMAGE_TOKENS_ESTIMATE``（2k–8k 的中值）加计，便于与 ``context_budget_tokens`` 对齐。
    """
    raw_c = msg.get("content")
    if isinstance(raw_c, list):
        text_chars = 0
        image_n = 0
        for part in raw_c:
            if not isinstance(part, dict):
                continue
            if part.get("type") == "text":
                text_chars += len(str(part.get("text") or ""))
            elif part.get("type") == "image_url":
                image_n += 1
        text_tok = (text_chars // 4 + 1) if text_chars else 0
        vision_tok = image_n * VISION_IMAGE_TOKENS_ESTIMATE
        tc = msg.get("tool_calls")
        extra = len(json.dumps(tc, ensure_ascii=False)) // 4 + 1 if tc else 0
        return max(1, text_tok + vision_tok + extra)

    n = len(str(raw_c or ""))
    tc = msg.get("tool_calls")
    if tc:
        n += len(json.dumps(tc, ensure_ascii=False))
    return max(1, n // 4 + 1)
