//! Integration tests for MCP Gateway

use mcp_gateway::config::{CircuitBreakerConfig, Config, RetryConfig};
use mcp_gateway::failsafe::{CircuitBreaker, CircuitState, RetryPolicy, with_retry};
use mcp_gateway::protocol::{
    JsonRpcRequest, JsonRpcResponse, PROTOCOL_VERSION, RequestId, SUPPORTED_VERSIONS,
    negotiate_version,
};
use pretty_assertions::assert_eq;
use std::time::Duration;

#[test]
fn test_protocol_version() {
    // Latest protocol version (2025-11-25)
    assert_eq!(PROTOCOL_VERSION, "2025-11-25");
    // Supported versions include latest and all previous
    assert!(SUPPORTED_VERSIONS.contains(&"2025-11-25"));
    assert!(SUPPORTED_VERSIONS.contains(&"2025-06-18"));
    assert!(SUPPORTED_VERSIONS.contains(&"2025-03-26"));
    assert!(SUPPORTED_VERSIONS.contains(&"2024-11-05"));
    assert!(SUPPORTED_VERSIONS.contains(&"2024-10-07"));
}

#[test]
fn test_version_negotiation() {
    // Client requests supported version - gets it back
    assert_eq!(negotiate_version("2025-11-25"), "2025-11-25");
    assert_eq!(negotiate_version("2025-06-18"), "2025-06-18");
    assert_eq!(negotiate_version("2025-03-26"), "2025-03-26");
    assert_eq!(negotiate_version("2024-11-05"), "2024-11-05");
    assert_eq!(negotiate_version("2024-10-07"), "2024-10-07");

    // Client requests unknown version - gets latest as fallback
    assert_eq!(negotiate_version("2023-01-01"), "2025-11-25");
    assert_eq!(negotiate_version("unknown"), "2025-11-25");
}

#[test]
fn test_request_id_display() {
    let id_num = RequestId::Number(42);
    assert_eq!(id_num.to_string(), "42");

    let id_str = RequestId::String("test-123".to_string());
    assert_eq!(id_str.to_string(), "test-123");
}

#[test]
fn test_json_rpc_request_serialization() {
    let request = JsonRpcRequest {
        jsonrpc: "2.0".to_string(),
        id: RequestId::Number(1),
        method: "tools/list".to_string(),
        params: None,
    };

    let json = serde_json::to_string(&request).unwrap();
    assert!(json.contains("\"jsonrpc\":\"2.0\""));
    assert!(json.contains("\"method\":\"tools/list\""));
    assert!(json.contains("\"id\":1"));
}

#[test]
fn test_json_rpc_response_success() {
    let response = JsonRpcResponse::success(RequestId::Number(1), serde_json::json!({"tools": []}));

    assert!(response.error.is_none());
    assert!(response.result.is_some());
    assert_eq!(response.id, Some(RequestId::Number(1)));
}

#[test]
fn test_json_rpc_response_error() {
    let response = JsonRpcResponse::error(Some(RequestId::Number(1)), -32600, "Invalid request");

    assert!(response.result.is_none());
    assert!(response.error.is_some());
    let error = response.error.unwrap();
    assert_eq!(error.code, -32600);
    assert_eq!(error.message, "Invalid request");
}

#[test]
fn test_config_defaults() {
    let config = Config::default();

    assert_eq!(config.server.host, "127.0.0.1");
    assert_eq!(config.server.port, 39400);
    assert!(config.meta_mcp.enabled);
    assert!(config.failsafe.circuit_breaker.enabled);
    assert!(config.failsafe.retry.enabled);
}

#[test]
fn test_circuit_breaker_starts_closed() {
    let config = CircuitBreakerConfig {
        enabled: true,
        failure_threshold: 5,
        success_threshold: 3,
        reset_timeout: Duration::from_secs(30),
    };
    let cb = CircuitBreaker::new("test", &config);
    assert_eq!(cb.state(), CircuitState::Closed);
}

#[test]
fn test_circuit_breaker_opens_after_failures() {
    let config = CircuitBreakerConfig {
        enabled: true,
        failure_threshold: 3,
        success_threshold: 2,
        reset_timeout: Duration::from_secs(30),
    };
    let cb = CircuitBreaker::new("test", &config);

    // Record failures
    cb.record_failure("test", Duration::ZERO);
    cb.record_failure("test", Duration::ZERO);
    assert_eq!(cb.state(), CircuitState::Closed); // Still closed

    cb.record_failure("test", Duration::ZERO); // Third failure
    assert_eq!(cb.state(), CircuitState::Open); // Now open
}

#[test]
fn test_circuit_breaker_disabled() {
    let config = CircuitBreakerConfig {
        enabled: false, // Disabled
        failure_threshold: 1,
        success_threshold: 1,
        reset_timeout: Duration::from_secs(1),
    };
    let cb = CircuitBreaker::new("test", &config);

    // Even with failures, it stays closed when disabled
    cb.record_failure("test", Duration::ZERO);
    cb.record_failure("test", Duration::ZERO);
    assert!(cb.can_proceed());
}

#[tokio::test]
async fn test_retry_policy_retries_on_failure() {
    let config = RetryConfig {
        enabled: true,
        max_attempts: 3,
        initial_backoff: Duration::from_millis(1), // Fast for tests
        max_backoff: Duration::from_millis(10),
        multiplier: 2.0,
    };
    let policy = RetryPolicy::new(&config);

    let attempts = std::sync::atomic::AtomicU32::new(0);
    let result: Result<(), mcp_gateway::Error> = with_retry(&policy, "test", || {
        let attempt = attempts.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1;
        async move {
            if attempt < 3 {
                Err(mcp_gateway::Error::Transport("test".to_string()))
            } else {
                Ok(())
            }
        }
    })
    .await;

    assert!(result.is_ok());
    assert_eq!(attempts.load(std::sync::atomic::Ordering::SeqCst), 3);
}

#[tokio::test]
async fn test_retry_policy_disabled() {
    let config = RetryConfig {
        enabled: false, // Disabled
        max_attempts: 3,
        initial_backoff: Duration::from_millis(1),
        max_backoff: Duration::from_millis(10),
        multiplier: 2.0,
    };
    let policy = RetryPolicy::new(&config);

    let attempts = std::sync::atomic::AtomicU32::new(0);
    let result: Result<(), mcp_gateway::Error> = with_retry(&policy, "test", || {
        attempts.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
        async { Err(mcp_gateway::Error::Transport("test".to_string())) }
    })
    .await;

    // Only one attempt when disabled
    assert!(result.is_err());
    assert_eq!(attempts.load(std::sync::atomic::Ordering::SeqCst), 1);
}
