//! Protocol version negotiation
//!
//! MCP supports multiple protocol versions. This module handles version negotiation
//! between client and server.

/// Latest supported MCP protocol version
pub const LATEST_PROTOCOL_VERSION: &str = "2025-11-25";

// =============================================================================
// CONST STRING MACRO - Copied from rust-sdk
// =============================================================================

/// Macro for creating compile-time constant string types
///
/// This creates a zero-sized type that serializes/deserializes to a specific string value.
/// Useful for JSON schema type fields that must always be a specific constant.
#[macro_export]
macro_rules! const_string {
    ($name:ident = $value:literal) => {
        #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
        pub struct $name;

        impl $name {
            pub const VALUE: &'static str = $value;

            pub fn as_str(&self) -> &'static str {
                Self::VALUE
            }
        }

        impl serde::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: serde::Serializer,
            {
                $value.serialize(serializer)
            }
        }

        impl<'de> serde::Deserialize<'de> for $name {
            fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
            where
                D: serde::Deserializer<'de>,
            {
                let s: String = serde::Deserialize::deserialize(deserializer)?;
                if s == $value {
                    Ok($name)
                } else {
                    Err(serde::de::Error::custom(format!(
                        "expected const string value {:?}, but got {:?}",
                        $value, s
                    )))
                }
            }
        }
    };
}

// Re-export for use in submodules
pub use const_string;

/// JSON-RPC version (always "2.0")
pub const JSON_RPC_VERSION: &str = "2.0";

/// All supported protocol versions in order of preference
///
/// Order matters: first match wins during negotiation
pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &[
    "2025-11-25", // Latest with tasks, elicitation, icons
    "2025-06-18", // Previous stable version
];

/// Default minimum client versions
///
/// Can be overridden by specific implementations
pub const DEFAULT_MIN_CLIENT_VERSIONS: &[(&str, &str)] =
    &[("claude-code", "2.0.56"), ("codex-mcp-client", "0.63.0")];

/// Check if a protocol version is supported
pub fn is_supported_protocol_version(version: &str) -> bool {
    SUPPORTED_PROTOCOL_VERSIONS.contains(&version)
}

/// Negotiate protocol version between client and server
///
/// Returns the client's version if supported, otherwise returns an error with supported versions
pub fn negotiate_protocol_version(client_version: &str) -> Result<String, Vec<String>> {
    if is_supported_protocol_version(client_version) {
        Ok(client_version.to_string())
    } else {
        Err(SUPPORTED_PROTOCOL_VERSIONS
            .iter()
            .map(|v| v.to_string())
            .collect())
    }
}

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

    #[test]
    fn test_supported_version() {
        assert!(is_supported_protocol_version("2025-11-25"));
        assert!(is_supported_protocol_version("2025-06-18"));
        assert!(!is_supported_protocol_version("2024-01-01"));
    }

    #[test]
    fn test_negotiate_supported_version() {
        assert_eq!(
            negotiate_protocol_version("2025-11-25"),
            Ok("2025-11-25".to_string())
        );
        assert_eq!(
            negotiate_protocol_version("2025-06-18"),
            Ok("2025-06-18".to_string())
        );
    }

    #[test]
    fn test_negotiate_unsupported_version() {
        let result = negotiate_protocol_version("2024-01-01");
        assert!(result.is_err());
        let supported = result.unwrap_err();
        assert_eq!(supported, vec!["2025-11-25", "2025-06-18"]);
    }
}
