import pytest
import aiohttp
import os
from unittest.mock import Mock, patch
from calimero import JsonRpcClient, Config

@pytest.fixture
def mock_config():
    """Create a mock config for testing."""
    config = Mock(spec=Config)
    config.node_url = "http://localhost:2428"
    config.keypair = Mock()
    config.keypair.sign.return_value = b"signature"
    return config

@pytest.fixture
def mock_env_vars():
    """Set up mock environment variables."""
    with patch.dict(os.environ, {
        'CONTEXT_ID': 'test_context',
        'EXECUTOR_PUBLIC_KEY': 'test_key'
    }):
        yield

@pytest.mark.asyncio
async def test_json_rpc_client_creation(mock_config, mock_env_vars):
    """Test JsonRpcClient initialization."""
    client = JsonRpcClient(mock_config)
    assert client.node_url == "http://localhost:2428"
    assert client.context_id == "test_context"
    assert client.executor_public_key == "test_key"

@pytest.mark.asyncio
async def test_prepare_headers(mock_config, mock_env_vars):
    """Test header preparation."""
    client = JsonRpcClient(mock_config)
    headers = client._prepare_headers()
    
    assert 'Content-Type' in headers
    assert 'X-Signature' in headers
    assert 'X-Timestamp' in headers
    assert headers['Content-Type'] == 'application/json'

@pytest.mark.asyncio
async def test_prepare_request(mock_config, mock_env_vars):
    """Test request preparation."""
    client = JsonRpcClient(mock_config)
    request = client._prepare_request("test_method", {"arg": "value"})
    
    assert request['jsonrpc'] == '2.0'
    assert request['method'] == 'execute'
    assert request['params']['contextId'] == 'test_context'
    assert request['params']['method'] == 'test_method'
    assert request['params']['argsJson'] == {"arg": "value"}
    assert request['params']['executorPublicKey'] == 'test_key'

@pytest.mark.asyncio
async def test_execute_request(mock_config, mock_env_vars):
    """Test executing RPC request."""
    client = JsonRpcClient(mock_config)
    
    mock_response = {
        "jsonrpc": "2.0",
        "result": {"success": True},
        "id": 1
    }
    
    with patch('aiohttp.ClientSession.post') as mock_post:
        mock_post.return_value.__aenter__.return_value.json.return_value = mock_response
        mock_post.return_value.__aenter__.return_value.status = 200
        
        response = await client.execute("test_method", {"arg": "value"})
        assert response == mock_response
        
        # Verify request was made with correct parameters
        mock_post.assert_called_once()
        call_args = mock_post.call_args[1]
        assert "json" in call_args
        assert call_args["json"]["method"] == "execute"
        assert call_args["json"]["params"]["contextId"] == "test_context"

@pytest.mark.asyncio
async def test_execute_request_error(mock_config, mock_env_vars):
    """Test error handling in RPC request."""
    client = JsonRpcClient(mock_config)
    
    with patch('aiohttp.ClientSession.post') as mock_post:
        mock_post.return_value.__aenter__.return_value.status = 400
        mock_post.return_value.__aenter__.return_value.text.return_value = "Error message"
        
        with pytest.raises(Exception) as exc_info:
            await client.execute("test_method", {"arg": "value"})
        assert "HTTP error 400" in str(exc_info.value) 