"""
Unit tests for the adapter layer.
"""

import pytest
from datetime import datetime

from agent_orchestrator.adapters.base import (
    RiskLevel,
    AgentStatus,
    AgentResponse,
    UsageStats,
    BaseAdapter,
    LLMAdapter,
    CLIAgentAdapter,
)
from agent_orchestrator.journal.status_packet import TaskArtifacts


class TestRiskLevel:
    """Tests for RiskLevel enum."""

    def test_risk_levels_exist(self):
        """Test all risk levels are defined."""
        assert RiskLevel.LOW.value == "low"
        assert RiskLevel.MEDIUM.value == "medium"
        assert RiskLevel.HIGH.value == "high"
        assert RiskLevel.CRITICAL.value == "critical"


class TestAgentStatus:
    """Tests for AgentStatus enum."""

    def test_status_values(self):
        """Test agent status values."""
        assert AgentStatus.IDLE.value == "idle"
        assert AgentStatus.RUNNING.value == "running"
        assert AgentStatus.STUCK.value == "stuck"
        assert AgentStatus.PAUSED.value == "paused"
        assert AgentStatus.TERMINATED.value == "terminated"


class TestAgentResponse:
    """Tests for AgentResponse dataclass."""

    def test_default_values(self):
        """Test default response values."""
        response = AgentResponse(content="Test output")

        assert response.content == "Test output"
        assert response.tokens_used == 0
        assert response.cost == 0.0
        assert response.success is True
        assert response.error is None

    def test_total_tokens(self):
        """Test total tokens calculation."""
        response = AgentResponse(
            content="Test",
            tokens_input=100,
            tokens_output=50,
        )
        assert response.total_tokens() == 150

        # Also respects tokens_used if set directly
        response2 = AgentResponse(content="Test", tokens_used=200)
        assert response2.total_tokens() == 200

    def test_failed_response(self):
        """Test creating a failed response."""
        response = AgentResponse(
            content="",
            success=False,
            error="Connection timeout",
        )

        assert response.success is False
        assert response.error == "Connection timeout"


class TestUsageStats:
    """Tests for UsageStats dataclass."""

    def test_default_values(self):
        """Test default usage values."""
        stats = UsageStats()

        assert stats.tokens_input == 0
        assert stats.tokens_output == 0
        assert stats.total_cost == 0.0
        assert stats.requests_count == 0
        assert stats.errors_count == 0

    def test_total_tokens(self):
        """Test total tokens calculation."""
        stats = UsageStats(tokens_input=1000, tokens_output=500)
        assert stats.total_tokens() == 1500

    def test_add_response(self):
        """Test adding a response to stats."""
        stats = UsageStats()

        response = AgentResponse(
            content="Test",
            tokens_input=100,
            tokens_output=50,
            cost=0.01,
            success=True,
        )
        stats.add_response(response)

        assert stats.tokens_input == 100
        assert stats.tokens_output == 50
        assert stats.total_cost == 0.01
        assert stats.requests_count == 1
        assert stats.errors_count == 0

        # Add failed response
        failed = AgentResponse(content="", success=False, tokens_input=10, tokens_output=5)
        stats.add_response(failed)

        assert stats.requests_count == 2
        assert stats.errors_count == 1

    def test_error_rate(self):
        """Test error rate calculation."""
        stats = UsageStats(requests_count=10, errors_count=2)
        assert stats.error_rate() == 20.0

        empty = UsageStats()
        assert empty.error_rate() == 0.0


class MockLLMAdapter(LLMAdapter):
    """Mock LLM adapter for testing."""

    async def execute(self, task, context):
        return AgentResponse(
            content=f"Executed: {task}",
            tokens_input=10,
            tokens_output=20,
            cost=0.001,
        )

    async def stream(self, task, context):
        yield f"Streaming: {task}"


class MockCLIAdapter(CLIAgentAdapter):
    """Mock CLI adapter for testing."""

    async def execute(self, task, context):
        return AgentResponse(
            content=f"CLI executed: {task}",
            tokens_input=50,
            tokens_output=100,
        )

    async def stream(self, task, context):
        yield f"CLI streaming: {task}"


class TestLLMAdapter:
    """Tests for LLMAdapter base class."""

    def test_initialization(self):
        """Test adapter initialization."""
        adapter = MockLLMAdapter("test-agent", "gpt-4")

        assert adapter.agent_id == "test-agent"
        assert adapter.model == "gpt-4"
        assert adapter.status == AgentStatus.IDLE

    def test_get_usage(self):
        """Test getting usage stats."""
        adapter = MockLLMAdapter("test-agent", "gpt-4")
        stats = adapter.get_usage()

        assert isinstance(stats, UsageStats)
        assert stats.requests_count == 0

    def test_is_healthy(self):
        """Test health check."""
        adapter = MockLLMAdapter("test-agent", "gpt-4")
        assert adapter.is_healthy() is True

        adapter._status = AgentStatus.TERMINATED
        assert adapter.is_healthy() is False

    def test_set_task(self):
        """Test setting current task."""
        adapter = MockLLMAdapter("test-agent", "gpt-4")
        adapter.set_task("task-123")

        assert adapter._current_task_id == "task-123"
        assert adapter.status == AgentStatus.RUNNING

    def test_clear_task(self):
        """Test clearing current task."""
        adapter = MockLLMAdapter("test-agent", "gpt-4")
        adapter.set_task("task-123")
        adapter.clear_task()

        assert adapter._current_task_id is None
        assert adapter.status == AgentStatus.IDLE

    def test_write_status_packet(self):
        """Test writing status packet."""
        adapter = MockLLMAdapter("test-agent", "gpt-4")
        packet = adapter.write_status_packet()

        assert packet.agent_id == "test-agent"
        assert packet.status in ("completed", "idle", "running")


class TestCLIAgentAdapter:
    """Tests for CLIAgentAdapter base class."""

    def test_initialization(self):
        """Test adapter initialization."""
        adapter = MockCLIAdapter("cli-agent", workspace_path="/tmp/workspace")

        assert adapter.agent_id == "cli-agent"
        assert adapter.workspace_path == "/tmp/workspace"

    def test_inject_context_empty(self):
        """Test context injection with empty context."""
        adapter = MockCLIAdapter("cli-agent")
        result = adapter.inject_context("Do something", {})

        assert "Do something" in result
        assert "Your Task" in result

    def test_inject_context_with_state(self):
        """Test context injection with project state."""
        adapter = MockCLIAdapter("cli-agent")
        context = {
            "project_state": {"version": "1.0"},
            "constraints": ["No database changes"],
            "recent_decisions": ["Use JWT"],
        }
        result = adapter.inject_context("Implement auth", context)

        assert "Current Project State" in result
        assert "Constraints" in result
        assert "Recent Decisions" in result
        assert "No database changes" in result
        assert "Use JWT" in result

    def test_is_healthy(self):
        """Test health check."""
        adapter = MockCLIAdapter("cli-agent")
        assert adapter.is_healthy() is True

        adapter._status = AgentStatus.STUCK
        assert adapter.is_healthy() is False


class TestAdapterCancellation:
    """Tests for adapter cancellation."""

    @pytest.mark.asyncio
    async def test_cancel(self):
        """Test cancelling an adapter."""
        adapter = MockLLMAdapter("test-agent", "gpt-4")
        adapter._status = AgentStatus.RUNNING

        result = await adapter.cancel()

        assert result is True
        assert adapter.status == AgentStatus.IDLE
