"""MCP server smoke tests - verify the kanoniv-mcp binary responds to JSON-RPC.

Spawns kanoniv-mcp as a subprocess, sends JSON-RPC over stdin, validates responses.
Requires KANONIV_API_KEY env var and kanoniv-mcp binary in PATH or target/release/.

Config via env vars:
  KANONIV_API_URL       - default: https://api.kanoniv.com
  KANONIV_API_KEY       - required
  KANONIV_MCP_BINARY    - path to kanoniv-mcp binary (default: kanoniv-mcp)
"""

from __future__ import annotations

import json
import os
import shutil
import subprocess

import pytest

API_URL = os.environ.get("KANONIV_API_URL", "https://api.kanoniv.com")
API_KEY = os.environ.get("KANONIV_API_KEY", "")
MCP_BINARY = os.environ.get("KANONIV_MCP_BINARY", "kanoniv-mcp")


def _skip_if_no_key():
    if not API_KEY:
        pytest.skip("KANONIV_API_KEY not set")


def _skip_if_no_binary():
    if not shutil.which(MCP_BINARY):
        pytest.skip(f"MCP binary not found: {MCP_BINARY}")


class McpProcess:
    """Thin wrapper around the kanoniv-mcp subprocess for JSON-RPC."""

    def __init__(self):
        env = os.environ.copy()
        env["KANONIV_API_URL"] = API_URL
        env["KANONIV_API_KEY"] = API_KEY
        env["KANONIV_MCP_MODE"] = "hybrid"
        env["RUST_LOG"] = "warn"

        self.proc = subprocess.Popen(
            [MCP_BINARY],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=env,
            text=True,
            bufsize=1,
        )
        self._next_id = 1

    def send(self, method: str, params: dict | None = None) -> dict:
        req_id = self._next_id
        self._next_id += 1
        request = {"jsonrpc": "2.0", "id": req_id, "method": method}
        if params is not None:
            request["params"] = params
        line = json.dumps(request) + "\n"
        self.proc.stdin.write(line)
        self.proc.stdin.flush()
        resp_line = self.proc.stdout.readline()
        if not resp_line:
            raise RuntimeError("MCP process closed stdout")
        return json.loads(resp_line)

    def notify(self, method: str, params: dict | None = None) -> None:
        request = {"jsonrpc": "2.0", "method": method}
        if params is not None:
            request["params"] = params
        line = json.dumps(request) + "\n"
        self.proc.stdin.write(line)
        self.proc.stdin.flush()

    def close(self):
        self.proc.stdin.close()
        self.proc.wait(timeout=5)


@pytest.fixture(scope="module")
def mcp():
    _skip_if_no_key()
    _skip_if_no_binary()
    proc = McpProcess()
    # Initialize the MCP session
    resp = proc.send("initialize", {
        "protocolVersion": "2024-11-05",
        "capabilities": {},
        "clientInfo": {"name": "smoke-test", "version": "0.1.0"},
    })
    # Send initialized notification
    proc.notify("notifications/initialized")
    yield proc, resp
    proc.close()


@pytest.mark.smoke_mcp
class TestMCPServer:
    def test_mcp_initialize(self, mcp):
        proc, init_resp = mcp
        assert "result" in init_resp
        result = init_resp["result"]
        assert "serverInfo" in result
        assert "capabilities" in result

    def test_mcp_list_tools(self, mcp):
        proc, _ = mcp
        resp = proc.send("tools/list")
        assert "result" in resp
        tools = resp["result"].get("tools", [])
        assert len(tools) > 0
        # Verify tools have required fields
        for tool in tools:
            assert "name" in tool
            assert "description" in tool

    def test_mcp_list_resources(self, mcp):
        proc, _ = mcp
        resp = proc.send("resources/list")
        assert "result" in resp
        resources = resp["result"].get("resources", [])
        assert len(resources) > 0

    def test_mcp_call_tool(self, mcp):
        proc, _ = mcp
        resp = proc.send("tools/call", {
            "name": "manage_entities",
            "arguments": {"action": "list", "limit": "5"},
        })
        # Should get a result (even if empty data)
        assert "result" in resp or "error" in resp
        if "result" in resp:
            content = resp["result"].get("content", [])
            assert len(content) > 0
