"""
Tests for the MCP proxy.

These tests verify the proxy functionality including:
- Configuration validation
- Message handling
- SSE communication
- Error handling
"""

import json
from typing import Any
from unittest.mock import MagicMock, patch

import pytest

from ubidots_mcp_server.proxy import ConfigurationError, MCPProxy


class TestMCPProxyInit:
    """Test MCPProxy initialization."""

    def test_init_with_env_vars(self, monkeypatch: pytest.MonkeyPatch) -> None:
        """Test initialization with environment variables."""
        monkeypatch.setenv("X_AUTH_TOKEN", "test-token")
        monkeypatch.setenv("MCP_BASE_URL", "https://test.mcp.com/mcp")

        proxy = MCPProxy()

        assert proxy.auth_token == "test-token"
        assert proxy.base_url == "https://test.mcp.com/mcp"

    def test_init_with_parameters(self) -> None:
        """Test initialization with direct parameters."""
        proxy = MCPProxy(
            base_url="https://custom.mcp.com/mcp", auth_token="custom-token"
        )

        assert proxy.auth_token == "custom-token"
        assert proxy.base_url == "https://custom.mcp.com/mcp"

    def test_init_missing_auth_token(self, monkeypatch: pytest.MonkeyPatch) -> None:
        """Test that missing auth token raises ConfigurationError."""
        monkeypatch.delenv("X_AUTH_TOKEN", raising=False)

        with pytest.raises(ConfigurationError, match="X_AUTH_TOKEN"):
            MCPProxy()

    def test_default_base_url(self, monkeypatch: pytest.MonkeyPatch) -> None:
        """Test that default base URL is used when not specified."""
        monkeypatch.setenv("X_AUTH_TOKEN", "test-token")
        monkeypatch.delenv("MCP_BASE_URL", raising=False)

        proxy = MCPProxy()

        assert proxy.base_url == "https://mcp.ubidots.com/mcp"

    def test_headers_property(self, monkeypatch: pytest.MonkeyPatch) -> None:
        """Test that headers include proper authentication."""
        monkeypatch.setenv("X_AUTH_TOKEN", "test-token-123")

        proxy = MCPProxy()
        headers = proxy.headers

        assert headers["Authorization"] == "Bearer test-token-123"
        assert headers["Content-Type"] == "application/json"
        assert headers["Accept"] == "application/json, text/event-stream"


class TestMCPProxyMessages:
    """Test message handling in the proxy."""

    @pytest.mark.asyncio
    async def test_write_stdout_message(
        self, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
    ) -> None:
        """Test writing JSON-RPC messages to stdout."""
        monkeypatch.setenv("X_AUTH_TOKEN", "test-token")

        proxy = MCPProxy()
        message: dict[str, Any] = {
            "jsonrpc": "2.0",
            "id": 1,
            "result": {"status": "ok"},
        }

        await proxy._write_stdout_message(message)

        captured = capsys.readouterr()
        assert captured.out == json.dumps(message) + "\n"

    @pytest.mark.asyncio
    async def test_read_stdin_messages(self, monkeypatch: pytest.MonkeyPatch) -> None:
        """Test reading JSON-RPC messages from stdin."""
        monkeypatch.setenv("X_AUTH_TOKEN", "test-token")

        messages = [
            {"jsonrpc": "2.0", "method": "test", "id": 1},
            {"jsonrpc": "2.0", "method": "test2", "id": 2},
        ]

        # Mock stdin
        input_data = "\n".join(json.dumps(msg) for msg in messages) + "\n"

        proxy = MCPProxy()
        proxy._running = True

        collected_messages = []

        # Use a mock for stdin.readline
        readline_mock = MagicMock(side_effect=input_data.split("\n") + [""])

        with patch("sys.stdin.readline", readline_mock):
            async for msg in proxy._read_stdin_messages():
                collected_messages.append(msg)
                if len(collected_messages) >= len(messages):
                    proxy._running = False
                    break

        assert len(collected_messages) == len(messages)
        assert collected_messages[0] == messages[0]
        assert collected_messages[1] == messages[1]


class TestMCPProxyContextManager:
    """Test async context manager functionality."""

    @pytest.mark.asyncio
    async def test_context_manager(self, monkeypatch: pytest.MonkeyPatch) -> None:
        """Test that proxy can be used as async context manager."""
        monkeypatch.setenv("X_AUTH_TOKEN", "test-token")

        async with MCPProxy() as proxy:
            assert proxy._client is not None

    @pytest.mark.asyncio
    async def test_context_manager_cleanup(
        self, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """Test that context manager properly cleans up resources."""
        monkeypatch.setenv("X_AUTH_TOKEN", "test-token")

        proxy = MCPProxy()

        async with proxy:
            client = proxy._client
            assert client is not None

        # After context exit, client should be closed (we can't easily check this
        # without more mocking, but we verify the flow works)
        assert proxy._client is not None  # Reference still exists


class TestMCPProxyIntegration:
    """Integration tests for the proxy."""

    @pytest.mark.asyncio
    async def test_proxy_lifecycle(self, monkeypatch: pytest.MonkeyPatch) -> None:
        """Test basic proxy lifecycle."""
        monkeypatch.setenv("X_AUTH_TOKEN", "test-token")

        proxy = MCPProxy()

        assert proxy.auth_token == "test-token"
        assert not proxy._running

        # Proxy should be able to start and stop
        proxy._running = True
        assert proxy._running

        proxy._running = False
        assert not proxy._running
