//! End-to-end test of `tools/list` cursor pagination against a real stdio MCP
//! subprocess.
//!
//! A tiny Python "server" returns its tools across two pages: the first
//! `tools/list` (no `cursor`) yields one tool plus a `nextCursor`; the follow-up
//! (with that `cursor`) yields the rest and no `nextCursor`. This exercises the
//! cursor loop in `McpServer::start`. Skipped if `python3` is absent.

use std::io::Write;
use std::process::Command;

use mcp_client::config::{McpServerConfig, McpTransport};
use mcp_client::server::McpServer;

const FAKE_SERVER: &str = r#"
import sys, json

def send(obj):
    sys.stdout.write(json.dumps(obj) + "\n")
    sys.stdout.flush()

def readline():
    line = sys.stdin.readline()
    if not line:
        return None
    line = line.strip()
    return line if line else readline()

while True:
    raw = readline()
    if raw is None:
        break
    msg = json.loads(raw)
    mid = msg.get("id")
    method = msg.get("method")
    if method == "initialize":
        send({"jsonrpc": "2.0", "id": mid, "result": {
            "protocolVersion": "2025-11-25", "capabilities": {},
            "serverInfo": {"name": "fake", "version": "0"}}})
    elif method == "notifications/initialized":
        pass
    elif method == "tools/list":
        cursor = (msg.get("params") or {}).get("cursor")
        if cursor is None:
            # Page 1: one tool + a cursor pointing at the next page.
            send({"jsonrpc": "2.0", "id": mid, "result": {
                "tools": [{"name": "alpha", "description": "first",
                           "inputSchema": {"type": "object", "properties": {}}}],
                "nextCursor": "page-2"}})
        elif cursor == "page-2":
            # Page 2: the rest, no further cursor → loop terminates.
            send({"jsonrpc": "2.0", "id": mid, "result": {
                "tools": [{"name": "beta",  "description": "second",
                           "inputSchema": {"type": "object", "properties": {}}},
                          {"name": "gamma", "description": "third",
                           "inputSchema": {"type": "object", "properties": {}}}]}})
        else:
            send({"jsonrpc": "2.0", "id": mid, "result": {"tools": []}})
    elif mid is not None:
        send({"jsonrpc": "2.0", "id": mid, "error": {"code": -32601, "message": "unknown"}})
"#;

fn python3_available() -> bool {
    Command::new("python3").arg("--version").output().is_ok()
}

#[tokio::test]
async fn tools_list_follows_next_cursor_across_pages() {
    if !python3_available() {
        eprintln!("python3 not found — skipping pagination integration test");
        return;
    }

    let script_path =
        std::env::temp_dir().join(format!("skald_paginate_{}.py", std::process::id()));
    std::fs::File::create(&script_path)
        .unwrap()
        .write_all(FAKE_SERVER.as_bytes())
        .unwrap();

    let cfg = McpServerConfig {
        name: "fake".to_string(),
        transport: McpTransport::Stdio,
        command: Some("python3".to_string()),
        args: Some(vec![script_path.to_string_lossy().to_string()]),
        env: None,
        url: None,
        api_key: None,
    };

    let server = McpServer::start(&cfg, None, None, None)
        .await
        .expect("server should start");

    let names: Vec<&str> = server.tools().iter().map(|t| t.name.as_str()).collect();
    assert_eq!(
        names,
        vec!["alpha", "beta", "gamma"],
        "all pages should be collected"
    );

    let _ = std::fs::remove_file(&script_path);
}
