//! Security utilities for HTTP transport
//!
//! Per MCP spec 2025-03-26 security requirements:
//! - MUST validate Origin header on all incoming connections (prevent DNS rebinding)
//! - SHOULD bind to localhost (127.0.0.1) for local servers, not 0.0.0.0
//! - MUST implement proper authentication for all connections

use super::types::{HttpTransportConfig, HttpTransportError};

/// Validate Origin header per MCP security requirements
///
/// Per MCP spec:
/// - Servers MUST validate the Origin header to prevent DNS rebinding attacks
/// - For localhost servers: accept localhost origins
/// - For remote servers: check against allowed_origins whitelist
///
/// # Arguments
///
/// * `origin` - Origin header value from request (None if header not present)
/// * `config` - HTTP transport configuration
///
/// # Returns
///
/// Ok(()) if origin is allowed, Err otherwise
pub fn validate_origin(
    origin: Option<&str>,
    config: &HttpTransportConfig,
) -> Result<(), HttpTransportError> {
    match origin {
        None => {
            // No Origin header - likely non-browser client (curl, MCP client)
            // Per spec: browsers always send Origin, so this is safe to allow
            tracing::debug!("Request without Origin header (non-browser client)");
            Ok(())
        }
        Some(origin_value) => {
            // Localhost origins always allowed if localhost_only mode
            if config.localhost_only && is_localhost_origin(origin_value) {
                tracing::debug!(origin = origin_value, "Allowed localhost origin");
                return Ok(());
            }

            // Check allowed origins whitelist
            if config.allowed_origins.is_empty() {
                // No allowed origins configured
                if config.localhost_only {
                    // In localhost mode, accept localhost origins only
                    if is_localhost_origin(origin_value) {
                        return Ok(());
                    }
                }

                // Reject browser requests when no origins configured
                tracing::warn!(
                    origin = origin_value,
                    "Rejected browser request: no allowed origins configured"
                );
                return Err(HttpTransportError::OriginNotAllowed(
                    origin_value.to_string(),
                ));
            }

            // Check if origin is in whitelist
            if config.allowed_origins.contains(&origin_value.to_string()) {
                tracing::debug!(origin = origin_value, "Allowed whitelisted origin");
                Ok(())
            } else {
                tracing::warn!(
                    origin = origin_value,
                    allowed = ?config.allowed_origins,
                    "Rejected origin: not in whitelist"
                );
                Err(HttpTransportError::OriginNotAllowed(
                    origin_value.to_string(),
                ))
            }
        }
    }
}

/// Check if origin is a localhost origin
///
/// Accepts:
/// - http://localhost[:port]
/// - https://localhost[:port]
/// - http://127.0.0.1[:port]
/// - https://127.0.0.1[:port]
/// - http://[::1][:port] (IPv6 localhost)
/// - https://[::1][:port]
fn is_localhost_origin(origin: &str) -> bool {
    origin.starts_with("http://localhost")
        || origin.starts_with("https://localhost")
        || origin.starts_with("http://127.0.0.1")
        || origin.starts_with("https://127.0.0.1")
        || origin.starts_with("http://[::1]")
        || origin.starts_with("https://[::1]")
}

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

    #[test]
    fn test_is_localhost_origin() {
        assert!(is_localhost_origin("http://localhost"));
        assert!(is_localhost_origin("http://localhost:8080"));
        assert!(is_localhost_origin("https://localhost"));
        assert!(is_localhost_origin("https://localhost:8080"));
        assert!(is_localhost_origin("http://127.0.0.1"));
        assert!(is_localhost_origin("http://127.0.0.1:8080"));
        assert!(is_localhost_origin("https://127.0.0.1"));
        assert!(is_localhost_origin("https://127.0.0.1:8080"));
        assert!(is_localhost_origin("http://[::1]"));
        assert!(is_localhost_origin("http://[::1]:8080"));
        assert!(is_localhost_origin("https://[::1]"));
        assert!(is_localhost_origin("https://[::1]:8080"));

        assert!(!is_localhost_origin("http://example.com"));
        assert!(!is_localhost_origin("https://evil.com"));
        assert!(!is_localhost_origin("http://192.168.1.1"));
    }

    #[test]
    fn test_validate_origin_no_header() {
        let config = HttpTransportConfig::default();

        // No Origin header - allow (non-browser client)
        assert!(validate_origin(None, &config).is_ok());
    }

    #[test]
    fn test_validate_origin_localhost_mode() {
        let config = HttpTransportConfig {
            localhost_only: true,
            allowed_origins: vec![],
            ..Default::default()
        };

        // Localhost origins allowed
        assert!(validate_origin(Some("http://localhost"), &config).is_ok());
        assert!(validate_origin(Some("http://localhost:8080"), &config).is_ok());
        assert!(validate_origin(Some("https://127.0.0.1"), &config).is_ok());
        assert!(validate_origin(Some("http://[::1]:3000"), &config).is_ok());

        // Remote origins rejected
        assert!(validate_origin(Some("http://example.com"), &config).is_err());
        assert!(validate_origin(Some("https://evil.com"), &config).is_err());
    }

    #[test]
    fn test_validate_origin_whitelist() {
        let config = HttpTransportConfig {
            localhost_only: false,
            allowed_origins: vec![
                "https://example.com".to_string(),
                "https://app.example.com".to_string(),
            ],
            ..Default::default()
        };

        // Whitelisted origins allowed
        assert!(validate_origin(Some("https://example.com"), &config).is_ok());
        assert!(validate_origin(Some("https://app.example.com"), &config).is_ok());

        // Non-whitelisted origins rejected
        assert!(validate_origin(Some("https://evil.com"), &config).is_err());
        assert!(validate_origin(Some("http://example.com"), &config).is_err()); // Different scheme
    }

    #[test]
    fn test_validate_origin_localhost_with_whitelist() {
        let config = HttpTransportConfig {
            localhost_only: true,
            allowed_origins: vec!["https://example.com".to_string()],
            ..Default::default()
        };

        // Localhost origins always allowed in localhost mode
        assert!(validate_origin(Some("http://localhost"), &config).is_ok());

        // Whitelisted remote origins also allowed
        assert!(validate_origin(Some("https://example.com"), &config).is_ok());

        // Non-whitelisted remote origins rejected
        assert!(validate_origin(Some("https://evil.com"), &config).is_err());
    }

    #[test]
    fn test_validate_origin_no_whitelist_remote_mode() {
        let config = HttpTransportConfig {
            localhost_only: false,
            allowed_origins: vec![],
            ..Default::default()
        };

        // No whitelist in remote mode - reject all browser requests
        assert!(validate_origin(Some("http://localhost"), &config).is_err());
        assert!(validate_origin(Some("https://example.com"), &config).is_err());

        // No Origin header still allowed (non-browser)
        assert!(validate_origin(None, &config).is_ok());
    }
}
