//! MCP capability negotiation types
//!
//! Capabilities define what features both client and server support.
//! These are exchanged during the initialize handshake.

use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;

use super::types::Implementation;

/// Server capabilities
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ServerCapabilities {
    /// Logging capability
    #[serde(skip_serializing_if = "Option::is_none")]
    pub logging: Option<LoggingCapability>,

    /// Tools capability
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tools: Option<ToolsCapability>,

    /// Resources capability
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resources: Option<ResourcesCapability>,

    /// Prompts capability
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prompts: Option<PromptsCapability>,

    /// Completion capability
    #[serde(skip_serializing_if = "Option::is_none")]
    pub completion: Option<CompletionCapability>,

    /// Tasks capability (supports async task execution)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tasks: Option<TasksCapability>,

    /// Experimental/custom capabilities
    #[serde(skip_serializing_if = "Option::is_none")]
    pub experimental: Option<HashMap<String, Value>>,
}

/// Client capabilities
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientCapabilities {
    /// Roots capability
    #[serde(skip_serializing_if = "Option::is_none")]
    pub roots: Option<RootsCapability>,

    /// Sampling capability (client can perform LLM sampling)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sampling: Option<SamplingCapability>,

    /// Elicitation capability (client can request user input)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub elicitation: Option<ElicitationCapability>,

    /// Tasks capability (supports async task execution)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tasks: Option<TasksCapability>,

    /// Experimental/custom capabilities
    #[serde(skip_serializing_if = "Option::is_none")]
    pub experimental: Option<HashMap<String, Value>>,
}

/// Logging capability
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingCapability {}

/// Tools capability
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolsCapability {
    /// Whether tools support list changes notification
    #[serde(skip_serializing_if = "Option::is_none")]
    pub list_changed: Option<bool>,
}

/// Resources capability
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResourcesCapability {
    /// Whether resources support subscription
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subscribe: Option<bool>,

    /// Whether resources support list changes notification
    #[serde(skip_serializing_if = "Option::is_none")]
    pub list_changed: Option<bool>,

    /// Whether resource templates are supported
    #[serde(skip_serializing_if = "Option::is_none")]
    pub list_templates: Option<bool>,
}

/// Prompts capability
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PromptsCapability {
    /// Whether prompts support list changes notification
    #[serde(skip_serializing_if = "Option::is_none")]
    pub list_changed: Option<bool>,
}

/// Completion capability (autocomplete)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompletionCapability {}

/// Roots capability (client-provided workspace roots)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RootsCapability {
    /// Whether roots support list changes notification
    #[serde(skip_serializing_if = "Option::is_none")]
    pub list_changed: Option<bool>,
}

/// Sampling capability
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SamplingCapability {}

/// Elicitation capability
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ElicitationCapability {}

/// Tasks capability
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TasksCapability {
    /// Whether this party supports tasks/list
    #[serde(skip_serializing_if = "Option::is_none")]
    pub list: Option<EmptyObject>,

    /// Whether this party supports tasks/cancel
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cancel: Option<EmptyObject>,

    /// Specifies which request types can be augmented with tasks
    #[serde(skip_serializing_if = "Option::is_none")]
    pub requests: Option<TasksRequestsCapability>,
}

/// Requests that can be task-augmented
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TasksRequestsCapability {
    /// Task support for tool-related requests (server-side)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tools: Option<TasksToolsCapability>,

    /// Task support for sampling-related requests (client-side)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sampling: Option<TasksSamplingCapability>,

    /// Task support for elicitation-related requests (client-side)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub elicitation: Option<TasksElicitationCapability>,
}

/// Task support for tool requests
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TasksToolsCapability {
    /// Whether tools/call supports task-augmented execution
    #[serde(skip_serializing_if = "Option::is_none")]
    pub call: Option<EmptyObject>,
}

/// Task support for sampling requests
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TasksSamplingCapability {
    /// Whether sampling/createMessage supports task-augmented execution
    #[serde(skip_serializing_if = "Option::is_none")]
    pub create_message: Option<EmptyObject>,
}

/// Task support for elicitation requests
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TasksElicitationCapability {
    /// Whether elicitation/create supports task-augmented execution
    #[serde(skip_serializing_if = "Option::is_none")]
    pub create: Option<EmptyObject>,
}

/// Empty object placeholder (used for boolean-like capabilities)
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EmptyObject {}

/// Initialize request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeRequest {
    /// Protocol version
    pub protocol_version: String,

    /// Client capabilities
    pub capabilities: ClientCapabilities,

    /// Client implementation info
    pub client_info: Implementation,
}

/// Initialize result
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeResult {
    /// Protocol version (negotiated)
    pub protocol_version: String,

    /// Server capabilities
    pub capabilities: ServerCapabilities,

    /// Server implementation info
    pub server_info: Implementation,

    /// Optional server instructions
    #[serde(skip_serializing_if = "Option::is_none")]
    pub instructions: Option<String>,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_server_capabilities_default() {
        let caps = ServerCapabilities::default();
        assert!(caps.tools.is_none());
        assert!(caps.resources.is_none());
    }

    #[test]
    fn test_server_capabilities_serialization() {
        let caps = ServerCapabilities {
            tools: Some(ToolsCapability {
                list_changed: Some(true),
            }),
            resources: Some(ResourcesCapability {
                subscribe: Some(true),
                list_changed: Some(true),
                list_templates: Some(true),
            }),
            ..Default::default()
        };

        let json = serde_json::to_value(&caps).unwrap();
        assert!(json["tools"].is_object());
        assert!(json["resources"].is_object());
        assert_eq!(json["tools"]["listChanged"], true);
        assert_eq!(json["resources"]["listTemplates"], true);
    }

    #[test]
    fn test_initialize_request_deserialization() {
        let json = serde_json::json!({
            "protocolVersion": "2025-11-25",
            "capabilities": {},
            "clientInfo": {
                "name": "test-client",
                "version": "1.0.0"
            }
        });

        let req: InitializeRequest = serde_json::from_value(json).unwrap();
        assert_eq!(req.protocol_version, "2025-11-25");
        assert_eq!(req.client_info.name, "test-client");
    }
}
