"""Tests for tool registry and handlers."""

from unittest.mock import AsyncMock

import pytest

from servicenow_mcp.config import FeaturesConfig
from servicenow_mcp.tools import ToolRegistry


class TestToolRegistry:
    """Test tool registry functionality."""

    def test_tool_registration(self):
        """Test that all tools are registered correctly."""
        features = FeaturesConfig()  # All features enabled by default
        registry = ToolRegistry(features)

        # Check that tools are registered
        tools = registry.get_enabled_tools()
        tool_names = [tool.name for tool in tools]

        # Check core tools
        assert "query_table" in tool_names
        assert "get_record" in tool_names
        assert "create_record" in tool_names
        assert "update_record" in tool_names
        assert "delete_record" in tool_names

        # Check feature-specific tools
        assert "incident_create" in tool_names
        assert "incident_update" in tool_names
        assert "incident_search" in tool_names
        assert "change_create" in tool_names
        assert "ci_search" in tool_names
        assert "ci_relationships" in tool_names
        assert "user_search" in tool_names
        assert "kb_search" in tool_names
        assert "catalog_items" in tool_names
        assert "get_stats" in tool_names

    def test_feature_disabling(self):
        """Test that disabling features removes corresponding tools."""
        features = FeaturesConfig(
            incident_management=False,
            change_management=False,
            cmdb=False,
            custom_tables=False,
        )
        registry = ToolRegistry(features)

        tools = registry.get_enabled_tools()
        tool_names = [tool.name for tool in tools]

        # Incident tools should be disabled
        assert "incident_create" not in tool_names
        assert "incident_update" not in tool_names
        assert "incident_search" not in tool_names

        # Change tools should be disabled
        assert "change_create" not in tool_names

        # CMDB tools should be disabled
        assert "ci_search" not in tool_names
        assert "ci_relationships" not in tool_names

        # Custom table tools should be disabled
        assert "query_table" not in tool_names
        assert "get_record" not in tool_names
        assert "create_record" not in tool_names
        assert "update_record" not in tool_names
        assert "delete_record" not in tool_names

        # Other tools should still be enabled
        assert "user_search" in tool_names
        assert "kb_search" in tool_names
        assert "catalog_items" in tool_names

    def test_get_handler(self):
        """Test getting handlers for tools."""
        features = FeaturesConfig()
        registry = ToolRegistry(features)

        # Test getting valid handler
        handler = registry.get_handler("incident_create")
        assert handler is not None
        assert callable(handler)

        # Test getting invalid handler
        handler = registry.get_handler("non_existent_tool")
        assert handler is None

    @pytest.mark.asyncio
    async def test_incident_create_handler(self):
        """Test incident creation handler."""
        features = FeaturesConfig()
        registry = ToolRegistry(features)

        # Mock client
        mock_client = AsyncMock()
        mock_client.create_incident.return_value = {
            "sys_id": "123",
            "number": "INC0000001",
        }

        handler = registry.get_handler("incident_create")
        result = await handler(
            mock_client,
            {
                "short_description": "Test incident",
                "urgency": 1,
                "impact": 1,
                "assignment_group": "IT Support",
            },
        )

        assert result["sys_id"] == "123"
        assert result["number"] == "INC0000001"

        # Verify client was called correctly
        mock_client.create_incident.assert_called_once()
        call_args = mock_client.create_incident.call_args[0][0]
        assert call_args["short_description"] == "Test incident"
        assert call_args["urgency"] == 1
        assert call_args["impact"] == 1
        assert call_args["assignment_group"] == "IT Support"

    @pytest.mark.asyncio
    async def test_incident_update_handler(self):
        """Test incident update handler."""
        features = FeaturesConfig()
        registry = ToolRegistry(features)

        # Mock client
        mock_client = AsyncMock()
        mock_client.query_incidents.return_value = [
            {"sys_id": "123", "number": "INC0000001"}
        ]
        mock_client.update_incident.return_value = {
            "sys_id": "123",
            "number": "INC0000001",
            "state": "2",
        }

        handler = registry.get_handler("incident_update")
        result = await handler(
            mock_client,
            {"number": "INC0000001", "state": 2, "work_notes": "Working on this"},
        )

        assert result["state"] == "2"

        # Verify client calls
        mock_client.query_incidents.assert_called_once_with(
            query="number=INC0000001", limit=1
        )
        mock_client.update_incident.assert_called_once_with(
            "123", {"state": 2, "work_notes": "Working on this"}, display_value="both"
        )

    @pytest.mark.asyncio
    async def test_incident_update_not_found(self):
        """Test incident update when incident not found."""
        features = FeaturesConfig()
        registry = ToolRegistry(features)

        # Mock client
        mock_client = AsyncMock()
        mock_client.query_incidents.return_value = []

        handler = registry.get_handler("incident_update")
        result = await handler(mock_client, {"number": "INC9999999", "state": 2})

        assert result["error"] == "Incident INC9999999 not found"

    @pytest.mark.asyncio
    async def test_query_table_handler(self):
        """Test query table handler."""
        features = FeaturesConfig()
        registry = ToolRegistry(features)

        # Mock client
        mock_client = AsyncMock()
        mock_client.query_records.return_value = [
            {"sys_id": "1", "name": "Item 1"},
            {"sys_id": "2", "name": "Item 2"},
        ]

        handler = registry.get_handler("query_table")
        result = await handler(
            mock_client,
            {
                "table": "sc_cat_item",
                "query": "active=true",
                "fields": ["name", "price"],
                "limit": 50,
                "order_by": "-sys_created_on",
            },
        )

        assert len(result) == 2
        assert result[0]["name"] == "Item 1"

        # Verify client call
        mock_client.query_records.assert_called_once_with(
            table="sc_cat_item",
            query="active=true",
            fields=["name", "price"],
            limit=50,
            offset=0,
            order_by="-sys_created_on",
            display_value="both",
        )

    @pytest.mark.asyncio
    async def test_ci_search_handler(self):
        """Test CI search handler."""
        features = FeaturesConfig()
        registry = ToolRegistry(features)

        # Mock client
        mock_client = AsyncMock()
        mock_client.query_records.return_value = [
            {"sys_id": "ci1", "name": "PROD-WEB-01"},
            {"sys_id": "ci2", "name": "PROD-DB-01"},
        ]

        handler = registry.get_handler("ci_search")
        result = await handler(
            mock_client,
            {
                "name": "PROD*",
                "class": "cmdb_ci_server",
                "operational_status": 1,
                "environment": "production",
            },
        )

        assert len(result) == 2

        # Verify query construction
        mock_client.query_records.assert_called_once()
        call_args = mock_client.query_records.call_args
        assert call_args[0][0] == "cmdb_ci"
        query = call_args[1]["query"]
        assert "nameLIKEPROD*" in query
        assert "sys_class_name=cmdb_ci_server" in query
        assert "operational_status=1" in query
        assert "u_environment=production" in query

    @pytest.mark.asyncio
    async def test_user_search_handler(self):
        """Test user search handler."""
        features = FeaturesConfig()
        registry = ToolRegistry(features)

        # Mock client
        mock_client = AsyncMock()
        mock_client.query_users.return_value = [
            {
                "sys_id": "user1",
                "user_name": "john.doe",
                "email": "john.doe@example.com",
            }
        ]

        handler = registry.get_handler("user_search")
        result = await handler(
            mock_client, {"name": "John", "active": True, "department": "IT"}
        )

        assert len(result) == 1
        assert result[0]["user_name"] == "john.doe"

        # Verify query construction
        mock_client.query_users.assert_called_once()
        call_args = mock_client.query_users.call_args
        query = call_args[1]["query"]
        assert "first_nameLIKEJohn" in query
        assert "active=true" in query
        assert "department.name=IT" in query

    @pytest.mark.asyncio
    async def test_get_stats_handler(self):
        """Test aggregate statistics handler."""
        features = FeaturesConfig()
        registry = ToolRegistry(features)

        # Mock client
        mock_client = AsyncMock()
        mock_client.get_aggregate.return_value = [
            {"priority": 1, "count": 15},
            {"priority": 2, "count": 30},
            {"priority": 3, "count": 45},
        ]

        handler = registry.get_handler("get_stats")
        result = await handler(
            mock_client,
            {
                "table": "incident",
                "group_by": ["priority"],
                "aggregates": [{"type": "COUNT", "field": "sys_id", "alias": "count"}],
                "query": "active=true",
            },
        )

        assert len(result) == 3
        assert result[0]["count"] == 15

        # Verify client call
        mock_client.get_aggregate.assert_called_once_with(
            table="incident",
            query="active=true",
            group_by=["priority"],
            aggregate=[{"type": "COUNT", "field": "sys_id", "alias": "count"}],
        )
