//! HTTP transport types for MCP Streamable HTTP
//!
//! Implements types per MCP specification 2025-03-26:
//! https://modelcontextprotocol.io/specification/2025-03-26/basic/transports

use serde::{Deserialize, Serialize};
use std::time::Duration;
use thiserror::Error;

/// SSE Event with optional ID for resumability
///
/// Per MCP spec, SSE events follow the format:
/// ```text
/// id: unique-event-42
/// event: message
/// data: {"jsonrpc":"2.0","method":"..."}
///
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SseEvent {
    /// Optional event ID for resumability (Last-Event-ID)
    pub id: Option<String>,

    /// Optional event type
    pub event: Option<String>,

    /// Event data (JSON-RPC message as string)
    pub data: String,
}

impl SseEvent {
    /// Create a new SSE event
    pub fn new(data: String) -> Self {
        Self {
            id: None,
            event: None,
            data,
        }
    }

    /// Create event with ID for resumability
    pub fn with_id(mut self, id: String) -> Self {
        self.id = Some(id);
        self
    }

    /// Create event with type
    pub fn with_event(mut self, event: String) -> Self {
        self.event = Some(event);
        self
    }
}

/// HTTP transport configuration
///
/// Controls behavior of the MCP Streamable HTTP server:
/// - Network binding and endpoint routing
/// - Security (localhost-only, origin validation)
/// - Session management (timeouts, limits)
#[derive(Debug, Clone)]
pub struct HttpTransportConfig {
    /// Bind address (e.g., "127.0.0.1:62770")
    ///
    /// **Security**: For local servers, MUST bind to localhost (127.0.0.1)
    /// not 0.0.0.0 to prevent remote access
    pub bind_addr: String,

    /// MCP endpoint path (e.g., "/mcp", "/api/mcp")
    ///
    /// Default: "/mcp" per spec, but configurable for flexibility
    pub endpoint: String,

    /// Whether to bind only to localhost (127.0.0.1)
    ///
    /// **Security**: Set to true for local servers to prevent DNS rebinding attacks
    pub localhost_only: bool,

    /// Allowed origins for CORS/Origin validation
    ///
    /// **Security**: MCP spec requires Origin header validation.
    /// If empty and localhost_only=true, allows all localhost origins.
    /// If empty and localhost_only=false, rejects browser requests.
    pub allowed_origins: Vec<String>,

    /// Session timeout duration
    ///
    /// Sessions inactive for longer than this are eligible for cleanup
    pub session_timeout: Duration,

    /// Maximum concurrent SSE connections per session
    ///
    /// Per MCP spec, client MAY maintain multiple SSE streams.
    /// Limit to prevent resource exhaustion.
    pub max_sse_connections: usize,

    /// Event buffer size for resumability
    ///
    /// Number of recent events to buffer per session for Last-Event-ID replay
    pub event_buffer_size: usize,
}

impl Default for HttpTransportConfig {
    fn default() -> Self {
        Self {
            bind_addr: "127.0.0.1:62770".to_string(),
            endpoint: "/mcp".to_string(),
            localhost_only: true,
            allowed_origins: vec![],
            session_timeout: Duration::from_secs(3600), // 1 hour
            max_sse_connections: 10,
            event_buffer_size: 100,
        }
    }
}

/// HTTP transport errors
#[derive(Debug, Error)]
pub enum HttpTransportError {
    /// Session not found (HTTP 404)
    #[error("Session not found: {0}")]
    SessionNotFound(String),

    /// Missing Mcp-Session-Id header when required
    #[error("Missing Mcp-Session-Id header")]
    MissingSessionId,

    /// Origin header validation failed
    #[error("Origin not allowed: {0}")]
    OriginNotAllowed(String),

    /// Invalid JSON-RPC message
    #[error("Invalid JSON-RPC message: {0}")]
    InvalidJsonRpc(String),

    /// Session limit exceeded
    #[error("Too many SSE connections for session (limit: {0})")]
    TooManyConnections(usize),

    /// IO error
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    /// JSON serialization error
    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),

    /// HTTP error
    #[error("HTTP error: {0}")]
    Http(String),

    /// Internal error
    #[error("Internal error: {0}")]
    Internal(String),
}

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

    #[test]
    fn test_sse_event_creation() {
        let event = SseEvent::new("test data".to_string());
        assert_eq!(event.data, "test data");
        assert!(event.id.is_none());
        assert!(event.event.is_none());
    }

    #[test]
    fn test_sse_event_with_id() {
        let event = SseEvent::new("data".to_string()).with_id("event-42".to_string());
        assert_eq!(event.id, Some("event-42".to_string()));
    }

    #[test]
    fn test_sse_event_with_type() {
        let event = SseEvent::new("data".to_string()).with_event("message".to_string());
        assert_eq!(event.event, Some("message".to_string()));
    }

    #[test]
    fn test_config_defaults() {
        let config = HttpTransportConfig::default();
        assert_eq!(config.bind_addr, "127.0.0.1:62770");
        assert_eq!(config.endpoint, "/mcp");
        assert!(config.localhost_only);
        assert_eq!(config.allowed_origins.len(), 0);
        assert_eq!(config.session_timeout, Duration::from_secs(3600));
        assert_eq!(config.max_sse_connections, 10);
        assert_eq!(config.event_buffer_size, 100);
    }
}
