"""Anthropic prompt caching — inject cache_control breakpoints.

Places up to 4 ``cache_control`` markers on messages so the Anthropic API
(via OpenRouter or direct) caches the stable prefix across turns:

  1. System prompt  (stable across all turns → highest cache hit rate)
  2–4. Last 3 non-system messages  (rolling window)

Cached tokens cost 0.1x on reads (90% saving) and 1.25x on writes.
After the first turn the system prompt is almost always a cache hit.
"""

from __future__ import annotations

import copy
from typing import Any

_CACHE_MARKER = {"type": "ephemeral"}


def _apply_marker(msg: dict[str, Any]) -> None:
    """Add cache_control to the last content block of a message."""
    content = msg.get("content")

    # Tool messages: top-level marker
    if msg.get("role") == "tool":
        msg["cache_control"] = _CACHE_MARKER
        return

    # None/empty content
    if not content:
        msg["cache_control"] = _CACHE_MARKER
        return

    # String content → convert to content block list
    if isinstance(content, str):
        msg["content"] = [
            {"type": "text", "text": content, "cache_control": _CACHE_MARKER}
        ]
        return

    # List content → mark last block
    if isinstance(content, list) and content:
        last = content[-1]
        if isinstance(last, dict):
            last["cache_control"] = _CACHE_MARKER


def apply_cache_control(
    messages: list[dict[str, Any]],
    max_breakpoints: int = 4,
) -> list[dict[str, Any]]:
    """Return a deep copy of *messages* with cache_control breakpoints.

    Strategy (same as Hermes "system_and_3"):
      - 1 breakpoint on the system prompt (slot 0)
      - Up to 3 on the last non-system messages (rolling window)

    Args:
        messages: Conversation messages (not modified in place).
        max_breakpoints: Maximum cache breakpoints (Anthropic limit is 4).

    Returns:
        New message list with cache markers injected.
    """
    if not messages:
        return messages

    msgs = copy.deepcopy(messages)
    used = 0

    # 1. System prompt (always first if present)
    if msgs[0].get("role") == "system":
        _apply_marker(msgs[0])
        used += 1

    # 2. Last N non-system messages
    remaining = max_breakpoints - used
    non_sys_indices = [i for i in range(len(msgs)) if msgs[i].get("role") != "system"]
    for idx in non_sys_indices[-remaining:]:
        _apply_marker(msgs[idx])

    return msgs


def is_cacheable_model(model: str) -> bool:
    """Return True if the model supports Anthropic prompt caching."""
    if not model:
        return False
    lower = model.lower()
    return "claude" in lower
