"""
Tests for MCP Exception Classes
"""

import pytest

from app.services.mcp.exceptions import (
    MCPCircuitOpenError,
    MCPConnectionError,
    MCPError,
    MCPServerNotFoundError,
    MCPTimeoutError,
    MCPToolError,
    MCPToolNotFoundError,
    MCPValidationError,
)


class TestMCPError:
    """Tests for base MCPError class."""

    def test_basic_error(self):
        """Test basic error creation."""
        error = MCPError("Test error")
        assert error.message == "Test error"
        assert error.server_name is None
        assert error.details == {}
        assert str(error) == "Test error"

    def test_error_with_server_name(self):
        """Test error with server name."""
        error = MCPError("Test error", server_name="test-server")
        assert error.server_name == "test-server"
        assert "server=test-server" in str(error)

    def test_error_with_details(self):
        """Test error with additional details."""
        error = MCPError(
            "Test error",
            server_name="test-server",
            details={"key": "value"},
        )
        assert error.details == {"key": "value"}
        assert "details={'key': 'value'}" in str(error)


class TestMCPConnectionError:
    """Tests for MCPConnectionError class."""

    def test_basic_connection_error(self):
        """Test basic connection error."""
        error = MCPConnectionError("Connection failed")
        assert error.message == "Connection failed"
        assert error.url is None
        assert error.cause is None

    def test_connection_error_with_url(self):
        """Test connection error with URL."""
        error = MCPConnectionError(
            "Connection failed",
            server_name="test-server",
            url="http://localhost:8000",
        )
        assert error.url == "http://localhost:8000"
        assert "url=http://localhost:8000" in str(error)

    def test_connection_error_with_cause(self):
        """Test connection error with cause."""
        cause = ConnectionError("Network error")
        error = MCPConnectionError(
            "Connection failed",
            cause=cause,
        )
        assert error.cause is cause
        assert "ConnectionError" in str(error)


class TestMCPTimeoutError:
    """Tests for MCPTimeoutError class."""

    def test_basic_timeout_error(self):
        """Test basic timeout error."""
        error = MCPTimeoutError("Request timed out")
        assert error.message == "Request timed out"
        assert error.timeout_seconds is None
        assert error.operation is None

    def test_timeout_error_with_details(self):
        """Test timeout error with details."""
        error = MCPTimeoutError(
            "Request timed out",
            server_name="test-server",
            timeout_seconds=30.0,
            operation="POST /mcp",
        )
        assert error.timeout_seconds == 30.0
        assert error.operation == "POST /mcp"
        assert "timeout=30.0s" in str(error)
        assert "operation=POST /mcp" in str(error)


class TestMCPToolError:
    """Tests for MCPToolError class."""

    def test_basic_tool_error(self):
        """Test basic tool error."""
        error = MCPToolError("Tool execution failed")
        assert error.message == "Tool execution failed"
        assert error.tool_name is None
        assert error.tool_args is None
        assert error.error_code is None

    def test_tool_error_with_details(self):
        """Test tool error with all details."""
        error = MCPToolError(
            "Tool execution failed",
            server_name="llm-gateway",
            tool_name="chat",
            tool_args={"prompt": "Hello"},
            error_code="INVALID_ARGS",
        )
        assert error.tool_name == "chat"
        assert error.tool_args == {"prompt": "Hello"}
        assert error.error_code == "INVALID_ARGS"
        assert "tool=chat" in str(error)
        assert "error_code=INVALID_ARGS" in str(error)


class TestMCPServerNotFoundError:
    """Tests for MCPServerNotFoundError class."""

    def test_server_not_found(self):
        """Test server not found error."""
        error = MCPServerNotFoundError("unknown-server")
        assert error.server_name == "unknown-server"
        assert "MCP server not found: unknown-server" in error.message
        assert error.available_servers == []

    def test_server_not_found_with_available(self):
        """Test server not found with available servers listed."""
        error = MCPServerNotFoundError(
            "unknown-server",
            available_servers=["server-1", "server-2"],
        )
        assert error.available_servers == ["server-1", "server-2"]
        assert "available=['server-1', 'server-2']" in str(error)


