from __future__ import annotations

from typing import Any

__all__ = [
    "to_anthropic_messages",
    "to_gemini_contents",
    "to_openai_messages",
]


def to_openai_messages(messages: list[dict[str, Any]] | None) -> list[dict[str, Any]]:
    """Return OpenAI chat messages as-is, dropping empty entries.

    Neutral messages use ``role`` / ``content`` (system/user/assistant). Tool
    results are expected as plain user/assistant text — not ``role: tool``.
    """
    if not messages:
        return []

    out: list[dict[str, Any]] = []
    for message in messages:
        if not isinstance(message, dict):
            continue
        role = str(message.get("role") or "").strip().lower()
        if role not in {"system", "user", "assistant"}:
            continue
        content = message.get("content")
        if content is None:
            continue
        if isinstance(content, str) and not content.strip():
            continue
        out.append({"role": role, "content": content})
    return out


def to_anthropic_messages(
    messages: list[dict[str, Any]] | None,
) -> tuple[str | None, list[dict[str, Any]]]:
    """Split system text and map user/assistant messages for Anthropic Messages API.

    Returns ``(system, messages)`` where ``system`` is joined system content
    (or ``None``) and ``messages`` contains only ``user`` / ``assistant`` roles.

    Note: the agent loop may emit consecutive same-role turns (tool results as
    user text). Anthropic requires alternation — merge consecutive roles before
    the API call if needed (Agent C), or call sites can pre-merge.
    """
    if not messages:
        return None, []

    system_parts: list[str] = []
    out: list[dict[str, Any]] = []
    for message in messages:
        if not isinstance(message, dict):
            continue
        role = str(message.get("role") or "").strip().lower()
        content = message.get("content")
        text = _content_to_text(content)
        if role == "system":
            if text:
                system_parts.append(text)
            continue
        if role not in {"user", "assistant"}:
            continue
        if not text:
            continue
        out.append({"role": role, "content": text})

    system = "\n\n".join(system_parts) if system_parts else None
    return system, out


def to_gemini_contents(
    messages: list[dict[str, Any]] | None,
) -> tuple[str | None, list[dict[str, Any]]]:
    """Map neutral messages to Gemini ``(system_instruction, contents)``.

    - ``system`` roles become ``system_instruction`` (joined text, or ``None``)
    - ``user`` stays ``user``; ``assistant`` maps to ``model``
    - consecutive same-role contents are merged (Gemini prefers fewer turns)
    - each content is ``{"role": ..., "parts": [{"text": ...}]}``
    """
    if not messages:
        return None, []

    system_parts: list[str] = []
    contents: list[dict[str, Any]] = []

    for message in messages:
        if not isinstance(message, dict):
            continue
        role = str(message.get("role") or "").strip().lower()
        text = _content_to_text(message.get("content"))
        if role == "system":
            if text:
                system_parts.append(text)
            continue
        if role == "assistant":
            gemini_role = "model"
        elif role == "user":
            gemini_role = "user"
        else:
            continue
        if not text:
            continue

        if contents and contents[-1]["role"] == gemini_role:
            prev_parts = contents[-1]["parts"]
            prev_text = prev_parts[0].get("text", "") if prev_parts else ""
            prev_parts[0] = {"text": f"{prev_text}\n\n{text}" if prev_text else text}
            continue

        contents.append({"role": gemini_role, "parts": [{"text": text}]})

    system_instruction = "\n\n".join(system_parts) if system_parts else None
    return system_instruction, contents


def _content_to_text(content: Any) -> str:
    if content is None:
        return ""
    if isinstance(content, str):
        return content.strip()
    if isinstance(content, list):
        # Best-effort: join text-ish parts from multimodal-ish lists.
        parts: list[str] = []
        for item in content:
            if isinstance(item, str) and item.strip():
                parts.append(item.strip())
            elif isinstance(item, dict):
                text = item.get("text")
                if isinstance(text, str) and text.strip():
                    parts.append(text.strip())
        return "\n".join(parts)
    return str(content).strip()
