"""
Tests for KivotSession.

Follows the same pattern as runner_test.py: pytest with async,
mock workflow.execute_activity.
"""

import pytest
from datetime import timedelta
from unittest.mock import patch

from temporalio.common import RetryPolicy

from .session import KivotSession


class TestKivotSessionLoadHistory:
    """Test KivotSession.load_history()."""

    @pytest.mark.asyncio
    @patch("kivot_agents.session.workflow.execute_activity")
    async def test_load_history_returns_messages(self, mock_execute):
        """load_history should return messages from GetThreadMessagesActivity."""
        async def mock_activity(name, *args, **kwargs):
            return {
                "thread_id": "thread-1",
                "messages": [
                    {"role": "user", "content": "Hello"},
                    {"role": "assistant", "content": "Hi there!"},
                ],
                "count": 2,
            }

        mock_execute.side_effect = mock_activity

        session = KivotSession(task_queue="test-queue")
        messages = await session.load_history("thread-1")

        assert len(messages) == 2
        assert messages[0]["role"] == "user"
        assert messages[0]["content"] == "Hello"
        assert messages[1]["role"] == "assistant"
        assert messages[1]["content"] == "Hi there!"

        # Verify activity was called correctly
        mock_execute.assert_called_once()
        call_args = mock_execute.call_args
        assert call_args[0][0] == "GetThreadMessagesActivity"
        assert call_args[0][1] == {"thread_id": "thread-1", "limit": 0}
        assert call_args[1]["task_queue"] == "test-queue"

    @pytest.mark.asyncio
    @patch("kivot_agents.session.workflow.execute_activity")
    async def test_load_history_empty(self, mock_execute):
        """load_history on empty thread should return empty list."""
        async def mock_activity(name, *args, **kwargs):
            return {
                "thread_id": "thread-empty",
                "messages": [],
                "count": 0,
            }

        mock_execute.side_effect = mock_activity

        session = KivotSession()
        messages = await session.load_history("thread-empty")

        assert messages == []

    @pytest.mark.asyncio
    @patch("kivot_agents.session.workflow.execute_activity")
    async def test_load_history_with_limit(self, mock_execute):
        """load_history should pass limit to the activity."""
        async def mock_activity(name, *args, **kwargs):
            return {"thread_id": "thread-1", "messages": [], "count": 0}

        mock_execute.side_effect = mock_activity

        session = KivotSession()
        await session.load_history("thread-1", limit=25)

        call_args = mock_execute.call_args
        assert call_args[0][1]["limit"] == 25


class TestKivotSessionSaveMessages:
    """Test KivotSession.save_messages()."""

    @pytest.mark.asyncio
    @patch("kivot_agents.session.workflow.execute_activity")
    async def test_save_messages_filters_system(self, mock_execute):
        """save_messages should filter out system messages."""
        async def mock_activity(name, *args, **kwargs):
            return {"count": 2}

        mock_execute.side_effect = mock_activity

        session = KivotSession()
        messages = [
            {"role": "system", "content": "You are helpful."},
            {"role": "user", "content": "Hello"},
            {"role": "assistant", "content": "Hi!"},
        ]

        saved = await session.save_messages("thread-1", "run-1", messages)

        assert saved == 2
        # Verify only user + assistant were sent
        call_args = mock_execute.call_args
        sent_messages = call_args[0][1]["messages"]
        assert len(sent_messages) == 2
        assert sent_messages[0]["role"] == "user"
        assert sent_messages[1]["role"] == "assistant"

    @pytest.mark.asyncio
    @patch("kivot_agents.session.workflow.execute_activity")
    async def test_save_messages_empty_after_filter(self, mock_execute):
        """save_messages with only system messages should save nothing."""
        session = KivotSession()
        messages = [
            {"role": "system", "content": "You are helpful."},
        ]

        saved = await session.save_messages("thread-1", "run-1", messages)

        assert saved == 0
        mock_execute.assert_not_called()

    @pytest.mark.asyncio
    @patch("kivot_agents.session.workflow.execute_activity")
    async def test_save_messages_preserves_tool_calls(self, mock_execute):
        """save_messages should preserve tool_calls and tool_call_id."""
        async def mock_activity(name, *args, **kwargs):
            return {"count": 2}

        mock_execute.side_effect = mock_activity

        session = KivotSession()
        messages = [
            {
                "role": "assistant",
                "content": "",
                "tool_calls": [
                    {"id": "call-1", "name": "search", "arguments": '{"q": "test"}'}
                ],
            },
            {
                "role": "tool",
                "content": "search result",
                "tool_call_id": "call-1",
            },
        ]

        await session.save_messages("thread-1", "run-1", messages)

        call_args = mock_execute.call_args
        sent_messages = call_args[0][1]["messages"]
        assert len(sent_messages) == 2
        assert sent_messages[0]["tool_calls"] == messages[0]["tool_calls"]
        assert sent_messages[1]["tool_call_id"] == "call-1"


class TestKivotSessionCreateThread:
    """Test KivotSession.create_thread()."""

    @pytest.mark.asyncio
    @patch("kivot_agents.session.workflow.execute_activity")
    async def test_create_thread(self, mock_execute):
        """create_thread should call CreateThreadActivity correctly."""
        async def mock_activity(name, *args, **kwargs):
            return {"thread_id": "thread-new", "created": True}

        mock_execute.side_effect = mock_activity

        session = KivotSession(task_queue="my-queue")
        result = await session.create_thread(
            "thread-new", agent_id="agent-1", title="Test"
        )

        assert result["thread_id"] == "thread-new"
        assert result["created"] is True

        call_args = mock_execute.call_args
        assert call_args[0][0] == "CreateThreadActivity"
        assert call_args[0][1] == {
            "thread_id": "thread-new",
            "agent_id": "agent-1",
            "title": "Test",
        }
        assert call_args[1]["task_queue"] == "my-queue"


class TestKivotSessionConfig:
    """Test KivotSession configuration."""

    def test_default_config(self):
        """Session should have sensible defaults."""
        session = KivotSession()
        assert session.task_queue == "core-activities"
        assert session.timeout == timedelta(seconds=10)
        assert session.retry_policy.maximum_attempts == 3

    def test_custom_config(self):
        """Session should accept custom configuration."""
        custom_retry = RetryPolicy(
            maximum_attempts=5,
            initial_interval=timedelta(seconds=2),
        )
        session = KivotSession(
            task_queue="custom-queue",
            timeout=timedelta(seconds=30),
            retry_policy=custom_retry,
        )
        assert session.task_queue == "custom-queue"
        assert session.timeout == timedelta(seconds=30)
        assert session.retry_policy.maximum_attempts == 5


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
