"""
Tests for Agentbyte root types.

Run with: uv run pytest tests/test_types.py -v
"""

import time

import pytest

from agentbyte.agents.types import Usage
from agentbyte.messages import UserMessage
from agentbyte.types import (
    AgentExecutionCompleteEvent,
    AgentExecutionStartEvent,
    AgentSelectionEvent,
    OrchestrationCompleteEvent,
    OrchestrationEvent,
    OrchestrationResponse,
    OrchestrationStartEvent,
    StopMessage,
    ToolResult,
)


class TestToolResult:
    """Test ToolResult class functionality."""

    def test_tool_result_success(self):
        """Test creating a successful ToolResult."""
        result = ToolResult(
            success=True,
            result="data",
            error=None,
            metadata={"tool": "test"},
        )
        assert result.success is True
        assert result.result == "data"
        assert result.error is None
        assert result.metadata == {"tool": "test"}

    def test_tool_result_failure(self):
        """Test creating a failed ToolResult."""
        result = ToolResult(
            success=False,
            result=None,
            error="Something went wrong",
            metadata={},
        )
        assert result.success is False
        assert result.result is None
        assert result.error == "Something went wrong"

    def test_tool_result_immutability(self):
        """Test that ToolResult is immutable (frozen)."""
        result = ToolResult(
            success=True,
            result="data",
        )

        with pytest.raises(Exception):
            result.success = False

    def test_tool_result_metadata_default(self):
        """Test that metadata defaults to empty dict."""
        result = ToolResult(success=True, result="data")
        assert result.metadata == {}


class TestStopMessage:
    """Test StopMessage class functionality."""

    def test_stop_message_creation(self):
        """Test creating a StopMessage."""
        stop = StopMessage(
            content="Maximum messages reached",
            source="MaxMessageTermination",
            metadata={"count": 10},
        )
        assert stop.content == "Maximum messages reached"
        assert stop.source == "MaxMessageTermination"
        assert stop.metadata == {"count": 10}

    def test_stop_message_immutability(self):
        """Test that StopMessage is immutable."""
        stop = StopMessage(content="Done", source="Test", metadata={})
        with pytest.raises(Exception):
            stop.content = "New content"

    def test_stop_message_metadata_default(self):
        """Test that metadata defaults to empty dict."""
        stop = StopMessage(content="Done", source="Test")
        assert stop.metadata == {}


class TestOrchestrationResponse:
    """Test OrchestrationResponse class functionality."""

    def test_orchestration_response_creation(self):
        """Test creating an OrchestrationResponse."""
        messages = [UserMessage(content="Test", source="user")]
        usage = Usage(duration_ms=1000, llm_calls=2)
        stop = StopMessage(content="Done", source="Test")

        response = OrchestrationResponse(
            messages=messages,
            final_result="Task completed",
            usage=usage,
            stop_message=stop,
            pattern_metadata={"cycles": 2},
        )

        assert len(response.messages) == 1
        assert response.final_result == "Task completed"
        assert response.usage.duration_ms == 1000
        assert response.stop_message.content == "Done"
        assert response.pattern_metadata == {"cycles": 2}

    def test_orchestration_response_metadata_default(self):
        """Test that pattern_metadata defaults to empty dict."""
        response = OrchestrationResponse(
            messages=[],
            final_result="Done",
            usage=Usage(),
            stop_message=StopMessage(content="Done", source="Test"),
        )
        assert response.pattern_metadata == {}

    def test_orchestration_response_mutability(self):
        """Test that OrchestrationResponse is mutable (not frozen)."""
        response = OrchestrationResponse(
            messages=[],
            final_result="Done",
            usage=Usage(),
            stop_message=StopMessage(content="Done", source="Test"),
        )
        # Should be able to modify since frozen=False
        response.final_result = "Updated"
        assert response.final_result == "Updated"


class TestOrchestrationEvents:
    """Test orchestration event classes."""

    def test_orchestration_event_base(self):
        """Test base OrchestrationEvent."""
        event = OrchestrationEvent()
        assert event.source == "orchestrator"
        assert isinstance(event.timestamp, float)
        assert event.timestamp > 0

    def test_orchestration_start_event(self):
        """Test OrchestrationStartEvent."""
        event = OrchestrationStartEvent(
            task="Write a haiku", pattern="RoundRobinOrchestrator"
        )
        assert event.task == "Write a haiku"
        assert event.pattern == "RoundRobinOrchestrator"
        assert event.source == "orchestrator"

    def test_orchestration_complete_event(self):
        """Test OrchestrationCompleteEvent."""
        event = OrchestrationCompleteEvent(
            result="Task completed", stop_reason="Maximum iterations"
        )
        assert event.result == "Task completed"
        assert event.stop_reason == "Maximum iterations"

    def test_agent_selection_event(self):
        """Test AgentSelectionEvent."""
        event = AgentSelectionEvent(
            selected_agent="writer", selection_reason="Iteration 3"
        )
        assert event.selected_agent == "writer"
        assert event.selection_reason == "Iteration 3"

    def test_agent_execution_start_event(self):
        """Test AgentExecutionStartEvent."""
        event = AgentExecutionStartEvent(executing_agent="researcher", context_size=5)
        assert event.executing_agent == "researcher"
        assert event.context_size == 5

    def test_agent_execution_complete_event(self):
        """Test AgentExecutionCompleteEvent."""
        event = AgentExecutionCompleteEvent(
            executing_agent="critic", success=True, message_count=3
        )
        assert event.executing_agent == "critic"
        assert event.success is True
        assert event.message_count == 3

    def test_event_immutability(self):
        """Test that events are immutable."""
        event = OrchestrationStartEvent(task="Test", pattern="Test")
        with pytest.raises(Exception):
            event.task = "New task"

    def test_event_timestamp_default(self):
        """Test that timestamp is auto-generated."""
        before = time.time()
        event = OrchestrationEvent()
        after = time.time()

        assert before <= event.timestamp <= after

    def test_event_serialization(self):
        """Test that events can be serialized."""
        event = AgentSelectionEvent(selected_agent="test", selection_reason="testing")

        # Should be able to convert to dict and back
        data = event.model_dump()
        assert data["selected_agent"] == "test"
        assert data["selection_reason"] == "testing"
        assert "timestamp" in data

        # Should be able to reconstruct
        reconstructed = AgentSelectionEvent(**data)
        assert reconstructed.selected_agent == event.selected_agent
