//! Logging utilities for Veza services
//! 
//! This module provides utility functions for structured logging across
//! all Veza services.

use std::collections::HashMap;
use std::fmt::Display;

/// Log a request with structured information
/// 
/// # Examples
/// ```
/// use veza_common::utils::logging::log_request;
/// 
/// log_request("api", "GET", "/users");
/// ```
pub fn log_request(service: &str, method: &str, path: &str) {
    // Uses eprintln! for compatibility, can be replaced with tracing/log in implementations
    eprintln!("[{}] {} {} - Request received", service, method, path);
}

/// Log a request with additional context
/// 
/// # Examples
/// ```
/// use veza_common::utils::logging::log_request_with_context;
/// use std::collections::HashMap;
/// 
/// let mut context = HashMap::new();
/// context.insert("user_id".to_string(), "123".to_string());
/// context.insert("ip".to_string(), "192.168.1.1".to_string());
/// 
/// log_request_with_context("api", "GET", "/users", &context);
/// ```
pub fn log_request_with_context(
    service: &str,
    method: &str,
    path: &str,
    context: &HashMap<String, String>,
) {
    eprint!("[{}] {} {} - Request received", service, method, path);
    if !context.is_empty() {
        eprint!(" | Context: ");
        for (key, value) in context {
            eprint!("{}={} ", key, value);
        }
    }
    eprintln!();
}

/// Log an error with context
/// 
/// # Examples
/// ```
/// use veza_common::utils::logging::log_error;
/// 
/// log_error("api", "Database connection failed", None);
/// ```
pub fn log_error(service: &str, message: &str, error: Option<&dyn Display>) {
    if let Some(err) = error {
        eprintln!("[ERROR] [{}] {} - Error: {}", service, message, err);
    } else {
        eprintln!("[ERROR] [{}] {}", service, message);
    }
}

/// Log a warning with context
/// 
/// # Examples
/// ```
/// use veza_common::utils::logging::log_warning;
/// 
/// log_warning("api", "Rate limit approaching");
/// ```
pub fn log_warning(service: &str, message: &str) {
    eprintln!("[WARN] [{}] {}", service, message);
}

/// Log an info message
/// 
/// # Examples
/// ```
/// use veza_common::utils::logging::log_info;
/// 
/// log_info("api", "Service started");
/// ```
pub fn log_info(service: &str, message: &str) {
    eprintln!("[INFO] [{}] {}", service, message);
}

/// Log a debug message
/// 
/// # Examples
/// ```
/// use veza_common::utils::logging::log_debug;
/// 
/// log_debug("api", "Processing user request");
/// ```
pub fn log_debug(service: &str, message: &str) {
    eprintln!("[DEBUG] [{}] {}", service, message);
}

/// Create a formatted log message
/// 
/// # Examples
/// ```
/// use veza_common::utils::logging::format_log_message;
/// 
/// let message = format_log_message("api", "INFO", "Service started");
/// assert!(message.contains("api"));
/// assert!(message.contains("INFO"));
/// ```
pub fn format_log_message(service: &str, level: &str, message: &str) -> String {
    format!("[{}] [{}] {}", level, service, message)
}

/// Create a formatted log message with timestamp
/// 
/// # Examples
/// ```
/// use veza_common::utils::logging::format_log_message_with_timestamp;
/// 
/// let message = format_log_message_with_timestamp("api", "INFO", "Service started");
/// assert!(message.contains("api"));
/// assert!(message.contains("INFO"));
/// ```
pub fn format_log_message_with_timestamp(service: &str, level: &str, message: &str) -> String {
    use chrono::Utc;
    let timestamp = Utc::now().format("%Y-%m-%d %H:%M:%S UTC");
    format!("[{}] [{}] [{}] {}", timestamp, level, service, message)
}

/// Create a structured log entry
/// 
/// # Examples
/// ```
/// use veza_common::utils::logging::StructuredLogEntry;
/// 
/// let entry = StructuredLogEntry::new("api", "INFO", "Service started");
/// let json = entry.to_json();
/// assert!(json.contains("api"));
/// ```
#[derive(Debug, Clone)]
pub struct StructuredLogEntry {
    pub timestamp: String,
    pub service: String,
    pub level: String,
    pub message: String,
    pub context: HashMap<String, String>,
}

impl StructuredLogEntry {
    /// Create a new structured log entry
    pub fn new(service: &str, level: &str, message: &str) -> Self {
        use chrono::Utc;
        Self {
            timestamp: Utc::now().to_rfc3339(),
            service: service.to_string(),
            level: level.to_string(),
            message: message.to_string(),
            context: HashMap::new(),
        }
    }

