"""Tests for MCP module — client, providers, giga_sync, server, zo_client."""

from __future__ import annotations

import asyncio
import json
from pathlib import Path
from unittest.mock import patch

import pytest


def _run(coro):
    """Run async coroutine (works without pytest-asyncio)."""
    return asyncio.run(coro)


class TestMCPProviders:
    """Test MCP provider config loading."""

    def test_load_providers_defaults_when_no_file(self, temp_dir: Path) -> None:
        from vastian.mcp.providers import load_mcp_providers

        providers = load_mcp_providers(temp_dir / "nonexistent.yaml")
        assert "giga" in providers
        assert "zo" in providers
        assert providers["giga"].url == "https://mcp.gigamind.dev/mcp"
        assert providers["zo"].url == "https://api.zo.computer/mcp"
        assert providers["zo"].api_key_env == "ZO_API_KEY"

    def test_load_providers_from_yaml(self, temp_dir: Path) -> None:
        import yaml
        from vastian.mcp.providers import load_mcp_providers

        cfg = temp_dir / "mcp-providers.yaml"
        cfg.write_text(
            yaml.dump(
                {
                    "providers": {
                        "giga": {
                            "url": "https://custom.giga/mcp",
                            "auth": "github",
                            "enabled": True,
                        },
                        "zo": {"url": "https://zo.local/mcp", "auth": "bearer", "enabled": False},
                    },
                }
            ),
            encoding="utf-8",
        )
        providers = load_mcp_providers(cfg)
        assert "giga" in providers
        assert providers["giga"].url == "https://custom.giga/mcp"
        assert "zo" not in providers  # disabled


class TestGigaSync:
    """Test giga_sync — push/pull with mocked MCP client."""

    def test_giga_push_returns_error_when_no_giga_config(self) -> None:
        from vastian.mcp.giga_sync import giga_push

        with patch("vastian.mcp.giga_sync.load_mcp_providers", return_value={}):
            plans, neurons, errors = _run(giga_push())
        assert plans == 0
        assert neurons == 0
        assert len(errors) == 1
        assert "not configured" in errors[0].lower()

    def test_giga_push_with_empty_outbox(self, temp_dir: Path) -> None:
        from vastian.mcp import giga_sync
        from vastian.mcp.giga_sync import giga_push

        with patch.object(giga_sync, "OUTBOX_PLANS", temp_dir / "plans"):
            with patch.object(giga_sync, "OUTBOX_NEURONS", temp_dir / "neurons"):
                with patch.object(giga_sync, "SENT_DIR", temp_dir / "sent"):
                    with patch("vastian.mcp.giga_sync.load_mcp_providers") as mock_load:
                        mock_load.return_value = {
                            "giga": type("C", (), {"url": "https://test/mcp", "enabled": True})(),
                        }
                        (temp_dir / "plans").mkdir(parents=True)
                        (temp_dir / "neurons").mkdir(parents=True)
                        plans, neurons, errors = _run(giga_push())
        assert plans == 0
        assert neurons == 0
        assert len(errors) == 0

    def test_giga_pull_returns_error_when_no_config(self) -> None:
        from vastian.mcp.giga_sync import giga_pull

        with patch("vastian.mcp.giga_sync.load_mcp_providers", return_value={}):
            total, errors = _run(giga_pull())
        assert total == 0
        assert len(errors) == 1


class TestMCPClient:
    """Test MCPClient."""

    def test_client_init(self) -> None:
        from vastian.mcp.client import MCPClient

        client = MCPClient("https://example.com/mcp", headers={"Authorization": "Bearer x"})
        assert client.url == "https://example.com/mcp"
        assert client.headers["Authorization"] == "Bearer x"


class TestMCPServer:
    """Test Vastian MCP server JSON-RPC handling."""

    def test_tools_list_returns_definitions(self) -> None:
        from vastian.mcp.server import _handle_json_rpc, _tools_definitions

        req = {"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}
        resp = _handle_json_rpc(req)
        assert resp is not None
        assert resp.get("result", {}).get("tools")
        tools = resp["result"]["tools"]
        names = [t["name"] for t in tools]
        assert "kanban_list" in names
        assert "send_message" in names
        assert "knowledge_search" in names

    def test_initialize_returns_protocol_version(self) -> None:
        from vastian.mcp.server import _handle_json_rpc

        req = {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "initialize",
            "params": {"protocolVersion": "2024-11-05"},
        }
        resp = _handle_json_rpc(req)
        assert resp is not None
        assert "2024-11-05" in str(resp.get("result", {}))
        assert "vastian" in str(resp.get("result", {}).get("serverInfo", {})).lower()

    def test_kanban_list_tool(self) -> None:
        from vastian.mcp.server import _execute_tool

        result = _execute_tool("kanban_list", {})
        assert "content" in result
        assert result["content"]
        data = json.loads(result["content"][0]["text"])
        assert isinstance(data, list)

    def test_unknown_tool_returns_error(self) -> None:
        from vastian.mcp.server import _execute_tool

        result = _execute_tool("nonexistent_tool", {})
        assert result.get("isError") is True
        assert "Unknown tool" in result["content"][0]["text"]

    def test_tools_call_dispatches_to_execute(self) -> None:
        from vastian.mcp.server import _handle_json_rpc

        req = {
            "jsonrpc": "2.0",
            "id": 2,
            "method": "tools/call",
            "params": {"name": "sprint_status", "arguments": {}},
        }
        resp = _handle_json_rpc(req)
        assert resp is not None
        assert "result" in resp
        assert "content" in resp["result"]
        assert resp["result"]["content"]

    def test_notifications_initialized_returns_none(self) -> None:
        from vastian.mcp.server import _handle_json_rpc

        req = {
            "jsonrpc": "2.0",
            "method": "notifications/initialized",
            "params": {},
        }
        resp = _handle_json_rpc(req)
        assert resp is None


class TestZoClient:
    """Test Zo MCP client helpers."""

    def test_zo_mcp_client_returns_none_without_key(self) -> None:
        from vastian.mcp.zo_client import _zo_mcp_client

        assert _zo_mcp_client("") is None

    def test_pull_inbox_via_mcp_returns_empty_without_key(self) -> None:
        from vastian.mcp.zo_client import pull_inbox_via_mcp

        result = _run(pull_inbox_via_mcp(""))
        assert result == []
