//! Logging infrastructure for MCP notifications
//!
//! Provides helpers for sending log messages via MCP `notifications/message`
//! that are visible to the LLM. Supports RFC 5424 severity levels and optional
//! rate limiting via throttle_machines.

use std::str::FromStr;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;

use crate::server::middleware::RateLimiter;
use crate::transport::traits::JsonRpcNotification;

/// Log level for MCP notifications (RFC 5424 compatible)
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum LogLevel {
    /// System is unusable (0)
    Emergency,
    /// Action must be taken immediately (1)
    Alert,
    /// Critical conditions (2)
    Critical,
    /// Error conditions (3)
    Error,
    /// Warning conditions (4)
    Warning,
    /// Normal but significant events (5)
    Notice,
    /// Informational messages (6)
    Info,
    /// Detailed debugging information (7)
    Debug,
}

impl LogLevel {
    /// Get the string representation for JSON-RPC
    pub fn as_str(&self) -> &'static str {
        match self {
            LogLevel::Emergency => "emergency",
            LogLevel::Alert => "alert",
            LogLevel::Critical => "critical",
            LogLevel::Error => "error",
            LogLevel::Warning => "warning",
            LogLevel::Notice => "notice",
            LogLevel::Info => "info",
            LogLevel::Debug => "debug",
        }
    }
}

impl FromStr for LogLevel {
    type Err = String;

    /// Parse level from string (case-insensitive)
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "emergency" => Ok(Self::Emergency),
            "alert" => Ok(Self::Alert),
            "critical" => Ok(Self::Critical),
            "error" => Ok(Self::Error),
            "warning" => Ok(Self::Warning),
            "notice" => Ok(Self::Notice),
            "info" => Ok(Self::Info),
            "debug" => Ok(Self::Debug),
            _ => Err(format!("Invalid log level: {}", s)),
        }
    }
}

impl std::fmt::Display for LogLevel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

/// Logger configuration with rate limiting and minimum level
#[derive(Clone)]
pub struct LoggerConfig {
    /// Minimum log level to send (default: Info)
    pub min_level: LogLevel,
    /// Optional rate limiter (uses throttle_machines GCRA)
    pub rate_limiter: Option<Arc<RateLimiter>>,
}

impl Default for LoggerConfig {
    fn default() -> Self {
        Self {
            min_level: LogLevel::Info,
            rate_limiter: None, // Disabled by default to avoid circular dependency
        }
    }
}

/// Logger for sending MCP notifications visible to the LLM
///
/// This logger sends `notifications/message` notifications that the LLM can see,
/// allowing tools and resources to communicate status information.
///
/// # Example
///
/// ```rust,ignore
/// let logger = McpLogger::new(notification_tx, "my-tool");
/// logger.info("Tool initialized successfully");
/// logger.warning("API rate limit approaching");
/// logger.error("Failed to connect to database");
/// ```
#[derive(Clone)]
pub struct McpLogger {
    tx: mpsc::UnboundedSender<JsonRpcNotification>,
    logger_name: String,
    config: Arc<Mutex<LoggerConfig>>,
}

impl McpLogger {
    /// Create a new MCP logger with default configuration
    pub fn new(
        tx: mpsc::UnboundedSender<JsonRpcNotification>,
        logger_name: impl Into<String>,
    ) -> Self {
        Self::with_config(tx, logger_name, LoggerConfig::default())
    }

    /// Create a new MCP logger with custom configuration
    pub fn with_config(
        tx: mpsc::UnboundedSender<JsonRpcNotification>,
        logger_name: impl Into<String>,
        config: LoggerConfig,
    ) -> Self {
        Self {
            tx,
            logger_name: logger_name.into(),
            config: Arc::new(Mutex::new(config)),
        }
    }

    /// Send a log message at the specified level (returns true if sent)
    pub fn log(&self, level: LogLevel, message: impl Into<String>) -> bool {
        // Check filtering
        {
            let config = self.config.lock().unwrap();
            if level > config.min_level {
                return false;
            }
            if let Some(ref limiter) = config.rate_limiter
                && limiter.check().is_err()
            {
                return false; // Rate limited
            }
        }

        let notification = JsonRpcNotification::new(
            "notifications/message",
            Some(serde_json::json!({
                "level": level.as_str(),
                "logger": self.logger_name,
                "data": message.into()
            })),
        );
        let _ = self.tx.send(notification);
        true
    }

    /// Set minimum log level
    pub fn set_min_level(&self, level: LogLevel) {
        self.config.lock().unwrap().min_level = level;
    }

