"""
Unit tests for the Agent Capability Registry.

Tests:
- Capability enumeration
- Agent capability registration
- Capability-based lookup
- Agent ranking and scoring
"""

import pytest
from agent_orchestrator.plugins.capabilities import (
    AgentCapability,
    AgentCapabilities,
    CapabilityScore,
    CapabilityRegistry,
)


class TestAgentCapability:
    """Tests for the AgentCapability enum."""

    def test_capability_values(self):
        """Test standard capability values."""
        assert AgentCapability.CODE_EDIT.value == "code_edit"
        assert AgentCapability.GIT.value == "git"
        assert AgentCapability.TERMINAL.value == "terminal"
        assert AgentCapability.DEPLOY.value == "deploy"

    def test_all_capabilities_exist(self):
        """Test that all expected capabilities are defined."""
        expected = [
            "FILE_READ", "FILE_WRITE", "FILE_DELETE",
            "CODE_EDIT", "CODE_REVIEW", "CODE_REFACTOR", "CODE_GENERATE",
            "RUN_TESTS", "DEBUG",
            "GIT", "GIT_COMMIT", "GIT_MERGE",
            "TERMINAL", "SHELL_COMMAND",
            "DEPLOY", "DOCKER", "KUBERNETES",
            "SEARCH", "WEB_BROWSE",
            "STREAMING", "FUNCTION_CALLING",
            "FAST", "LARGE_CONTEXT", "AUTONOMOUS",
        ]
        for cap_name in expected:
            assert hasattr(AgentCapability, cap_name)


class TestCapabilityScore:
    """Tests for the CapabilityScore dataclass."""

    def test_score_creation(self):
        """Test creating a capability score."""
        score = CapabilityScore(
            capability="code_edit",
            score=0.9,
            notes="Expert level",
        )
        assert score.capability == "code_edit"
        assert score.score == 0.9
        assert score.notes == "Expert level"

    def test_score_to_dict(self):
        """Test converting score to dictionary."""
        score = CapabilityScore(
            capability="git",
            score=0.8,
            notes="Proficient",
        )
        data = score.to_dict()
        assert data["capability"] == "git"
        assert data["score"] == 0.8
        assert data["notes"] == "Proficient"


class TestAgentCapabilities:
    """Tests for the AgentCapabilities dataclass."""

    def test_capabilities_creation(self):
        """Test creating agent capabilities."""
        caps = AgentCapabilities(agent_id="agent-1")
        assert caps.agent_id == "agent-1"
        assert len(caps.capabilities) == 0

    def test_add_capability_enum(self):
        """Test adding capability using enum."""
        caps = AgentCapabilities(agent_id="agent-1")
        caps.add_capability(AgentCapability.CODE_EDIT, score=0.9)

        assert caps.has_capability(AgentCapability.CODE_EDIT)
        assert caps.get_score(AgentCapability.CODE_EDIT) == 0.9

    def test_add_capability_string(self):
        """Test adding capability using string."""
        caps = AgentCapabilities(agent_id="agent-1")
        caps.add_capability("custom_capability", score=0.7)

        assert caps.has_capability("custom_capability")
        assert caps.get_score("custom_capability") == 0.7

    def test_remove_capability(self):
        """Test removing a capability."""
        caps = AgentCapabilities(agent_id="agent-1")
        caps.add_capability(AgentCapability.GIT)
        assert caps.has_capability(AgentCapability.GIT)

        caps.remove_capability(AgentCapability.GIT)
        assert not caps.has_capability(AgentCapability.GIT)

    def test_get_score_default(self):
        """Test default scores for capabilities."""
        caps = AgentCapabilities(agent_id="agent-1")
        caps.capabilities.add("basic_cap")  # Add without score

        # Present capability without explicit score returns 1.0
        assert caps.get_score("basic_cap") == 1.0

        # Missing capability returns 0.0
        assert caps.get_score("missing_cap") == 0.0

    def test_to_dict(self):
        """Test converting capabilities to dictionary."""
        caps = AgentCapabilities(agent_id="agent-1")
        caps.add_capability(AgentCapability.CODE_EDIT, score=0.9)
        caps.metadata["custom"] = "value"

        data = caps.to_dict()
        assert data["agent_id"] == "agent-1"
        assert "code_edit" in data["capabilities"]
        assert data["metadata"]["custom"] == "value"

    def test_from_dict(self):
        """Test creating capabilities from dictionary."""
        data = {
            "agent_id": "agent-1",
            "capabilities": ["code_edit", "git"],
            "scores": {
                "code_edit": {
                    "capability": "code_edit",
                    "score": 0.9,
                    "notes": "Expert",
                }
            },
            "metadata": {"type": "cli"},
        }

        caps = AgentCapabilities.from_dict(data)
        assert caps.agent_id == "agent-1"
        assert caps.has_capability("code_edit")
        assert caps.has_capability("git")
        assert caps.get_score("code_edit") == 0.9
        assert caps.metadata["type"] == "cli"


