"""
Tests for Uncertainty Dispatch (WO-ECHO-UNCERTAINTY-DISPATCH)

Tests:
1. Uncertainty detection - keywords, patterns, aggregate confidence
2. Agent registry - registration, lookup, availability
3. Dispatch handler - parallel execution, synthesis, receipts
"""

import asyncio
import pytest
from pathlib import Path
from typing import Any, Dict

# Configure pytest-anyio for async tests - use asyncio backend only
pytest_plugins = ('anyio',)

@pytest.fixture
def anyio_backend():
    return 'asyncio'

# Add project root to path
import sys
PROJECT_ROOT = Path(__file__).parent.parent
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))


# =============================================================================
# Test Uncertainty Detection
# =============================================================================

class TestUncertaintyDetection:
    """Tests for detect_uncertainty_intent() in helpers.py"""

    def test_explicit_temporal_keywords(self):
        """Test detection of temporal uncertainty keywords."""
        from helix_cdc.echo.ops.helpers import detect_uncertainty_intent

        result = detect_uncertainty_intent("What is the latest version of fastapi?")

        assert result["is_uncertainty"] is True
        assert result["uncertainty_type"] == "temporal"
        assert "latest" in result["matched_keywords"]
        assert result["confidence"] > 0.0

    def test_explicit_codebase_keywords(self):
        """Test detection of codebase-related keywords."""
        from helix_cdc.echo.ops.helpers import detect_uncertainty_intent

        result = detect_uncertainty_intent("Which file in this codebase handles routing?")

        assert result["is_uncertainty"] is True
        assert result["uncertainty_type"] == "codebase"
        assert any(kw in result["matched_keywords"] for kw in ["which file", "in this codebase"])

    def test_explicit_external_keywords(self):
        """Test detection of external documentation keywords."""
        from helix_cdc.echo.ops.helpers import detect_uncertainty_intent

        result = detect_uncertainty_intent("What does the pypi documentation say about this?")

        assert result["is_uncertainty"] is True
        assert result["uncertainty_type"] == "external"
        assert "pypi" in result["matched_keywords"] or "documentation" in result["matched_keywords"]

    def test_lookup_patterns(self):
        """Test detection of lookup request patterns."""
        from helix_cdc.echo.ops.helpers import detect_uncertainty_intent

        test_cases = [
            "Look up the installation instructions",
            "Search for error handling examples",
            "What is the current Python version?",
        ]

        for prompt in test_cases:
            result = detect_uncertainty_intent(prompt)
            assert result["is_uncertainty"] is True, f"Failed for: {prompt}"
            assert len(result["matched_patterns"]) > 0 or len(result["matched_keywords"]) >= 2

    def test_low_other_confidence_triggers_uncertainty(self):
        """Test that low confidence from all other detectors triggers uncertainty."""
        from helix_cdc.echo.ops.helpers import detect_uncertainty_intent

        # Query that doesn't match geometry, math, retrieval, or code
        # but has some uncertainty keywords
        result = detect_uncertainty_intent(
            "What is the current status of the project?",
            other_detections={
                "geometry": {"confidence": 0.1, "is_geometry": False},
                "math": {"confidence": 0.0, "is_math": False},
                "retrieval": {"confidence": 0.2, "is_retrieval": False},
                "code": {"confidence": 0.1, "is_code": False},
            }
        )

        # "current" is an uncertainty keyword, should trigger
        assert result["is_uncertainty"] is True
        assert result["aggregate_other_confidence"] < 0.3

    def test_no_uncertainty_for_clear_queries(self):
        """Test that queries with clear intent don't trigger uncertainty."""
        from helix_cdc.echo.ops.helpers import detect_uncertainty_intent

        # Math query - should not be uncertain
        result = detect_uncertainty_intent(
            "Calculate 5 + 3",
            other_detections={
                "geometry": {"confidence": 0.0, "is_geometry": False},
                "math": {"confidence": 0.9, "is_math": True},
                "retrieval": {"confidence": 0.0, "is_retrieval": False},
                "code": {"confidence": 0.0, "is_code": False},
            }
        )

        # Math has high confidence, no uncertainty keywords
        assert result["is_uncertainty"] is False

    def test_suggested_agents(self):
        """Test that correct agents are suggested for each uncertainty type."""
        from helix_cdc.echo.ops.helpers import detect_uncertainty_intent

        # Temporal -> search
        result = detect_uncertainty_intent("What is the latest version?")
        assert "search" in result["suggested_agents"]

        # Codebase -> codebase
        result = detect_uncertainty_intent("Where is this implemented in the code?")
        assert "codebase" in result["suggested_agents"]


# =============================================================================
# Test Agent Registry
# =============================================================================

