# -*- coding: utf-8 -*-
"""Location: ./tests/unit/mcpgateway/test_rpc_backward_compatibility.py
Copyright 2026
SPDX-License-Identifier: Apache-2.0
Authors: Mihai Criveti

Test backward compatibility for tool invocation after PR #746.
"""

# Standard
from unittest.mock import AsyncMock, MagicMock, patch

# Third-Party
from fastapi.testclient import TestClient
import pytest
from sqlalchemy.orm import Session

# First-Party
from mcpgateway.main import app


@pytest.fixture
def client():
    """Create a test client."""
    return TestClient(app)


@pytest.fixture
def mock_db():
    """Create a mock database session."""
    return MagicMock(spec=Session)


class TestRPCBackwardCompatibility:
    """Test backward compatibility for RPC tool invocation."""

    def test_old_format_tool_invocation_with_backward_compatibility(self, client, mock_db):
        """Test that old format (direct tool name as method) still works with backward compatibility."""
        with patch("mcpgateway.config.settings.auth_required", False):
            with patch("mcpgateway.config.settings.csrf_enabled", False):
                with patch("mcpgateway.main.get_db", return_value=mock_db):
                    with patch("mcpgateway.main.PermissionChecker.has_permission", new=AsyncMock(return_value=True)):
                        with patch("mcpgateway.main.tool_service.invoke_tool", new_callable=AsyncMock) as mock_invoke:
                            mock_invoke.return_value = {"result": "success", "data": "test data from old format"}

                            # Old format: tool name directly as method
                            request_body = {"jsonrpc": "2.0", "method": "my_custom_tool", "params": {"query": "test query", "limit": 10}, "id": 123}

                            response = client.post("/rpc", json=request_body)

                            assert response.status_code == 200
                            result = response.json()
                            assert result["jsonrpc"] == "2.0"
                            assert "result" in result
                            assert result["result"]["result"] == "success"
                            assert result["result"]["data"] == "test data from old format"
                            assert result["id"] == 123

                            # Verify the tool was invoked with correct parameters
                            mock_invoke.assert_called_once()
                            call_args = mock_invoke.call_args
                            assert call_args.kwargs["name"] == "my_custom_tool"
                        assert call_args.kwargs["arguments"] == {"query": "test query", "limit": 10}

    def test_new_format_tool_invocation_still_works(self, client, mock_db):
        """Test that new format (tools/call method) continues to work."""
        with patch("mcpgateway.config.settings.auth_required", False):
            with patch("mcpgateway.config.settings.csrf_enabled", False):
                with patch("mcpgateway.main.get_db", return_value=mock_db):
                    with patch("mcpgateway.main.PermissionChecker.has_permission", new=AsyncMock(return_value=True)):
                        with patch("mcpgateway.main.tool_service.invoke_tool", new_callable=AsyncMock) as mock_invoke:
                            mock_invoke.return_value = {"result": "success", "data": "test data from new format"}

                            # New format: tools/call method
                            request_body = {"jsonrpc": "2.0", "method": "tools/call", "params": {"name": "my_custom_tool", "arguments": {"query": "test query", "limit": 10}}, "id": 456}

                            response = client.post("/rpc", json=request_body)

                            assert response.status_code == 200
                        result = response.json()
                        assert result["jsonrpc"] == "2.0"
                        assert "result" in result
                        assert result["result"]["result"] == "success"
                        assert result["result"]["data"] == "test data from new format"
                        assert result["id"] == 456

                        # Verify the tool was invoked with correct parameters
                        mock_invoke.assert_called_once()
                        call_args = mock_invoke.call_args
                        assert call_args.kwargs["name"] == "my_custom_tool"
                        assert call_args.kwargs["arguments"] == {"query": "test query", "limit": 10}

    def test_both_formats_invoke_same_tool(self, client, mock_db):
        """Test that both old and new formats can invoke the same tool successfully."""
        with patch("mcpgateway.config.settings.auth_required", False):
            with patch("mcpgateway.config.settings.csrf_enabled", False):
                with patch("mcpgateway.main.get_db", return_value=mock_db):
                    with patch("mcpgateway.main.PermissionChecker.has_permission", new=AsyncMock(return_value=True)):
                        with patch("mcpgateway.main.tool_service.invoke_tool", new_callable=AsyncMock) as mock_invoke:
                            mock_invoke.return_value = {"result": "success"}

                            # Test old format
                            old_format_request = {"jsonrpc": "2.0", "method": "search_tool", "params": {"query": "old format"}, "id": 1}

                            response_old = client.post("/rpc", json=old_format_request)
                            assert response_old.status_code == 200

                            # Verify first call
                            assert mock_invoke.call_count == 1
                            call_args = mock_invoke.call_args
                            assert call_args.kwargs["name"] == "search_tool"
                            assert call_args.kwargs["arguments"]["query"] == "old format"

                            # Reset mock
                            mock_invoke.reset_mock()

                            # Test new format
                            new_format_request = {"jsonrpc": "2.0", "method": "tools/call", "params": {"name": "search_tool", "arguments": {"query": "new format"}}, "id": 2}

                            response_new = client.post("/rpc", json=new_format_request)
                            assert response_new.status_code == 200

                            # Verify second call after reset
                            assert mock_invoke.call_count == 1
                            call_args = mock_invoke.call_args
                            assert call_args.kwargs["name"] == "search_tool"
                            assert call_args.kwargs["arguments"]["query"] == "new format"

    def test_invalid_method_still_returns_error(self, client, mock_db):
        """Test that truly invalid methods still return an error."""
        with patch("mcpgateway.config.settings.auth_required", False):
            with patch("mcpgateway.config.settings.csrf_enabled", False):
                with patch("mcpgateway.main.get_db", return_value=mock_db):
                    with patch("mcpgateway.main.PermissionChecker.has_permission", new=AsyncMock(return_value=True)):
                        with patch("mcpgateway.main.tool_service.invoke_tool", new_callable=AsyncMock) as mock_invoke:
                            # Simulate tool not found
                            mock_invoke.side_effect = ValueError("Tool not found")

                            # Gateway forwarding has been removed, so just expect error from tool failure
                            request_body = {"jsonrpc": "2.0", "method": "completely_invalid_method", "params": {}, "id": 999}

                            response = client.post("/rpc", json=request_body)

                            assert response.status_code == 200
                        result = response.json()
                        assert result["jsonrpc"] == "2.0"
                        assert "error" in result
                        assert result["error"]["code"] == -32603
                        assert result["error"]["message"] == "Internal error"
                        assert result["id"] == 999
