"""
AgniPod SDK — Chat Completions
================================

Usage::

    from agnipod import AgniPod

    client = AgniPod()

    # Simple
    response = client.chat.create(
        model="qwen3:8b",
        messages=[{"role": "user", "content": "Hello!"}],
    )
    print(response.content)

    # With system prompt
    response = client.chat.create(
        model="qwen3:8b",
        system_prompt="You are a concise assistant.",
        messages=[{"role": "user", "content": "What is AI?"}],
    )

    # Streaming
    stream = client.chat.create(
        model="qwen3:8b",
        messages=[{"role": "user", "content": "Tell me a story"}],
        stream=True,
    )
    for chunk in stream:
        print(chunk, end="", flush=True)
"""

from __future__ import annotations
from typing import Any, Dict, List, Optional, Union

from ._client import HttpClient
from ._types import Completion, StreamResponse
from ._exceptions import ValidationError

_ENDPOINT = "/chat"


class Chat:
    """Namespace for ``/chat`` operations."""

    def __init__(self, client: HttpClient):
        self._client = client

    def create(
        self,
        *,
        model: str,
        messages: List[Dict[str, str]],
        system_prompt: Optional[str] = None,
        context: Optional[str] = None,
        temperature: Optional[float] = None,
        top_p: Optional[float] = None,
        top_k: Optional[int] = None,
        min_p: Optional[float] = None,
        max_tokens: Optional[int] = None,
        stop: Optional[List[str]] = None,
        repeat_penalty: Optional[float] = None,
        presence_penalty: Optional[float] = None,
        frequency_penalty: Optional[float] = None,
        seed: Optional[int] = None,
        function_calling: bool = False,
        stream: bool = False,
    ) -> Union[Completion, StreamResponse]:
        """Create a chat completion.

        Parameters
        ----------
        model : str
            Name of the model (e.g. ``"qwen3:8b"``).
        messages : list[dict]
            Conversation messages. Each dict must have ``role`` and ``content`` keys.
            Roles: ``"user"``, ``"assistant"``.
        system_prompt : str, optional
            System-level instruction prepended before messages.
        context : str, optional
            Additional context appended to the prompt (e.g. retrieved documents or
            function call results).
        temperature, top_p, top_k, min_p, max_tokens, stop, repeat_penalty,
        presence_penalty, frequency_penalty, seed :
            Optional sampling parameters.
        function_calling : bool
            Enable function/tool calling mode.
        stream : bool
            If ``True``, returns a ``StreamResponse`` that yields ``StreamChunk`` objects.

        Returns
        -------
        Completion or StreamResponse
        """
        if not model:
            raise ValidationError("model is required")
        if not messages or not isinstance(messages, list):
            raise ValidationError("messages must be a non-empty list")

        payload: Dict[str, Any] = {
            "model": model,
            "messages": messages,
            "stream": stream,
        }

        if system_prompt is not None:
            payload["system_prompt"] = system_prompt
        if context is not None:
            payload["context"] = context
        if function_calling:
            payload["function_calling"] = True

        # Add optional sampling params (only if set)
        _add_optional(payload, "temperature", temperature)
        _add_optional(payload, "top_p", top_p)
        _add_optional(payload, "top_k", top_k)
        _add_optional(payload, "min_p", min_p)
        _add_optional(payload, "max_tokens", max_tokens)
        _add_optional(payload, "stop", stop)
        _add_optional(payload, "repeat_penalty", repeat_penalty)
        _add_optional(payload, "presence_penalty", presence_penalty)
        _add_optional(payload, "frequency_penalty", frequency_penalty)
        _add_optional(payload, "seed", seed)

        if stream:
            chunks = self._client.stream_sse(_ENDPOINT, payload)
            return StreamResponse(chunks)

        data = self._client.post(_ENDPOINT, json_data=payload)
        return Completion(data)


def _add_optional(d: dict, key: str, value: Any):
    if value is not None:
        d[key] = value
