"""Chat API resource wrapper"""

from typing import Optional, List, Dict, Any, Union, TYPE_CHECKING

from .base import Resource
from ..models.chat import (
    ChatMessage,
    WebSearchOptions,
    ResponseFormat,
    Tool,
    Reasoning,
    ChatRequest,
    ChatCompletion,
)
from ..utils.constants import ENDPOINT_CHAT, DEFAULT_CHAT_TIMEOUT

if TYPE_CHECKING:
    from ..core.streaming import Stream


class ChatResource(Resource):
    """Chat API resource.

    Provides access to the ``/v1/chat/completions`` endpoint supporting both
    non-streaming and streaming (SSE) responses.

    Example - non-streaming::

        response = client.chat.create(
            model="openai/gpt-5.4",
            messages=[ChatMessage(role="user", content="Hello!")],
            web_search="on",
        )
        print(response.text)

    Example - streaming::

        for event in client.chat.create(
            model="openai/gpt-5.4",
            messages=[ChatMessage(role="user", content="Hello!")],
            stream=True,
        ):
            if event.type == "content" and event.choices:
                print(event.choices[0].delta.content or "", end="", flush=True)
    """

    def create(
        self,
        messages: List[Union[ChatMessage, Dict[str, Any]]],
        model: str,
        stream: bool = False,
        web_search: Optional[str] = None,
        web_search_options: Optional[WebSearchOptions] = None,
        max_tokens: Optional[int] = None,
        max_completion_tokens: Optional[int] = None,
        temperature: Optional[float] = None,
        top_p: Optional[float] = None,
        frequency_penalty: Optional[float] = None,
        presence_penalty: Optional[float] = None,
        response_format: Optional[ResponseFormat] = None,
        stop: Optional[List[str]] = None,
        seed: Optional[int] = None,
        reasoning_effort: Optional[str] = None,
        logprobs: Optional[bool] = None,
        top_logprobs: Optional[int] = None,
        logit_bias: Optional[Dict[str, float]] = None,
        tools: Optional[List[Union[Tool, Dict[str, Any]]]] = None,
        tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
        user: Optional[str] = None,
        timeout: Optional[float] = None,
    ) -> Union[ChatCompletion, "Stream"]:
        """Create a chat completion.

        Args:
            messages: Conversation history. Each item may be a
                      :class:`~octen.models.chat.ChatMessage` or a plain dict
                      ``{"role": ..., "content": ...}``.
            model: Model ID (e.g. ``"openai/gpt-5.4"``).
            stream: If ``True``, return a
                    :class:`~octen.core.streaming.Stream` iterator of
                    :class:`~octen.models.chat.StreamEvent` objects.
                    If ``False`` (default), return a
                    :class:`~octen.models.chat.ChatCompletion`.
            web_search: ``"on"`` to augment the response with live web search,
                        ``"off"`` to disable. Omit to use the server default.
            web_search_options: Fine-grained search configuration.
            max_tokens: Maximum number of output tokens.
            max_completion_tokens: Alternative max-token parameter.
            temperature: Sampling temperature in ``[0, 2]``.
            top_p: Nucleus sampling probability in ``(0, 1]``.
            frequency_penalty: Repeat-word penalty in ``[-2, 2]``.
            presence_penalty: New-topic penalty in ``[-2, 2]``.
            response_format: Output format specification.
            stop: Up to 4 stop sequences.
            seed: Integer seed for deterministic sampling.
            reasoning_effort: Reasoning effort: ``"low"``, ``"medium"``, or ``"high"``.
            logprobs: Whether to return log probabilities.
            top_logprobs: Number of most-likely tokens at each position ``[0, 20]``.
            logit_bias: Token ID to bias value mapping.
            tools: List of tool definitions available to the model.
            tool_choice: Tool selection strategy: ``"none"``, ``"auto"``,
                         ``"required"``, or a dict specifying a particular tool.
            user: Opaque end-user identifier.
            timeout: Per-request timeout in seconds; overrides the client default.
                     Defaults to ``DEFAULT_CHAT_TIMEOUT`` (60s) if not set.

        Returns:
            :class:`~octen.models.chat.ChatCompletion` when ``stream=False``,
            or a :class:`~octen.core.streaming.Stream` when ``stream=True``.
        """
        # Normalise messages: accept both ChatMessage and plain dicts
        normalised_messages = []
        for msg in messages:
            if isinstance(msg, dict):
                normalised_messages.append(ChatMessage(**msg))
            else:
                normalised_messages.append(msg)

        # Normalise tools
        normalised_tools = None
        if tools:
            normalised_tools = []
            for tool in tools:
                if isinstance(tool, dict):
                    normalised_tools.append(Tool(**tool))
                else:
                    normalised_tools.append(tool)

        # Build and validate request via Pydantic
        request = ChatRequest(
            model=model,
            messages=normalised_messages,
            stream=stream if stream else None,
            web_search=web_search,
            web_search_options=web_search_options,
            max_tokens=max_tokens,
            max_completion_tokens=max_completion_tokens,
            temperature=temperature,
            top_p=top_p,
            frequency_penalty=frequency_penalty,
            presence_penalty=presence_penalty,
            response_format=response_format,
            stop=stop,
            seed=seed,
            reasoning=Reasoning(effort=reasoning_effort) if reasoning_effort else None,
            logprobs=logprobs,
            top_logprobs=top_logprobs,
            logit_bias=logit_bias,
            tools=normalised_tools,
            tool_choice=tool_choice,
            user=user,
        )

        payload = request.model_dump(exclude_none=True)

        # Use chat-specific timeout default
        effective_timeout = timeout if timeout is not None else DEFAULT_CHAT_TIMEOUT

        if stream:
            return self._client.stream_request(
                method="POST",
                endpoint=ENDPOINT_CHAT,
                json=payload,
                timeout=effective_timeout,
            )

        response_data = self._client.request(
            method="POST",
            endpoint=ENDPOINT_CHAT,
            json=payload,
            timeout=effective_timeout,
        )
        return ChatCompletion(**response_data)