class TestCapabilityRegistry:
    """Tests for the CapabilityRegistry class."""

    def test_registry_init(self):
        """Test initializing the registry."""
        registry = CapabilityRegistry()
        assert len(registry.list_agents()) == 0

    def test_register_agent(self):
        """Test registering an agent with capabilities."""
        registry = CapabilityRegistry()

        agent_caps = registry.register(
            "claude-code",
            [AgentCapability.CODE_EDIT, AgentCapability.GIT],
        )

        assert agent_caps.agent_id == "claude-code"
        assert agent_caps.has_capability(AgentCapability.CODE_EDIT)

    def test_register_with_scores(self):
        """Test registering with capability scores."""
        registry = CapabilityRegistry()

        registry.register(
            "claude-code",
            [AgentCapability.CODE_EDIT, AgentCapability.GIT],
            scores={"code_edit": 0.95, "git": 0.8},
        )

        caps = registry.get_capabilities("claude-code")
        assert caps.get_score("code_edit") == 0.95
        assert caps.get_score("git") == 0.8

    def test_register_with_metadata(self):
        """Test registering with metadata."""
        registry = CapabilityRegistry()

        registry.register(
            "claude-code",
            [AgentCapability.CODE_EDIT],
            metadata={"type": "cli", "version": "1.0"},
        )

        caps = registry.get_capabilities("claude-code")
        assert caps.metadata["type"] == "cli"

    def test_unregister_agent(self):
        """Test unregistering an agent."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT])
        assert registry.unregister("agent-1")
        assert "agent-1" not in registry.list_agents()

    def test_unregister_nonexistent(self):
        """Test unregistering non-existent agent."""
        registry = CapabilityRegistry()
        assert not registry.unregister("nonexistent")

    def test_has_capability(self):
        """Test checking if agent has capability."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT, AgentCapability.GIT])

        assert registry.has_capability("agent-1", AgentCapability.CODE_EDIT)
        assert registry.has_capability("agent-1", "git")
        assert not registry.has_capability("agent-1", AgentCapability.DEPLOY)
        assert not registry.has_capability("unknown", AgentCapability.CODE_EDIT)

    def test_get_agents_with_capability(self):
        """Test finding agents with a specific capability."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT, AgentCapability.GIT])
        registry.register("agent-2", [AgentCapability.GIT, AgentCapability.DEPLOY])
        registry.register("agent-3", [AgentCapability.CODE_EDIT])

        code_edit_agents = registry.get_agents_with_capability(AgentCapability.CODE_EDIT)
        assert "agent-1" in code_edit_agents
        assert "agent-3" in code_edit_agents
        assert "agent-2" not in code_edit_agents

        git_agents = registry.get_agents_with_capability("git")
        assert "agent-1" in git_agents
        assert "agent-2" in git_agents

    def test_get_agents_with_all_capabilities(self):
        """Test finding agents with all specified capabilities."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT, AgentCapability.GIT, AgentCapability.TERMINAL])
        registry.register("agent-2", [AgentCapability.CODE_EDIT, AgentCapability.GIT])
        registry.register("agent-3", [AgentCapability.CODE_EDIT])

        # Find agents with both CODE_EDIT and GIT
        agents = registry.get_agents_with_all_capabilities([
            AgentCapability.CODE_EDIT,
            AgentCapability.GIT,
        ])

        assert "agent-1" in agents
        assert "agent-2" in agents
        assert "agent-3" not in agents

        # Empty list returns all agents
        all_agents = registry.get_agents_with_all_capabilities([])
        assert len(all_agents) == 3

    def test_get_agents_with_any_capability(self):
        """Test finding agents with any of specified capabilities."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT])
        registry.register("agent-2", [AgentCapability.DEPLOY])
        registry.register("agent-3", [AgentCapability.SEARCH])

        agents = registry.get_agents_with_any_capability([
            AgentCapability.CODE_EDIT,
            AgentCapability.DEPLOY,
        ])

        assert "agent-1" in agents
        assert "agent-2" in agents
        assert "agent-3" not in agents

        # Empty list returns empty
        assert registry.get_agents_with_any_capability([]) == []

    def test_find_best_agent(self):
        """Test finding the best agent for capabilities."""
        registry = CapabilityRegistry()

        registry.register(
            "agent-1",
            [AgentCapability.CODE_EDIT, AgentCapability.GIT],
            scores={"code_edit": 0.9, "git": 0.9},
        )
        registry.register(
            "agent-2",
            [AgentCapability.CODE_EDIT, AgentCapability.GIT],
            scores={"code_edit": 0.7, "git": 0.7},
        )

        best = registry.find_best_agent([AgentCapability.CODE_EDIT, AgentCapability.GIT])
        assert best == "agent-1"

    def test_find_best_agent_with_preferred(self):
        """Test finding best agent with preferred capabilities."""
        registry = CapabilityRegistry()

        registry.register(
            "agent-1",
            [AgentCapability.CODE_EDIT],
            scores={"code_edit": 0.9},
        )
        registry.register(
            "agent-2",
            [AgentCapability.CODE_EDIT, AgentCapability.FAST],
            scores={"code_edit": 0.8, "fast": 1.0},
        )

        # Without preferred, agent-1 wins
        best = registry.find_best_agent([AgentCapability.CODE_EDIT])
        assert best == "agent-1"

        # With FAST as preferred, agent-2 might win
        best = registry.find_best_agent(
            [AgentCapability.CODE_EDIT],
            preferred_capabilities=[AgentCapability.FAST],
        )
        assert best == "agent-2"

    def test_find_best_agent_with_exclusions(self):
        """Test finding best agent with exclusions."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT], scores={"code_edit": 0.9})
        registry.register("agent-2", [AgentCapability.CODE_EDIT], scores={"code_edit": 0.8})

        best = registry.find_best_agent(
            [AgentCapability.CODE_EDIT],
            excluded_agents=["agent-1"],
        )
        assert best == "agent-2"

    def test_find_best_agent_no_match(self):
        """Test finding best agent when no match exists."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT])

        best = registry.find_best_agent([AgentCapability.DEPLOY])
        assert best is None

    def test_rank_agents(self):
        """Test ranking agents by capabilities."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT], scores={"code_edit": 0.9})
        registry.register("agent-2", [AgentCapability.CODE_EDIT], scores={"code_edit": 0.7})
        registry.register("agent-3", [AgentCapability.CODE_EDIT], scores={"code_edit": 0.8})
        registry.register("agent-4", [AgentCapability.GIT])  # No CODE_EDIT

        ranked = registry.rank_agents([AgentCapability.CODE_EDIT])

        assert len(ranked) == 3
        assert ranked[0][0] == "agent-1"
        assert ranked[1][0] == "agent-3"
        assert ranked[2][0] == "agent-2"

    def test_rank_agents_limit(self):
        """Test limiting ranked results."""
        registry = CapabilityRegistry()

        for i in range(10):
            registry.register(f"agent-{i}", [AgentCapability.CODE_EDIT])

        ranked = registry.rank_agents([AgentCapability.CODE_EDIT], limit=3)
        assert len(ranked) == 3

    def test_list_all_capabilities(self):
        """Test listing all unique capabilities."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT, AgentCapability.GIT])
        registry.register("agent-2", [AgentCapability.GIT, AgentCapability.DEPLOY])

        all_caps = registry.list_all_capabilities()
        assert "code_edit" in all_caps
        assert "git" in all_caps
        assert "deploy" in all_caps

    def test_get_summary(self):
        """Test getting registry summary."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT, AgentCapability.GIT])
        registry.register("agent-2", [AgentCapability.GIT])

        summary = registry.get_summary()
        assert summary["total_agents"] == 2
        assert summary["total_capabilities"] == 2
        assert "agent-1" in summary["agents"]
        assert summary["capability_coverage"]["git"] == 2

    def test_add_capability_to_agent(self):
        """Test adding capability to existing agent."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT])
        assert not registry.has_capability("agent-1", AgentCapability.GIT)

        result = registry.add_capability_to_agent("agent-1", AgentCapability.GIT, score=0.8)
        assert result is True
        assert registry.has_capability("agent-1", AgentCapability.GIT)

        # Non-existent agent
        assert not registry.add_capability_to_agent("unknown", AgentCapability.GIT)

    def test_remove_capability_from_agent(self):
        """Test removing capability from agent."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT, AgentCapability.GIT])

        result = registry.remove_capability_from_agent("agent-1", AgentCapability.GIT)
        assert result is True
        assert not registry.has_capability("agent-1", AgentCapability.GIT)

        # Non-existent agent
        assert not registry.remove_capability_from_agent("unknown", AgentCapability.GIT)


