"""
The ``thread`` module provides a client for the Snowflake Cortex Agents
Threads REST API.
"""
from dataclasses import dataclass
from typing import List, Optional, Dict, Any

import httpx

from relationalai.util.docutils import include_in_docs

__include_in_docs__ = True


@include_in_docs
class CortexThreadApi:
    """Client for interacting with the Snowflake Cortex Agents Threads REST API.

    Provides methods for creating, retrieving, updating, listing, and deleting
    conversation threads used with Cortex agents. Threads enable multi-turn
    conversations and message history management.

    Parameters
    ----------
    client : httpx.Client
        Configured ``httpx.Client`` with authentication headers.
    """
    def __init__(self, client: httpx.Client):
        self._c = client

    @dataclass
    class CreateThreadResponse:
        """Response containing newly created thread details.

        Attributes
        ----------
        thread_id : int
            Unique UUID identifier for the thread.
        thread_name : str
            Auto-generated or user-specified thread name.
        origin_application : str
            Application identifier that created the thread (max 16 bytes).
        created_on : int
            Unix timestamp of thread creation.
        updated_on : int
            Unix timestamp of last update.
        """
        thread_id: int
        thread_name: str
        origin_application: str
        created_on: int
        updated_on: int

    @include_in_docs
    def create(self, origin_application: Optional[str] = None) -> CreateThreadResponse:
        """Create a new conversation thread.

        Establishes a new thread for multi-turn conversations with Cortex
        agents. The ``origin_application`` field allows grouping threads by
        their source application.

        Parameters
        ----------
        origin_application : str, optional
            Identifier for the source application (max 16 bytes). Useful for
            filtering and organizing threads by application.

        Returns
        -------
        CreateThreadResponse
            The new thread's metadata including its unique ID.

        Raises
        ------
        httpx.HTTPStatusError
            If the request fails.
        """
        body = _maybe_add({}, {"origin_application": origin_application})
        response = self._c.post(
            url="/api/v2/cortex/threads",
            json=body,
        )
        response.raise_for_status()
        return CortexThreadApi.CreateThreadResponse(**response.json())

    @dataclass
    class Message:
        """A single message in a conversation thread.

        Messages form a conversation chain through ``parent_id`` references,
        enabling multi-turn dialogues between users and agents.

        Attributes
        ----------
        message_id : str
            Unique identifier for this message.
        message_payload : str
            The actual content of the message.
        role : str
            Message author role (``"user"`` or ``"assistant"``).
        parent_id : int
            ID of the previous message in the conversation chain.
        created_on : int
            Unix timestamp when the message was created.
        request_id : str
            Trace identifier for debugging and logging.
        """
        message_id: str
        message_payload: str
        role: str
        parent_id: int
        created_on: int
        request_id: str

    @dataclass
    class DescribeThreadResponse:
        """Response containing complete thread metadata and messages.

        Messages are returned in descending creation order (newest first) with
        support for pagination via query parameters.

        Attributes
        ----------
        thread_id : int
            Unique thread identifier.
        thread_name : str
            Thread name.
        origin_application : str or None
            Application that created the thread, if specified.
        created_on : int
            Unix timestamp of thread creation.
        updated_on : int
            Unix timestamp of last update.
        messages : list of CortexThreadApi.Message
            Messages in the thread (paginated, descending order).
        """
        thread_id: int
        thread_name: str
        origin_application: Optional[str]
        created_on: int
        updated_on: int
        messages: List['CortexThreadApi.Message']

    @include_in_docs
    def describe(self, thread_id: int) -> DescribeThreadResponse:
        """Retrieve thread metadata and messages.

        Returns complete thread information including metadata and paginated
        messages in descending creation order (newest first).

        Parameters
        ----------
        thread_id : int
            Unique identifier of the thread to retrieve.

        Returns
        -------
        DescribeThreadResponse
            Thread metadata and messages.

        Raises
        ------
        httpx.HTTPStatusError
            If the request fails or thread doesn't exist.
        """
        response = self._c.get(
            url=f"/api/v2/cortex/threads/{thread_id}",
        )
        response.raise_for_status()
        data = response.json()
        messages = [CortexThreadApi.Message(**msg) for msg in data.get('messages', [])]
        metadata = data.get('metadata', {})
        return CortexThreadApi.DescribeThreadResponse(
            thread_id=metadata['thread_id'],
            thread_name=metadata['thread_name'],
            origin_application=metadata.get('origin_application'),
            created_on=metadata['created_on'],
            updated_on=metadata['updated_on'],
            messages=messages
        )

    @include_in_docs
    def update(self, thread_id: int, thread_name: Optional[str] = None) -> None:
        """Update thread properties.

        Modifies thread metadata, specifically allowing updates to the thread
        name. Other thread properties are immutable.

        Parameters
        ----------
        thread_id : int
            Unique identifier of the thread to update.
        thread_name : str, optional
            New name for the thread. If ``None``, no update is performed.

        Returns
        -------
        None

        Raises
        ------
        httpx.HTTPStatusError
            If the request fails or thread doesn't exist.
        """
        body = _maybe_add({}, {"thread_name": thread_name})
        response = self._c.post(
            url=f"/api/v2/cortex/threads/{thread_id}",
            json=body,
        )
        response.raise_for_status()

    @dataclass
    class ThreadMetadata:
        """Summary metadata for a thread in a list response.

        Contains thread identification and timestamps without message content,
        optimized for displaying thread lists.

        Attributes
        ----------
        thread_id : int
            Unique thread identifier.
        thread_name : str
            Thread name.
        origin_application : str or None
            Application that created the thread, if specified.
        created_on : int
            Unix timestamp of thread creation.
        updated_on : int
            Unix timestamp of last update.
        """
        thread_id: int
        thread_name: str
        origin_application: Optional[str]
        created_on: int
        updated_on: int

    @dataclass
    class ListThreadsResponse:
        """Response containing a list of thread metadata objects.

        Attributes
        ----------
        threads : list of CortexThreadApi.ThreadMetadata
            Thread metadata for all user-owned threads.
        """
        threads: List['CortexThreadApi.ThreadMetadata']

    @include_in_docs
    def list(self, origin_application: Optional[str] = None) -> ListThreadsResponse:
        """List all threads owned by the current user.

        Retrieves metadata for all threads, optionally filtered by origin
        application. Does not include message content.

        Parameters
        ----------
        origin_application : str, optional
            Filter to show only threads created by a specific application.

        Returns
        -------
        ListThreadsResponse
            Thread metadata objects.

        Raises
        ------
        httpx.HTTPStatusError
            If the request fails.
        """
        params = _maybe_add({}, {"origin_application": origin_application})
        response = self._c.get(
            url="/api/v2/cortex/threads",
            params=params,
        )
        response.raise_for_status()
        data = response.json()
        threads = [CortexThreadApi.ThreadMetadata(**thread) for thread in data]
        return CortexThreadApi.ListThreadsResponse(threads=threads)

    @include_in_docs
    def delete(self, thread_id: int) -> None:
        """Delete a thread and all associated messages.

        Permanently removes a thread and its complete message history.
        This operation cannot be undone.

        Parameters
        ----------
        thread_id : int
            Unique identifier of the thread to delete.

        Returns
        -------
        None

        Raises
        ------
        httpx.HTTPStatusError
            If the request fails or thread doesn't exist.
        """
        response = self._c.delete(
            url=f"/api/v2/cortex/threads/{thread_id}",
        )
        response.raise_for_status()


def _maybe_add(d: dict, param: Dict[str, Any | None]) -> dict:
    """Conditionally add non-None values to a dictionary."""
    for k, v in param.items():
        if v is not None:
            d.update({k: v})
    return d