    /// Add context to the log entry
    pub fn with_context(mut self, key: String, value: String) -> Self {
        self.context.insert(key, value);
        self
    }

    /// Add multiple context fields
    pub fn with_contexts(mut self, contexts: HashMap<String, String>) -> Self {
        self.context.extend(contexts);
        self
    }

    /// Convert to JSON string
    pub fn to_json(&self) -> String {
        use serde_json::json;
        let mut json_obj = json!({
            "timestamp": self.timestamp,
            "service": self.service,
            "level": self.level,
            "message": self.message,
        });
        
        if !self.context.is_empty() {
            json_obj["context"] = json!(self.context);
        }
        
        serde_json::to_string(&json_obj).unwrap_or_else(|_| "{}".to_string())
    }

    /// Convert to formatted string
    pub fn to_string(&self) -> String {
        format_log_message_with_timestamp(&self.service, &self.level, &self.message)
    }
}

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

    #[test]
    fn test_format_log_message() {
        let message = format_log_message("api", "INFO", "Test message");
        assert!(message.contains("api"));
        assert!(message.contains("INFO"));
        assert!(message.contains("Test message"));
    }

    #[test]
    fn test_format_log_message_with_timestamp() {
        let message = format_log_message_with_timestamp("api", "INFO", "Test message");
        assert!(message.contains("api"));
        assert!(message.contains("INFO"));
        assert!(message.contains("Test message"));
        assert!(message.contains("UTC"));
    }

    #[test]
    fn test_structured_log_entry_new() {
        let entry = StructuredLogEntry::new("api", "INFO", "Test message");
        assert_eq!(entry.service, "api");
        assert_eq!(entry.level, "INFO");
        assert_eq!(entry.message, "Test message");
        assert!(!entry.timestamp.is_empty());
        assert!(entry.context.is_empty());
    }

    #[test]
    fn test_structured_log_entry_with_context() {
        let entry = StructuredLogEntry::new("api", "INFO", "Test message")
            .with_context("user_id".to_string(), "123".to_string());
        
        assert_eq!(entry.context.get("user_id"), Some(&"123".to_string()));
    }

    #[test]
    fn test_structured_log_entry_with_contexts() {
        let mut contexts = HashMap::new();
        contexts.insert("user_id".to_string(), "123".to_string());
        contexts.insert("ip".to_string(), "192.168.1.1".to_string());
        
        let entry = StructuredLogEntry::new("api", "INFO", "Test message")
            .with_contexts(contexts);
        
        assert_eq!(entry.context.len(), 2);
        assert_eq!(entry.context.get("user_id"), Some(&"123".to_string()));
        assert_eq!(entry.context.get("ip"), Some(&"192.168.1.1".to_string()));
    }

    #[test]
    fn test_structured_log_entry_to_json() {
        let entry = StructuredLogEntry::new("api", "INFO", "Test message")
            .with_context("user_id".to_string(), "123".to_string());
        
        let json = entry.to_json();
        assert!(json.contains("api"));
        assert!(json.contains("INFO"));
        assert!(json.contains("Test message"));
        assert!(json.contains("user_id"));
        assert!(json.contains("123"));
    }

    #[test]
    fn test_structured_log_entry_to_string() {
        let entry = StructuredLogEntry::new("api", "INFO", "Test message");
        let formatted = entry.to_string();
        assert!(formatted.contains("api"));
        assert!(formatted.contains("INFO"));
        assert!(formatted.contains("Test message"));
    }

    #[test]
    fn test_log_request() {
        // This test just verifies the function doesn't panic
        log_request("api", "GET", "/users");
    }

    #[test]
    fn test_log_request_with_context() {
        let mut context = HashMap::new();
        context.insert("user_id".to_string(), "123".to_string());
        
        // This test just verifies the function doesn't panic
        log_request_with_context("api", "GET", "/users", &context);
    }

    #[test]
    fn test_log_error() {
        // This test just verifies the function doesn't panic
        log_error("api", "Test error", None);
        log_error("api", "Test error with details", Some(&"Connection timeout"));
    }

    #[test]
    fn test_log_warning() {
        // This test just verifies the function doesn't panic
        log_warning("api", "Test warning");
    }

    #[test]
    fn test_log_info() {
        // This test just verifies the function doesn't panic
        log_info("api", "Test info");
    }

    #[test]
    fn test_log_debug() {
        // This test just verifies the function doesn't panic
        log_debug("api", "Test debug");
    }
}