class TestCapabilityRegistryIntegration:
    """Integration tests for capability registry."""

    def test_task_assignment_scenario(self):
        """Test realistic task assignment scenario."""
        registry = CapabilityRegistry()

        # Register different agents with varying capabilities
        registry.register(
            "claude-code",
            [
                AgentCapability.CODE_EDIT,
                AgentCapability.GIT,
                AgentCapability.TERMINAL,
                AgentCapability.STREAMING,
            ],
            scores={
                "code_edit": 0.95,
                "git": 0.9,
                "terminal": 0.85,
                "streaming": 1.0,
            },
        )

        registry.register(
            "gemini-cli",
            [
                AgentCapability.CODE_EDIT,
                AgentCapability.SEARCH,
                AgentCapability.LARGE_CONTEXT,
            ],
            scores={
                "code_edit": 0.85,
                "search": 0.95,
                "large_context": 1.0,
            },
        )

        registry.register(
            "codex-cli",
            [
                AgentCapability.CODE_EDIT,
                AgentCapability.AUTONOMOUS,
            ],
            scores={
                "code_edit": 0.8,
                "autonomous": 0.9,
            },
        )

        # Task: Code edit with git operations
        best = registry.find_best_agent(
            [AgentCapability.CODE_EDIT, AgentCapability.GIT],
        )
        assert best == "claude-code"

        # Task: Research with large context
        best = registry.find_best_agent(
            [AgentCapability.SEARCH],
            preferred_capabilities=[AgentCapability.LARGE_CONTEXT],
        )
        assert best == "gemini-cli"

        # Task: Autonomous operation
        best = registry.find_best_agent([AgentCapability.AUTONOMOUS])
        assert best == "codex-cli"

    def test_capability_index_consistency(self):
        """Test that capability index stays consistent."""
        registry = CapabilityRegistry()

        registry.register("agent-1", [AgentCapability.CODE_EDIT, AgentCapability.GIT])

        # Verify index
        assert "agent-1" in registry.get_agents_with_capability(AgentCapability.CODE_EDIT)
        assert "agent-1" in registry.get_agents_with_capability(AgentCapability.GIT)

        # Add capability
        registry.add_capability_to_agent("agent-1", AgentCapability.TERMINAL)
        assert "agent-1" in registry.get_agents_with_capability(AgentCapability.TERMINAL)

        # Remove capability
        registry.remove_capability_from_agent("agent-1", AgentCapability.GIT)
        assert "agent-1" not in registry.get_agents_with_capability(AgentCapability.GIT)

        # Unregister agent
        registry.unregister("agent-1")
        assert "agent-1" not in registry.get_agents_with_capability(AgentCapability.CODE_EDIT)
        assert "agent-1" not in registry.get_agents_with_capability(AgentCapability.TERMINAL)
