//! Logging utilities for Veza Rust services
//! 
//! This module provides centralized logging configuration and utilities.

use tracing::{Level, Subscriber};
use tracing_subscriber::{
    fmt::{self, format::FmtSpan},
    layer::SubscriberExt,
    util::SubscriberInitExt,
    EnvFilter, Registry,
};

use crate::{VezaError, VezaResult};

/// Initialize logging for Veza services
pub fn init() -> VezaResult<()> {
    // Set default log level if not specified
    if std::env::var("RUST_LOG").is_err() {
        std::env::set_var("RUST_LOG", "info");
    }
    
    // Create environment filter
    let env_filter = EnvFilter::try_from_default_env()
        .unwrap_or_else(|_| EnvFilter::new("info"));
    
    // Create formatting layer
    let fmt_layer = fmt::layer()
        .with_target(true)
        .with_thread_ids(true)
        .with_thread_names(true)
        .with_span_events(FmtSpan::CLOSE)
        .with_file(true)
        .with_line_number(true)
        .with_ansi(true);
    
    // Initialize the subscriber
    Registry::default()
        .with(env_filter)
        .with(fmt_layer)
        .init();
    
    tracing::info!("Logging initialized");
    Ok(())
}

/// Initialize logging with custom configuration
pub fn init_with_config(config: LoggingConfig) -> VezaResult<()> {
    // Set log level
    std::env::set_var("RUST_LOG", &config.level);
    
    // Create environment filter
    let env_filter = EnvFilter::try_from_default_env()
        .unwrap_or_else(|_| EnvFilter::new(&config.level));
    
    // Create formatting layer based on format
    let fmt_layer = match config.format.as_str() {
        "json" => fmt::layer()
            .json()
            .with_target(true)
            .with_thread_ids(true)
            .with_thread_names(true)
            .with_span_events(FmtSpan::CLOSE)
            .with_file(true)
            .with_line_number(true)
            .boxed(),
        "text" => fmt::layer()
            .with_target(true)
            .with_thread_ids(true)
            .with_thread_names(true)
            .with_span_events(FmtSpan::CLOSE)
            .with_file(true)
            .with_line_number(true)
            .with_ansi(true)
            .boxed(),
        _ => return Err(VezaError::Config(format!("Invalid log format: {}", config.format))),
    };
    
    // Initialize the subscriber
    Registry::default()
        .with(env_filter)
        .with(fmt_layer)
        .init();
    
    tracing::info!("Logging initialized with custom config");
    Ok(())
}

/// Logging configuration
#[derive(Debug, Clone)]
pub struct LoggingConfig {
    pub level: String,
    pub format: String,
    pub file: Option<String>,
    pub max_size: u64,
    pub max_files: u32,
    pub compress: bool,
}

impl Default for LoggingConfig {
    fn default() -> Self {
        Self {
            level: "info".to_string(),
            format: "json".to_string(),
            file: None,
            max_size: 100 * 1024 * 1024, // 100MB
            max_files: 5,
            compress: true,
        }
    }
}

/// Structured logging macros
#[macro_export]
macro_rules! log_info {
    ($($arg:tt)*) => {
        tracing::info!($($arg)*)
    };
}

#[macro_export]
macro_rules! log_warn {
    ($($arg:tt)*) => {
        tracing::warn!($($arg)*)
    };
}

#[macro_export]
macro_rules! log_error {
    ($($arg:tt)*) => {
        tracing::error!($($arg)*)
    };
}

#[macro_export]
macro_rules! log_debug {
    ($($arg:tt)*) => {
        tracing::debug!($($arg)*)
    };
}

#[macro_export]
macro_rules! log_trace {
    ($($arg:tt)*) => {
        tracing::trace!($($arg)*)
    };
}

/// Log performance metrics
pub fn log_performance(operation: &str, duration: std::time::Duration, metadata: &[(&str, &dyn std::fmt::Display)]) {
    tracing::info!(
        operation = operation,
        duration_ms = duration.as_millis(),
        ?metadata,
        "Performance metric"
    );
}

/// Log security events
pub fn log_security_event(event: &str, user_id: Option<uuid::Uuid>, ip_address: Option<&str>, metadata: &[(&str, &dyn std::fmt::Display)]) {
    tracing::warn!(
        event = event,
        user_id = ?user_id,
        ip_address = ip_address,
        ?metadata,
        "Security event"
    );
}