class TestAgentRegistry:
    """Tests for agent registry in helix_cdc/echo/agents/"""

    def test_registry_imports(self):
        """Test that agent registry imports correctly."""
        from helix_cdc.echo.agents import (
            AGENT_REGISTRY,
            DispatchableAgent,
            AgentResult,
            register_agent,
            get_agents_for_uncertainty,
        )

        assert AGENT_REGISTRY is not None
        assert callable(register_agent)
        assert callable(get_agents_for_uncertainty)

    def test_codebase_agent_registered(self):
        """Test that codebase agent is auto-registered."""
        from helix_cdc.echo.agents import codebase_agent  # noqa: F401
        from helix_cdc.echo.agents import AGENT_REGISTRY

        assert "codebase" in AGENT_REGISTRY

    def test_search_agent_registered(self):
        """Test that search agent is auto-registered."""
        from helix_cdc.echo.agents import search_agent  # noqa: F401
        from helix_cdc.echo.agents import AGENT_REGISTRY

        assert "search" in AGENT_REGISTRY

    def test_get_agents_for_uncertainty_types(self):
        """Test agent mapping for different uncertainty types."""
        from helix_cdc.echo.agents import get_agents_for_uncertainty
        from helix_cdc.echo.agents import codebase_agent  # noqa: F401
        from helix_cdc.echo.agents import search_agent  # noqa: F401

        # Codebase type -> should include codebase agent
        agents = get_agents_for_uncertainty("codebase")
        assert "codebase" in agents

        # Temporal type -> should include search agent
        agents = get_agents_for_uncertainty("temporal")
        assert "search" in agents

    def test_agent_result_to_dict(self):
        """Test AgentResult serialization."""
        from helix_cdc.echo.agents import AgentResult

        result = AgentResult(
            agent_id="test-123",
            agent_type="codebase",
            query="test query",
            result={"data": "value"},
            sources=["file.py:10"],
            confidence=0.8,
            elapsed_ms=100.0,
            error=None,
        )

        d = result.to_dict()
        assert d["agent_id"] == "test-123"
        assert d["agent_type"] == "codebase"
        assert d["confidence"] == 0.8
        assert d["sources"] == ["file.py:10"]


# =============================================================================
# Test Codebase Agent
# =============================================================================

class TestCodebaseAgent:
    """Tests for CodebaseAgent"""

    def test_agent_available(self):
        """Test codebase agent availability check."""
        from helix_cdc.echo.agents.codebase_agent import CodebaseAgent

        agent = CodebaseAgent()
        # Should be available if ripgrep is installed
        available = agent.available()
        assert isinstance(available, bool)

    def test_extract_search_terms(self):
        """Test search term extraction from queries."""
        from helix_cdc.echo.agents.codebase_agent import CodebaseAgent

        agent = CodebaseAgent()

        # Function name should be extracted
        terms = agent._extract_search_terms("Where is detect_uncertainty_intent defined?")
        assert "detect_uncertainty_intent" in terms

        # Quoted strings should be extracted
        terms = agent._extract_search_terms('Search for "TransportMode"')
        assert "TransportMode" in terms

        # CamelCase should be extracted
        terms = agent._extract_search_terms("Find the CodebaseAgent class")
        assert "CodebaseAgent" in terms

    @pytest.mark.anyio
    async def test_execute_returns_agent_result(self):
        """Test that execute returns a valid AgentResult."""
        from helix_cdc.echo.agents.codebase_agent import CodebaseAgent
        from helix_cdc.echo.agents import AgentResult

        agent = CodebaseAgent()
        if not agent.available():
            pytest.skip("ripgrep not available")

        result = await agent.execute(
            "Where is TransportMode defined?",
            {"uncertainty_type": "codebase"}
        )

        assert isinstance(result, AgentResult)
        assert result.agent_type == "codebase"
        assert result.query == "Where is TransportMode defined?"
        assert result.elapsed_ms >= 0


# =============================================================================
# Test Search Agent (Real Implementation - WO-ECHO-SEARCH-AGENT-REAL-01)
# =============================================================================

class TestSearchAgent:
    """Tests for SearchAgent local docs implementation"""

    def test_search_agent_available(self):
        """Test search agent availability checks ripgrep."""
        from helix_cdc.echo.agents.search_agent import SearchAgent

        agent = SearchAgent()
        # Available returns True if ripgrep is installed
        available = agent.available()
        assert isinstance(available, bool)

    @pytest.mark.anyio
    async def test_search_agent_returns_result_or_helpful_error(self):
        """Test that search agent returns result or helpful error."""
        from helix_cdc.echo.agents.search_agent import SearchAgent

        agent = SearchAgent()
        result = await agent.execute("What is the latest Python version?", {})

        assert result.agent_type == "search"
        # Either finds docs and returns results, or gives helpful error
        if result.error:
            # Should mention how to get docs (no longer returns "UNIMPLEMENTED")
            assert "build_docs_index.py" in result.error or "No documentation" in result.error or "No matches" in result.error


# =============================================================================
# Test Dispatch Handler Integration
# =============================================================================

