"""
Response models for Reno API.
"""

from typing import Dict, List, Any, Optional


class Usage:
    """Token usage information returned by the API."""

    def __init__(self, data: Dict[str, Any]) -> None:
        self.prompt_tokens: int = data.get("prompt_tokens", 0)
        self.completion_tokens: int = data.get("completion_tokens", 0)
        self.total_tokens: int = data.get("total_tokens", 0)

    def __repr__(self) -> str:
        return (
            f"Usage(prompt={self.prompt_tokens}, "
            f"completion={self.completion_tokens}, "
            f"total={self.total_tokens})"
        )

    def __str__(self) -> str:
        return (
            f"{self.total_tokens} tokens "
            f"({self.prompt_tokens} prompt + {self.completion_tokens} completion)"
        )


class Choice:
    """A single completion choice inside a :class:`Completion`."""

    def __init__(self, data: Dict[str, Any]) -> None:
        self.index: int = data.get("index", 0)
        self.message: Dict[str, str] = data.get("message", {})
        self.finish_reason: str = data.get("finish_reason", "stop")

    @property
    def content(self) -> str:
        """The text content of this choice."""
        return self.message.get("content", "")

    @property
    def role(self) -> str:
        """The role of the author (always ``'assistant'`` for completions)."""
        return self.message.get("role", "assistant")

    def to_message(self) -> Dict[str, str]:
        """Return this choice as a message dict ready to be appended to a conversation."""
        return {"role": self.role, "content": self.content}

    def __repr__(self) -> str:
        preview = self.content[:50] + "…" if len(self.content) > 50 else self.content
        return f"Choice(index={self.index}, content={preview!r})"

    def __str__(self) -> str:
        return self.content


class Completion:
    """A complete (non-streaming) API response."""

    def __init__(self, data: Dict[str, Any]) -> None:
        self.id: str = data.get("id", "")
        self.created: int = data.get("created", 0)
        self.model: str = data.get("model", "")
        self.choices: List[Choice] = [Choice(c) for c in data.get("choices", [])]
        self.usage: Usage = Usage(data.get("usage", {}))
        self._raw: Dict[str, Any] = data

    # ------------------------------------------------------------------
    # Convenience accessors
    # ------------------------------------------------------------------

    @property
    def text(self) -> str:
        """The text content of the first choice."""
        return self.choices[0].content if self.choices else ""

    @property
    def content(self) -> str:
        """Alias for :attr:`text`."""
        return self.text

    def to_message(self) -> Dict[str, str]:
        """
        Return the first choice as a message dict.

        Useful when building a follow-up conversation::

            messages.append(response.to_message())
            messages.append({"role": "user", "content": "Tell me more."})
        """
        return self.choices[0].to_message() if self.choices else {"role": "assistant", "content": ""}

    def to_dict(self) -> Dict[str, Any]:
        """Return the raw response payload."""
        return self._raw

    def __repr__(self) -> str:
        return f"Completion(id={self.id!r}, model={self.model!r}, choices={len(self.choices)})"

    def __str__(self) -> str:
        return self.text


class StreamChunk:
    """
    A single chunk emitted during a streaming response.

    Each chunk carries a small *delta* (partial text) that should be
    concatenated with previous deltas to reconstruct the full response.
    """

    def __init__(self, data: Dict[str, Any]) -> None:
        self.id: str = data.get("id", "")
        self.created: int = data.get("created", 0)
        self.model: str = data.get("model", "")
        self.choices: List[Dict[str, Any]] = data.get("choices", [])
        self._raw: Dict[str, Any] = data

    @property
    def delta(self) -> Optional[str]:
        """The incremental text content for this chunk, or ``None`` if absent."""
        if self.choices:
            return self.choices[0].get("delta", {}).get("content")
        return None

    @property
    def finish_reason(self) -> Optional[str]:
        """``'stop'`` on the last chunk, ``None`` otherwise."""
        if self.choices:
            return self.choices[0].get("finish_reason")
        return None

    @property
    def is_final(self) -> bool:
        """``True`` if this is the last chunk in the stream."""
        return self.finish_reason is not None

    def to_dict(self) -> Dict[str, Any]:
        """Return the raw chunk payload."""
        return self._raw

    def __repr__(self) -> str:
        return f"StreamChunk(id={self.id!r}, delta={self.delta!r}, final={self.is_final})"

    def __str__(self) -> str:
        return self.delta or ""