    /// Send a custom notification (like notifications/tools/list_changed)
    ///
    /// This allows tools/resources to broadcast custom events to clients.
    ///
    /// # Example
    /// ```rust,ignore
    /// // Notify client that data has changed
    /// ctx.logger.send_notification("notifications/data/updated", Some(serde_json::json!({
    ///     "entity": "users",
    ///     "count": 42
    /// })));
    ///
    /// // Notify without params
    /// ctx.logger.send_notification("notifications/cache/cleared", None);
    /// ```
    pub fn send_notification(
        &self,
        method: impl Into<String>,
        params: Option<serde_json::Value>,
    ) -> bool {
        let notification = JsonRpcNotification::new(method, params);
        self.tx.send(notification).is_ok()
    }

    /// Log an emergency message
    pub fn emergency(&self, message: impl Into<String>) -> bool {
        self.log(LogLevel::Emergency, message)
    }

    /// Log an alert message
    pub fn alert(&self, message: impl Into<String>) -> bool {
        self.log(LogLevel::Alert, message)
    }

    /// Log a critical message
    pub fn critical(&self, message: impl Into<String>) -> bool {
        self.log(LogLevel::Critical, message)
    }

    /// Log an error message
    pub fn error(&self, message: impl Into<String>) -> bool {
        self.log(LogLevel::Error, message)
    }

    /// Log a warning message
    pub fn warning(&self, message: impl Into<String>) -> bool {
        self.log(LogLevel::Warning, message)
    }

    /// Log a notice message
    pub fn notice(&self, message: impl Into<String>) -> bool {
        self.log(LogLevel::Notice, message)
    }

    /// Log an info message
    pub fn info(&self, message: impl Into<String>) -> bool {
        self.log(LogLevel::Info, message)
    }

    /// Log a debug message
    pub fn debug(&self, message: impl Into<String>) -> bool {
        self.log(LogLevel::Debug, message)
    }

    /// Create a child logger with a sub-name
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// let parent = McpLogger::new(tx, "tool");
    /// let child = parent.child("database");
    /// // child's logger name is "tool.database"
    /// ```
    pub fn child(&self, name: impl Into<String>) -> Self {
        Self {
            tx: self.tx.clone(),
            logger_name: format!("{}.{}", self.logger_name, name.into()),
            config: Arc::clone(&self.config),
        }
    }
}

/// Send a single log notification without maintaining a logger instance
///
/// # Arguments
///
/// * `tx` - Notification channel
/// * `level` - Log level
/// * `logger` - Logger name
/// * `message` - Log message
pub fn send_log_notification(
    tx: &mpsc::UnboundedSender<JsonRpcNotification>,
    level: LogLevel,
    logger: &str,
    message: impl Into<String>,
) {
    let notification = JsonRpcNotification::new(
        "notifications/message",
        Some(serde_json::json!({
            "level": level.as_str(),
            "logger": logger,
            "data": message.into()
        })),
    );
    let _ = tx.send(notification);
}

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

    #[test]
    fn test_log_level_as_str() {
        assert_eq!(LogLevel::Debug.as_str(), "debug");
        assert_eq!(LogLevel::Info.as_str(), "info");
        assert_eq!(LogLevel::Warning.as_str(), "warning");
        assert_eq!(LogLevel::Error.as_str(), "error");
    }

    #[test]
    fn test_log_level_display() {
        assert_eq!(format!("{}", LogLevel::Info), "info");
    }

    #[test]
    fn test_logger_creation() {
        let (tx, _rx) = mpsc::unbounded_channel();
        let logger = McpLogger::new(tx, "test-logger");
        assert_eq!(logger.logger_name, "test-logger");
    }

    #[test]
    fn test_child_logger() {
        let (tx, _rx) = mpsc::unbounded_channel();
        let parent = McpLogger::new(tx, "parent");
        let child = parent.child("child");
        assert_eq!(child.logger_name, "parent.child");
    }

    #[test]
    fn test_log_methods() {
        let (tx, mut rx) = mpsc::unbounded_channel();
        let config = LoggerConfig {
            min_level: LogLevel::Debug,
            rate_limiter: None,
        };
        let logger = McpLogger::with_config(tx, "test", config);

        logger.debug("debug message");
        logger.info("info message");
        logger.warning("warning message");
        logger.error("error message");

        // Verify we received 4 notifications
        let mut count = 0;
        while rx.try_recv().is_ok() {
            count += 1;
        }
        assert_eq!(count, 4);
    }

    #[test]
    fn test_send_log_notification() {
        let (tx, mut rx) = mpsc::unbounded_channel();
        send_log_notification(&tx, LogLevel::Info, "test", "test message");

        let notification = rx.try_recv().unwrap();
        assert_eq!(notification.method, "notifications/message");
    }
}