class TestDispatchHandlerIntegration:
    """Integration tests for _handle_uncertainty_dispatch"""

    @pytest.mark.anyio
    async def test_handler_creates_receipt(self):
        """Test that handler creates a receipt file."""
        import tempfile
        import shutil

        # Import handler
        from api.basin_server import _handle_uncertainty_dispatch
        from pydantic import BaseModel, Field
        from typing import List

        # Create minimal request mock
        class MockMessage(BaseModel):
            role: str = "user"
            content: str = "test"

        class MockQuantParams(BaseModel):
            phi0_deg: float = 0.0
            cardinality: int = 2
            control_consent: bool = True

        class MockRequest(BaseModel):
            model: str = "test"
            messages: List[MockMessage] = [MockMessage()]
            temperature: float = 0.0
            top_p: float = 1.0
            max_tokens: int = 128
            quant: MockQuantParams = MockQuantParams()

        # Create uncertainty result
        uncertainty_result = {
            "is_uncertain": True,
            "uncertainty_type": "codebase",
            "matched_keywords": ["where is"],
            "matched_patterns": [],
            "confidence": 0.5,
            "aggregate_other_confidence": 0.1,
            "suggested_agents": ["codebase"],
        }

        request = MockRequest()
        raw_prompt = "Where is the routing code?"

        # Run handler
        response = await _handle_uncertainty_dispatch(request, raw_prompt, uncertainty_result)

        # Check response structure
        assert response.id.startswith("chatcmpl-uncertainty-")
        assert response.backend_used == "uncertainty_dispatcher"
        assert len(response.choices) == 1
        assert response.choices[0]["message"]["role"] == "assistant"

        # Check basin_metadata
        metadata = response.choices[0].get("basin_metadata", {})
        assert metadata.get("transport_mode") == "uncertainty_dispatch"
        assert "agents_dispatched" in metadata

    def test_transport_policy_registered(self):
        """Test that UNCERTAINTY_DISPATCH is in transport policies."""
        from helix_cdc.echo.ops.transport_policy import (
            TransportMode,
            TRANSPORT_POLICIES,
            get_tool_routed_policies,
        )

        assert TransportMode.UNCERTAINTY_DISPATCH in TRANSPORT_POLICIES

        policy = TRANSPORT_POLICIES[TransportMode.UNCERTAINTY_DISPATCH]
        assert policy.priority == 5  # First priority
        assert policy.tool_name == "uncertainty_dispatcher"
        assert policy.work_order == "WO-ECHO-UNCERTAINTY-DISPATCH"

    def test_uncertainty_dispatch_checked_first(self):
        """Test that uncertainty dispatch has highest priority in routing."""
        from helix_cdc.echo.ops.transport_policy import get_tool_routed_policies

        policies = get_tool_routed_policies()
        assert len(policies) > 0

        # First policy should be uncertainty dispatch
        first_policy = policies[0]
        assert first_policy.mode.value == "uncertainty_dispatch"
        assert first_policy.priority == 5


# =============================================================================
# Test Receipt Model
# =============================================================================

class TestUncertaintyDispatchReceipt:
    """Tests for UncertaintyDispatchReceipt model"""

    def test_receipt_model_validates(self):
        """Test that receipt model validates correctly."""
        from helix_cdc.echo.ops.models import UncertaintyDispatchReceipt, AgentResultModel

        receipt = UncertaintyDispatchReceipt(
            receipt_id="test-123",
            timestamp_utc="2026-02-02T10:00:00Z",
            prompt="test prompt",
            prompt_sha256="abc123" * 10 + "ab",  # 64 chars
            uncertainty_type="codebase",
            aggregate_confidence=0.2,
            agents_dispatched=["codebase"],
            agent_results=[
                AgentResultModel(
                    agent_id="agent-1",
                    agent_type="codebase",
                    query="test",
                    result={"matches": []},
                    confidence=0.5,
                    elapsed_ms=100.0,
                )
            ],
            answer="Test answer",
            answer_sha256="def456" * 10 + "de",  # 64 chars
            sources_cited=["file.py:10"],
            chain_hash="hash" * 16,  # 64 chars
            status="PASS",
        )

        assert receipt.receipt_id == "test-123"
        assert receipt.uncertainty_type == "codebase"
        assert len(receipt.agent_results) == 1
        assert receipt.status == "PASS"

    def test_receipt_status_options(self):
        """Test that receipt supports all status options."""
        from helix_cdc.echo.ops.models import UncertaintyDispatchReceipt

        for status in ["PASS", "PARTIAL", "UNIMPLEMENTED"]:
            receipt = UncertaintyDispatchReceipt(
                receipt_id="test",
                timestamp_utc="2026-02-02T10:00:00Z",
                prompt="test",
                prompt_sha256="a" * 64,
                uncertainty_type="general",
                aggregate_confidence=0.1,
                agents_dispatched=[],
                answer="test",
                answer_sha256="b" * 64,
                chain_hash="c" * 64,
                status=status,
            )
            assert receipt.status == status


# =============================================================================
# Run tests
# =============================================================================

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