import copy
import re
from typing import Any, Dict, List, Tuple

from litellm import Message


def add_anthropic_caching(
    messages: List[Dict[str, Any] | Message], model_name: str
) -> List[Dict[str, Any] | Message]:
    """
    Add ephemeral caching to the most recent messages for Anthropic models.

    Args:
        messages: List of message dictionaries
        model_name: The model name to check if it's an Anthropic model

    Returns:
        List of messages with caching added to the most recent 3 messages
    """
    # Only apply caching for Anthropic models
    if not ("anthropic" in model_name.lower() or "claude" in model_name.lower()):
        return messages

    # Create a deep copy to avoid modifying the original messages
    cached_messages = copy.deepcopy(messages)

    # Add cache_control to the most recent 3 messages
    for n in range(len(cached_messages)):
        if n >= len(cached_messages) - 3:
            msg = cached_messages[n]

            # Handle both dict and Message-like objects
            if isinstance(msg, dict):
                # Ensure content is in the expected format
                if isinstance(msg.get("content"), str):
                    msg["content"] = [
                        {
                            "type": "text",
                            "text": msg["content"],
                            "cache_control": {"type": "ephemeral"},
                        }
                    ]
                elif isinstance(msg.get("content"), list):
                    # Add cache_control to each content item
                    for content_item in msg["content"]:
                        if isinstance(content_item, dict) and "type" in content_item:
                            content_item["cache_control"] = {"type": "ephemeral"}
            elif hasattr(msg, "content"):
                if isinstance(msg.content, str):
                    msg.content = [  # type: ignore
                        {
                            "type": "text",
                            "text": msg.content,
                            "cache_control": {"type": "ephemeral"},
                        }
                    ]
                elif isinstance(msg.content, list):
                    for content_item in msg.content:
                        if isinstance(content_item, dict) and "type" in content_item:
                            content_item["cache_control"] = {"type": "ephemeral"}

    return cached_messages


_HOSTED_VLLM_PREFIX = "hosted_vllm/"
_HOSTED_VLLM_MODEL_PATTERN = re.compile(r"^[A-Za-z0-9._-]{1,64}$")
_HOSTED_VLLM_REQUIRED_INT_FIELDS = ("max_input_tokens", "max_output_tokens")
_HOSTED_VLLM_REQUIRED_FLOAT_FIELDS = (
    "input_cost_per_token",
    "output_cost_per_token",
)


def validate_hosted_vllm_model_config(
    full_model_name: str, model_info: dict[str, Any] | None
) -> Tuple[str, dict[str, Any]]:
    """
    Validate hosted_vllm model configuration.

    Args:
        full_model_name: The user-provided model name (e.g., hosted_vllm/llama)
        model_info: Optional metadata dictionary that must include token limits + cost info

    Returns:
        (canonical_model_name, normalized_model_info)

    Raises:
        ValueError: If validation fails
    """

    if not full_model_name.startswith(_HOSTED_VLLM_PREFIX):
        raise ValueError(
            "hosted_vllm models must start with 'hosted_vllm/'. "
            f"Got '{full_model_name}'."
        )

    if full_model_name.count("/") != 1:
        raise ValueError(
            "hosted_vllm model names must contain exactly one '/'. "
            f"Got '{full_model_name}'."
        )

    canonical = full_model_name.split("/", 1)[1]
    if not _HOSTED_VLLM_MODEL_PATTERN.fullmatch(canonical):
        raise ValueError(
            "hosted_vllm canonical model names may only contain letters, numbers, "
            "'.', '-', '_' and must be fewer than 64 characters with no spaces. "
            f"Got '{canonical}'."
        )

    if not model_info:
        raise ValueError(
            "hosted_vllm models require `model_info` specifying token limits and costs. "
            "Please provide max_input_tokens, max_output_tokens, "
            "input_cost_per_token, and output_cost_per_token."
        )

    normalized_info = dict(model_info)

    for field in _HOSTED_VLLM_REQUIRED_INT_FIELDS:
        value = model_info.get(field)
        if value is None:
            raise ValueError(f"hosted_vllm model_info missing '{field}'.")
        try:
            normalized_info[field] = int(float(value))
        except (TypeError, ValueError):
            raise ValueError(
                f"hosted_vllm model_info field '{field}' must be a number. "
                f"Got '{value}'."
            )

    for field in _HOSTED_VLLM_REQUIRED_FLOAT_FIELDS:
        value = model_info.get(field)
        if value is None:
            raise ValueError(f"hosted_vllm model_info missing '{field}'.")
        try:
            normalized_info[field] = float(value)
        except (TypeError, ValueError):
            raise ValueError(
                f"hosted_vllm model_info field '{field}' must be a float. "
                f"Got '{value}'."
            )

    return canonical, normalized_info


def split_provider_model_name(model_name: str) -> tuple[str | None, str]:
    """
    Split a model name into (provider_prefix, canonical_name).

    Args:
        model_name: e.g. "anthropic/claude-3" or "gpt-4"

    Returns:
        tuple(provider_prefix | None, canonical_name)
    """
    if "/" not in model_name:
        return None, model_name

    provider, canonical = model_name.split("/", 1)
    return provider.lower(), canonical
