"""Tests for the session module."""

from datetime import datetime

from oclawma.session import (
    ConversationHistory,
    InteractiveSession,
    SessionExitError,
    SessionMessage,
    SessionStats,
)


class TestSessionMessage:
    """Test the SessionMessage dataclass."""

    def test_create_message(self):
        """Test creating a basic message."""
        msg = SessionMessage(role="user", content="Hello")

        assert msg.role == "user"
        assert msg.content == "Hello"
        assert isinstance(msg.timestamp, datetime)
        assert msg.metadata == {}

    def test_message_with_metadata(self):
        """Test creating a message with metadata."""
        msg = SessionMessage(
            role="assistant",
            content="Hi",
            metadata={"tool": "test", "result": "success"},
        )

        assert msg.metadata == {"tool": "test", "result": "success"}

    def test_to_dict_basic(self):
        """Test converting message to dict."""
        msg = SessionMessage(role="user", content="Hello")
        d = msg.to_dict()

        assert d == {"role": "user", "content": "Hello"}

    def test_to_dict_with_name(self):
        """Test converting message with name to dict."""
        msg = SessionMessage(
            role="assistant",
            content="Hello",
            metadata={"name": "gpt"},
        )
        d = msg.to_dict()

        assert d == {"role": "assistant", "content": "Hello", "name": "gpt"}


class TestSessionStats:
    """Test the SessionStats dataclass."""

    def test_create_stats(self):
        """Test creating session stats."""
        start = datetime.now()
        stats = SessionStats(start_time=start)

        assert stats.start_time == start
        assert stats.end_time is None
        assert stats.message_count == 0
        assert stats.user_messages == 0
        assert stats.assistant_messages == 0
        assert stats.tool_calls == 0
        assert stats.tokens_used == 0
        assert stats.tool_results == []


class TestConversationHistory:
    """Test the ConversationHistory class."""

    def test_create_history(self):
        """Test creating an empty history."""
        history = ConversationHistory()

        assert len(history) == 0
        assert history.get_messages() == []

    def test_add_message(self):
        """Test adding a message to history."""
        history = ConversationHistory()
        msg = history.add_message(role="user", content="Hello")

        assert len(history) == 1
        assert msg.role == "user"
        assert msg.content == "Hello"

    def test_add_multiple_messages(self):
        """Test adding multiple messages."""
        history = ConversationHistory()
        history.add_message(role="user", content="Hello")
        history.add_message(role="assistant", content="Hi there!")

        assert len(history) == 2

    def test_get_messages(self):
        """Test getting all messages."""
        history = ConversationHistory()
        history.add_message(role="system", content="System prompt")
        history.add_message(role="user", content="Hello")

        messages = history.get_messages()
        assert len(messages) == 2

        # Test excluding system messages
        messages_no_system = history.get_messages(include_system=False)
        assert len(messages_no_system) == 1
        assert messages_no_system[0].role == "user"

    def test_get_recent(self):
        """Test getting recent messages."""
        history = ConversationHistory()
        for i in range(5):
            history.add_message(role="user", content=f"Message {i}")

        recent = history.get_recent(3)
        assert len(recent) == 3
        assert recent[-1].content == "Message 4"

    def test_get_recent_more_than_exists(self):
        """Test getting more recent messages than exist."""
        history = ConversationHistory()
        history.add_message(role="user", content="Hello")

        recent = history.get_recent(10)
        assert len(recent) == 1

    def test_to_llm_messages(self):
        """Test converting to LLM message format."""
        history = ConversationHistory()
        history.add_message(role="system", content="System")
        history.add_message(role="user", content="Hello")

        llm_msgs = history.to_llm_messages()
        assert len(llm_msgs) == 2
        assert llm_msgs[0] == {"role": "system", "content": "System"}
        assert llm_msgs[1] == {"role": "user", "content": "Hello"}

    def test_clear(self):
        """Test clearing history."""
        history = ConversationHistory()
        history.add_message(role="user", content="Hello")
        history.clear()

        assert len(history) == 0

    def test_set_system_message(self):
        """Test setting system message."""
        history = ConversationHistory()
        history.add_message(role="user", content="Hello")
        history.set_system_message("System prompt")

        messages = history.get_messages()
        assert messages[0].role == "system"
        assert messages[0].content == "System prompt"
        assert messages[1].role == "user"

    def test_set_system_message_replaces_existing(self):
        """Test that setting system message replaces existing one."""
        history = ConversationHistory()
        history.set_system_message("Old prompt")
        history.add_message(role="user", content="Hello")
        history.set_system_message("New prompt")

        system_msgs = [m for m in history.get_messages() if m.role == "system"]
        assert len(system_msgs) == 1
        assert system_msgs[0].content == "New prompt"

    def test_estimate_tokens_empty(self):
        """Test token estimation on empty history."""
        history = ConversationHistory()
        assert history.estimate_tokens() == 0

    def test_estimate_tokens(self):
        """Test token estimation with messages."""
        history = ConversationHistory()
        history.add_message(role="user", content="Hello world")  # ~11 chars

        # ~11 chars / 4 + 4 overhead = ~7 tokens
        tokens = history.estimate_tokens()
        assert tokens > 0

    def test_max_messages_limit(self):
        """Test that history respects max messages limit."""
        history = ConversationHistory(max_messages=3)
        history.add_message(role="system", content="System")
        history.add_message(role="user", content="Hello 1")
        history.add_message(role="user", content="Hello 2")
        history.add_message(role="user", content="Hello 3")

        # Should keep system message and 2 most recent
        assert len(history) == 3
        assert history.get_messages()[0].role == "system"


class TestSessionExitError:
    """Test the SessionExitError exception."""

    def test_default_message(self):
        """Test default exit message."""
        exc = SessionExitError()
        assert exc.message == "Session ended"

    def test_custom_message(self):
        """Test custom exit message."""
        exc = SessionExitError("User quit")
        assert exc.message == "User quit"


class TestInteractiveSession:
    """Test the InteractiveSession class."""

    def test_create_session(self):
        """Test creating an interactive session."""
        session = InteractiveSession()

        assert session.model == "qwen2.5:3b"
        assert session.history is not None
        assert session.budget is not None
        assert session.tool_registry is not None

    def test_create_session_with_custom_model(self):
        """Test creating session with custom model."""
        session = InteractiveSession(model="llama3.2:latest")
        assert session.model == "llama3.2:latest"

    def test_system_prompt_setup(self):
        """Test that system prompt is set up on initialization."""
        session = InteractiveSession()

        messages = session.history.get_messages()
        assert len(messages) >= 1
        assert messages[0].role == "system"
        assert "OCLAWMA" in messages[0].content

    def test_exit_commands(self):
        """Test that exit commands are recognized."""
        session = InteractiveSession()

        assert "exit" in session.EXIT_COMMANDS
        assert "quit" in session.EXIT_COMMANDS
        assert "/exit" in session.EXIT_COMMANDS
        assert "/quit" in session.EXIT_COMMANDS

    def test_stats_tracking(self):
        """Test that session tracks stats."""
        session = InteractiveSession()

        assert session.stats.start_time is not None
        assert session.stats.message_count == 0
