//! Integration tests for the MCP sub-client.

use akribes_sdk::AkribesClient;
use akribes_sdk::models::McpOrigin;
use mockito::Server;

fn make_client(server: &Server) -> AkribesClient {
    AkribesClient::builder(server.url())
        .project_id(1)
        .name("test-app")
        .id("test-id")
        .build()
}

#[tokio::test]
async fn test_list_servers() {
    let mut server = Server::new_async().await;
    let body = r#"[
        {"alias":"github","url":"https://api.githubcopilot.com/mcp/","origin":"env","is_registry":false,"status":"connected","tool_count":12},
        {"alias":"context7","url":"https://mcp.context7.com/mcp","origin":"db","is_registry":true,"status":"degraded","tool_count":0}
    ]"#;
    let _m = server
        .mock("GET", "/projects/1/mcp/servers")
        .with_status(200)
        .with_header("content-type", "application/json")
        .with_body(body)
        .create_async()
        .await;

    let client = make_client(&server);
    let servers = client.project(1).mcp().list_servers().await.unwrap();
    assert_eq!(servers.len(), 2);
    assert_eq!(servers[0].alias, "github");
    assert!(matches!(servers[0].origin, McpOrigin::Env));
    assert_eq!(servers[0].tool_count, 12);
    assert!(matches!(servers[1].origin, McpOrigin::Db));
    assert!(servers[1].is_registry);
}

#[tokio::test]
async fn test_list_tools() {
    let mut server = Server::new_async().await;
    let body = r#"[
        {"qualified_name":"github.create_issue","server_alias":"github","description":"Create an issue","input_schema":{"type":"object","properties":{}}},
        {"qualified_name":"ctx.search","server_alias":"context7","input_schema":{"type":"object"}}
    ]"#;
    let _m = server
        .mock("GET", "/projects/1/mcp/tools")
        .with_status(200)
        .with_header("content-type", "application/json")
        .with_body(body)
        .create_async()
        .await;

    let client = make_client(&server);
    let tools = client.project(1).mcp().list_tools().await.unwrap();
    assert_eq!(tools.len(), 2);
    assert_eq!(tools[0].qualified_name, "github.create_issue");
    assert_eq!(tools[0].description.as_deref(), Some("Create an issue"));
    assert!(tools[1].description.is_none());
}

#[tokio::test]
async fn test_health() {
    let mut server = Server::new_async().await;
    let body = r#"{"status":"connected","last_error":null,"last_check_at":"2024-01-01T00:00:00Z"}"#;
    let _m = server
        .mock("GET", "/projects/1/mcp/servers/github/health")
        .with_status(200)
        .with_header("content-type", "application/json")
        .with_body(body)
        .create_async()
        .await;

    let client = make_client(&server);
    let health = client.project(1).mcp().health("github").await.unwrap();
    assert_eq!(health.status, "connected");
    assert_eq!(
        health.last_check_at.as_deref(),
        Some("2024-01-01T00:00:00Z")
    );
    assert!(health.last_error.is_none());
}

#[tokio::test]
async fn test_refresh_404_surfaces_http_status() {
    let mut server = Server::new_async().await;
    let _m = server
        .mock("POST", "/projects/1/mcp/servers/nope/refresh")
        .with_status(404)
        .with_body("server not found")
        .create_async()
        .await;

    let client = make_client(&server);
    let err = client.project(1).mcp().refresh("nope").await.unwrap_err();
    match err {
        akribes_sdk::AkribesError::HttpStatus { status, .. } => assert_eq!(status, 404),
        other => panic!("expected HttpStatus, got {:?}", other),
    }
}

#[tokio::test]
async fn test_refresh() {
    let mut server = Server::new_async().await;
    let body = r#"{"refreshed":true,"alias":"github","tool_count":12}"#;
    let _m = server
        .mock("POST", "/projects/1/mcp/servers/github/refresh")
        .with_status(200)
        .with_header("content-type", "application/json")
        .with_body(body)
        .create_async()
        .await;

    let client = make_client(&server);
    let result = client.project(1).mcp().refresh("github").await.unwrap();
    assert!(result.refreshed);
    assert_eq!(result.alias, "github");
    assert_eq!(result.tool_count, 12);
}

#[tokio::test]
async fn test_drift() {
    let mut server = Server::new_async().await;
    let body = r#"{"drifted":true,"added":["new_tool"],"removed":["old_tool"]}"#;
    let _m = server
        .mock("GET", "/projects/1/mcp/servers/github/drift")
        .with_status(200)
        .with_header("content-type", "application/json")
        .with_body(body)
        .create_async()
        .await;

    let client = make_client(&server);
    let drift = client.project(1).mcp().drift("github").await.unwrap();
    assert!(drift.drifted);
    assert_eq!(drift.added, vec!["new_tool"]);
    assert_eq!(drift.removed, vec!["old_tool"]);
}

#[tokio::test]
async fn test_drift_not_pinned() {
    let mut server = Server::new_async().await;
    let body = r#"{"drifted":false,"added":[],"removed":[],"reason":"not_pinned"}"#;
    let _m = server
        .mock("GET", "/projects/1/mcp/servers/github/drift")
        .with_status(200)
        .with_header("content-type", "application/json")
        .with_body(body)
        .create_async()
        .await;

    let client = make_client(&server);
    let drift = client.project(1).mcp().drift("github").await.unwrap();
    assert!(!drift.drifted);
    assert_eq!(drift.reason.as_deref(), Some("not_pinned"));
}

#[tokio::test]
async fn test_health_encodes_alias() {
    let mut server = Server::new_async().await;
    let body = r#"{"status":"offline"}"#;
    let _m = server
        .mock("GET", "/projects/1/mcp/servers/weird%2Falias/health")
        .with_status(200)
        .with_header("content-type", "application/json")
        .with_body(body)
        .create_async()
        .await;

    let client = make_client(&server);
    let health = client.project(1).mcp().health("weird/alias").await.unwrap();
    assert_eq!(health.status, "offline");
}