/// Log business events
pub fn log_business_event(event: &str, user_id: Option<uuid::Uuid>, resource_id: Option<uuid::Uuid>, metadata: &[(&str, &dyn std::fmt::Display)]) {
    tracing::info!(
        event = event,
        user_id = ?user_id,
        resource_id = ?resource_id,
        ?metadata,
        "Business event"
    );
}

/// Log system events
pub fn log_system_event(event: &str, component: &str, metadata: &[(&str, &dyn std::fmt::Display)]) {
    tracing::info!(
        event = event,
        component = component,
        ?metadata,
        "System event"
    );
}

/// Log error with context
pub fn log_error_with_context(error: &VezaError, context: &[(&str, &dyn std::fmt::Display)]) {
    if error.should_log() {
        tracing::error!(
            error = %error,
            ?context,
            "Error occurred"
        );
    } else {
        tracing::debug!(
            error = %error,
            ?context,
            "Error occurred"
        );
    }
}

/// Log request/response
pub fn log_request_response(
    method: &str,
    path: &str,
    status_code: u16,
    duration: std::time::Duration,
    user_id: Option<uuid::Uuid>,
    request_id: Option<&str>,
) {
    let level = if status_code >= 500 {
        Level::ERROR
    } else if status_code >= 400 {
        Level::WARN
    } else {
        Level::INFO
    };
    
    tracing::event!(
        level,
        method = method,
        path = path,
        status_code = status_code,
        duration_ms = duration.as_millis(),
        user_id = ?user_id,
        request_id = request_id,
        "HTTP request"
    );
}

/// Log database query
pub fn log_db_query(
    query: &str,
    duration: std::time::Duration,
    rows_affected: Option<u64>,
    error: Option<&str>,
) {
    if let Some(error) = error {
        tracing::error!(
            query = query,
            duration_ms = duration.as_millis(),
            rows_affected = ?rows_affected,
            error = error,
            "Database query failed"
        );
    } else {
        tracing::debug!(
            query = query,
            duration_ms = duration.as_millis(),
            rows_affected = ?rows_affected,
            "Database query"
        );
    }
}

/// Log cache operations
pub fn log_cache_operation(
    operation: &str,
    key: &str,
    hit: bool,
    duration: std::time::Duration,
    error: Option<&str>,
) {
    if let Some(error) = error {
        tracing::warn!(
            operation = operation,
            key = key,
            hit = hit,
            duration_ms = duration.as_millis(),
            error = error,
            "Cache operation failed"
        );
    } else {
        tracing::debug!(
            operation = operation,
            key = key,
            hit = hit,
            duration_ms = duration.as_millis(),
            "Cache operation"
        );
    }
}

/// Log WebSocket events
pub fn log_websocket_event(
    event: &str,
    user_id: Option<uuid::Uuid>,
    connection_id: Option<uuid::Uuid>,
    metadata: &[(&str, &dyn std::fmt::Display)],
) {
    tracing::info!(
        event = event,
        user_id = ?user_id,
        connection_id = ?connection_id,
        ?metadata,
        "WebSocket event"
    );
}

/// Log streaming events
pub fn log_streaming_event(
    event: &str,
    track_id: Option<uuid::Uuid>,
    user_id: Option<uuid::Uuid>,
    metadata: &[(&str, &dyn std::fmt::Display)],
) {
    tracing::info!(
        event = event,
        track_id = ?track_id,
        user_id = ?user_id,
        ?metadata,
        "Streaming event"
    );
}

/// Create a span for tracing
pub fn create_span(name: &str, metadata: &[(&str, &dyn std::fmt::Display)]) -> tracing::Span {
    tracing::info_span!(name, ?metadata)
}

/// Log startup information
pub fn log_startup(service_name: &str, version: &str, config: &[(&str, &dyn std::fmt::Display)]) {
    tracing::info!(
        service = service_name,
        version = version,
        ?config,
        "Service starting"
    );
}

/// Log shutdown information
pub fn log_shutdown(service_name: &str, uptime: std::time::Duration) {
    tracing::info!(
        service = service_name,
        uptime_seconds = uptime.as_secs(),
        "Service shutting down"
    );
}
