"""Token counting utilities (best-effort).

The bridge needs token-count estimates to trim conversation history. Exact
token accounting is provider-specific; this module provides a consistent
fallback strategy.
"""

from __future__ import annotations

from typing import Any


def _rough_tokens(text: str) -> int:
    # Very rough heuristic: 4 chars per token.
    return max(0, len(text) // 4)


def estimate_messages_tokens(messages: list[dict[str, Any]], system: str, provider: str) -> int:
    """Estimate token usage for tool history trimming.

    - Uses `tiktoken` when available for OpenAI-family models.
    - Falls back to a rough chars-per-token heuristic otherwise.
    """
    all_text: list[str] = []
    if system:
        all_text.append(system)

    for m in messages:
        # We accept common message shapes: {role, content} or OpenAI-style content arrays.
        content = m.get("content")
        if isinstance(content, str):
            all_text.append(content)
        elif isinstance(content, list):
            for block in content:
                if isinstance(block, dict):
                    if isinstance(block.get("text"), str):
                        all_text.append(block["text"])
                    elif isinstance(block.get("content"), str):
                        all_text.append(block["content"])
        # Some providers may store other keys; ignore for estimate.

    full = "\n".join(all_text)
    if provider in {"openai", "azure_openai", "openai_compatible", "groq", "together", "mistral"}:
        try:
            import tiktoken  # type: ignore

            # The encoding choice is model-dependent; as a conservative default, use cl100k_base.
            enc = tiktoken.get_encoding("cl100k_base")
            return len(enc.encode(full))
        except Exception:
            return _rough_tokens(full)

    return _rough_tokens(full)