class TestMCPToolNotFoundError:
    """Tests for MCPToolNotFoundError class."""

    def test_tool_not_found(self):
        """Test tool not found error."""
        error = MCPToolNotFoundError("unknown-tool")
        assert error.tool_name == "unknown-tool"
        assert "Tool not found: unknown-tool" in error.message
        assert error.available_tools == []

    def test_tool_not_found_with_available(self):
        """Test tool not found with available tools listed."""
        error = MCPToolNotFoundError(
            "unknown-tool",
            available_tools=["tool-1", "tool-2", "tool-3", "tool-4", "tool-5", "tool-6"],
        )
        assert len(error.available_tools) == 6
        # Should show first 5 tools with ellipsis
        assert "available_tools=['tool-1', 'tool-2', 'tool-3', 'tool-4', 'tool-5']..." in str(error)


class TestMCPCircuitOpenError:
    """Tests for MCPCircuitOpenError class."""

    def test_circuit_open_error(self):
        """Test circuit open error."""
        error = MCPCircuitOpenError("test-server")
        assert error.server_name == "test-server"
        assert "Circuit breaker open for server: test-server" in error.message
        assert error.failure_count is None
        assert error.reset_timeout is None

    def test_circuit_open_error_with_details(self):
        """Test circuit open error with details."""
        error = MCPCircuitOpenError(
            "test-server",
            failure_count=5,
            reset_timeout=30.0,
        )
        assert error.failure_count == 5
        assert error.reset_timeout == 30.0
        assert "failures=5" in str(error)
        assert "reset_in=30.0s" in str(error)


class TestMCPValidationError:
    """Tests for MCPValidationError class."""

    def test_validation_error(self):
        """Test validation error."""
        error = MCPValidationError("Validation failed")
        assert error.message == "Validation failed"
        assert error.tool_name is None
        assert error.field_errors == {}

    def test_validation_error_with_details(self):
        """Test validation error with field errors."""
        error = MCPValidationError(
            "Validation failed",
            tool_name="create_issue",
            field_errors={
                "title": "Title is required",
                "priority": "Invalid priority value",
            },
        )
        assert error.tool_name == "create_issue"
        assert error.field_errors == {
            "title": "Title is required",
            "priority": "Invalid priority value",
        }
        assert "tool=create_issue" in str(error)
        assert "fields=['title', 'priority']" in str(error)


class TestExceptionInheritance:
    """Tests for exception inheritance chain."""

    def test_all_errors_inherit_from_mcp_error(self):
        """Test that all custom exceptions inherit from MCPError."""
        assert issubclass(MCPConnectionError, MCPError)
        assert issubclass(MCPTimeoutError, MCPError)
        assert issubclass(MCPToolError, MCPError)
        assert issubclass(MCPServerNotFoundError, MCPError)
        assert issubclass(MCPToolNotFoundError, MCPError)
        assert issubclass(MCPCircuitOpenError, MCPError)
        assert issubclass(MCPValidationError, MCPError)

    def test_all_errors_inherit_from_exception(self):
        """Test that base MCPError inherits from Exception."""
        assert issubclass(MCPError, Exception)

    def test_catch_all_with_mcp_error(self):
        """Test that all errors can be caught with MCPError."""

        def raise_connection_error():
            raise MCPConnectionError("Connection failed")

        def raise_timeout_error():
            raise MCPTimeoutError("Timeout")

        def raise_tool_error():
            raise MCPToolError("Tool failed")

        with pytest.raises(MCPError):
            raise_connection_error()

        with pytest.raises(MCPError):
            raise_timeout_error()

        with pytest.raises(MCPError):
            raise_tool_error()
