"""
KivotSession - Multi-turn conversation adapter via Go ThreadStore.

Bridges Python agent workflows to Go ThreadStore via Temporal activities.
Used internally by KivotRunner when thread_id is provided.

All thread operations are executed as Temporal activities on the Go worker's
task queue, keeping Python workers stateless.
"""

from datetime import timedelta
from typing import Any, Dict, List, Optional

from temporalio import workflow
from temporalio.common import RetryPolicy


class KivotSession:
    """
    Session adapter for multi-turn conversations.

    Calls Go thread activities (CreateThreadActivity, GetThreadMessagesActivity,
    AppendThreadMessagesActivity) via Temporal's cross-task-queue activity
    execution. This preserves stateless workers and keeps the Go ThreadStore
    as the single source of truth for conversation history.

    Usage (internal to KivotRunner):
        session = KivotSession(task_queue="core-activities")
        history = await session.load_history("th_123", limit=50)
        # ... run agent ...
        await session.save_messages("th_123", "run_456", new_messages)
    """

    def __init__(
        self,
        task_queue: str = "core-activities",
        timeout: Optional[timedelta] = None,
        retry_policy: Optional[RetryPolicy] = None,
    ):
        """
        Initialize KivotSession.

        Args:
            task_queue: Temporal task queue where Go thread activities are registered.
            timeout: Start-to-close timeout for thread activities.
            retry_policy: Retry policy for thread activities.
        """
        self.task_queue = task_queue
        self.timeout = timeout or timedelta(seconds=10)
        self.retry_policy = retry_policy or RetryPolicy(
            maximum_attempts=3,
            initial_interval=timedelta(seconds=1),
            maximum_interval=timedelta(seconds=5),
        )

    async def create_thread(
        self,
        thread_id: str,
        agent_id: str = "",
        title: str = "",
    ) -> Dict[str, Any]:
        """
        Create a thread if it does not already exist.

        This is idempotent: calling it multiple times with the same thread_id
        is safe (the Go ThreadStore uses ON CONFLICT DO NOTHING).

        Args:
            thread_id: Unique thread identifier.
            agent_id: Optional agent identifier to associate with the thread.
            title: Optional human-readable title for the thread.

        Returns:
            Dict with "thread_id" and "created" (bool) keys.
        """
        return await workflow.execute_activity(
            "CreateThreadActivity",
            {
                "thread_id": thread_id,
                "agent_id": agent_id,
                "title": title,
            },
            task_queue=self.task_queue,
            start_to_close_timeout=self.timeout,
            retry_policy=self.retry_policy,
        )

    async def load_history(
        self,
        thread_id: str,
        limit: int = 0,
    ) -> List[Dict[str, Any]]:
        """
        Load conversation history from the Go ThreadStore.

        Returns messages in OpenAI chat format:
        [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}, ...]

        Args:
            thread_id: Thread to load history from.
            limit: Maximum number of messages to return (0 = all).
                   When set, returns the most recent N messages.

        Returns:
            List of message dicts in OpenAI format.
        """
        result = await workflow.execute_activity(
            "GetThreadMessagesActivity",
            {"thread_id": thread_id, "limit": limit},
            task_queue=self.task_queue,
            start_to_close_timeout=self.timeout,
            retry_policy=self.retry_policy,
        )
        return result.get("messages", [])

    async def save_messages(
        self,
        thread_id: str,
        run_id: str,
        messages: List[Dict[str, Any]],
    ) -> int:
        """
        Save new messages to the Go ThreadStore.

        Filters out system messages (agent instructions are ephemeral and
        should not be persisted across runs). Only user, assistant, and tool
        messages are saved.

        Args:
            thread_id: Thread to append messages to.
            run_id: Run identifier to tag messages with.
            messages: List of message dicts in OpenAI format.

        Returns:
            Number of messages actually saved.
        """
        # Filter: only save user, assistant, and tool messages.
        # System messages contain agent instructions which are ephemeral.
        saveable = [
            m for m in messages if m.get("role") in ("user", "assistant", "tool")
        ]

        if not saveable:
            return 0

        # Convert to ThreadMessage format expected by Go activity.
        thread_messages: List[Dict[str, Any]] = []
        for m in saveable:
            tm: Dict[str, Any] = {
                "role": m["role"],
                "content": m.get("content", ""),
            }
            if m.get("tool_calls"):
                tm["tool_calls"] = m["tool_calls"]
            if m.get("tool_call_id"):
                tm["tool_call_id"] = m["tool_call_id"]
            thread_messages.append(tm)

        result = await workflow.execute_activity(
            "AppendThreadMessagesActivity",
            {
                "thread_id": thread_id,
                "run_id": run_id,
                "messages": thread_messages,
            },
            task_queue=self.task_queue,
            start_to_close_timeout=self.timeout,
            retry_policy=self.retry_policy,
        )
        return result.get("count", 0)
