"""Shared helpers for GenAI providers and chat (OpenAI-style messages, tool call parsing)."""

import json
import logging
from typing import Any

logger = logging.getLogger(__name__)


def parse_tool_calls_from_message(
    message: dict[str, Any],
) -> list[dict[str, Any]] | None:
    """
    Parse tool_calls from an OpenAI-style message dict.

    Message may have "tool_calls" as a list of:
      {"id": str, "function": {"name": str, "arguments": str}, ...}

    Returns a list of {"id", "name", "arguments"} with arguments parsed as dict,
    or None if no tool_calls. Used by Ollama and LlamaCpp (non-stream) responses.
    """
    raw = message.get("tool_calls")
    if not raw or not isinstance(raw, list):
        return None
    result = []
    for idx, tool_call in enumerate(raw):
        function_data = tool_call.get("function") or {}
        raw_arguments = function_data.get("arguments") or {}
        if isinstance(raw_arguments, dict):
            arguments = raw_arguments
        elif isinstance(raw_arguments, str):
            try:
                arguments = json.loads(raw_arguments)
            except (json.JSONDecodeError, KeyError, TypeError) as e:
                logger.warning(
                    "Failed to parse tool call arguments: %s, tool: %s",
                    e,
                    function_data.get("name", "unknown"),
                )
                arguments = {}
        else:
            arguments = {}
        result.append(
            {
                "id": tool_call.get("id", "") or f"call_{idx}",
                "name": function_data.get("name", ""),
                "arguments": arguments,
            }
        )
    return result if result else None


def build_assistant_message_for_conversation(
    content: Any,
    tool_calls_raw: list[dict[str, Any]] | None,
) -> dict[str, Any]:
    """
    Build the assistant message dict in OpenAI format for appending to a conversation.

    tool_calls_raw: list of {"id", "name", "arguments"} (arguments as dict), or None.
    """
    msg: dict[str, Any] = {"role": "assistant", "content": content}
    if tool_calls_raw:
        msg["tool_calls"] = [
            {
                "id": tc["id"],
                "type": "function",
                "function": {
                    "name": tc["name"],
                    "arguments": json.dumps(tc.get("arguments") or {}),
                },
                # Gemini-only: opaque signature that must be echoed back on
                # the same functionCall part in the next turn. Other providers
                # do not set or read this.
                **(
                    {"thought_signature": tc["thought_signature"]}
                    if tc.get("thought_signature")
                    else {}
                ),
            }
            for tc in tool_calls_raw
        ]
    return msg
