//! Comprehensive logging and monitoring for apt-ostree


use tracing_subscriber::{
    fmt::{format::FmtSpan, time::UtcTime},
    layer::SubscriberExt,
    util::SubscriberInitExt,
    EnvFilter,
    Layer,
};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use serde::{Serialize, Deserialize};
use chrono::{DateTime, Utc};

/// Logging configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingConfig {
    pub level: String,
    pub format: LogFormat,
    pub output: LogOutput,
    pub file_path: Option<String>,
    pub max_file_size: Option<u64>,
    pub max_files: Option<usize>,
    pub enable_metrics: bool,
    pub enable_health_checks: bool,
}

/// Log format options
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LogFormat {
    Json,
    Text,
    Compact,
}

/// Log output options
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LogOutput {
    Console,
    File,
    Both,
}

/// Metrics collection
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Metrics {
    pub operation_count: u64,
    pub error_count: u64,
    pub success_count: u64,
    pub operation_times: HashMap<String, Vec<f64>>,
    pub last_operation: Option<DateTime<Utc>>,
    pub system_health: SystemHealth,
}

/// System health information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemHealth {
    pub status: HealthStatus,
    pub last_check: DateTime<Utc>,
    pub checks: HashMap<String, HealthCheck>,
}

/// Health status
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum HealthStatus {
    Healthy,
    Warning,
    Critical,
    Unknown,
}

/// Individual health check
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthCheck {
    pub status: HealthStatus,
    pub message: String,
    pub last_check: DateTime<Utc>,
    pub details: Option<HashMap<String, String>>,
}

/// Performance metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
    pub operation_name: String,
    pub duration_ms: f64,
    pub timestamp: DateTime<Utc>,
    pub metadata: HashMap<String, String>,
}

/// Logging manager
pub struct LoggingManager {
    config: LoggingConfig,
    metrics: Arc<RwLock<Metrics>>,
    performance_tracker: Arc<RwLock<Vec<PerformanceMetrics>>>,
}

impl LoggingManager {
    /// Create a new logging manager
    pub fn new(config: LoggingConfig) -> Self {
        Self {
            config,
            metrics: Arc::new(RwLock::new(Metrics::new())),
            performance_tracker: Arc::new(RwLock::new(Vec::new())),
        }
    }

    /// Initialize the logging system
    pub fn init(&self) -> Result<(), Box<dyn std::error::Error>> {
        let env_filter = EnvFilter::try_from_default_env()
            .unwrap_or_else(|_| EnvFilter::new(&self.config.level));

        let mut layers = Vec::new();

        // Console layer
        if matches!(self.config.output, LogOutput::Console | LogOutput::Both) {
            let console_layer = tracing_subscriber::fmt::layer()
                .with_timer(UtcTime::rfc_3339())
                .with_span_events(FmtSpan::CLOSE)
                .with_target(true)
                .with_thread_ids(true)
                .with_thread_names(true);

            layers.push(console_layer.boxed());
        }

        // File layer
        if matches!(self.config.output, LogOutput::File | LogOutput::Both) {
            if let Some(file_path) = &self.config.file_path {
                let file_appender = tracing_appender::rolling::RollingFileAppender::builder()
                    .rotation(tracing_appender::rolling::Rotation::DAILY)
                    .max_log_files(self.config.max_files.unwrap_or(7))
                    .build(file_path)?;

                let file_layer = tracing_subscriber::fmt::layer()
                    .with_timer(UtcTime::rfc_3339())
                    .with_span_events(FmtSpan::CLOSE)
                    .with_target(true)
                    .with_thread_ids(true)
                    .with_thread_names(true)
                    .with_writer(file_appender);

                layers.push(file_layer.boxed());
            }
        }

        // JSON layer for structured logging
        if matches!(self.config.format, LogFormat::Json) {
            let json_layer = tracing_subscriber::fmt::layer()
                .json()
                .with_timer(UtcTime::rfc_3339())
                .with_span_events(FmtSpan::CLOSE)
                .with_target(true)
                .with_thread_ids(true)
                .with_thread_names(true);

            layers.push(json_layer.boxed());
        }

        // Initialize the subscriber
        tracing_subscriber::registry()
            .with(env_filter)
            .with(layers)
            .init();

        tracing::info!("Logging system initialized with level: {}", self.config.level);
        Ok(())
    }

    /// Record an operation
    pub async fn record_operation(&self, operation_name: &str, success: bool, duration_ms: f64) {
        let mut metrics = self.metrics.write().await;
        metrics.operation_count += 1;
        
        if success {
            metrics.success_count += 1;
        } else {
            metrics.error_count += 1;
        }

        metrics.last_operation = Some(Utc::now());

        // Record operation time
        let times = metrics.operation_times.entry(operation_name.to_string()).or_insert_with(Vec::new);
        times.push(duration_ms);

        // Keep only last 1000 measurements
        if times.len() > 1000 {
            times.remove(0);
        }

        // Record performance metrics
        let mut performance = self.performance_tracker.write().await;
        performance.push(PerformanceMetrics {
            operation_name: operation_name.to_string(),
            duration_ms,
            timestamp: Utc::now(),
            metadata: HashMap::new(),
        });

        // Keep only last 10000 performance records
        if performance.len() > 10000 {
            performance.remove(0);
        }
    }

    /// Get current metrics
    pub async fn get_metrics(&self) -> Metrics {
        self.metrics.read().await.clone()
    }

    /// Get performance metrics for an operation
    pub async fn get_operation_metrics(&self, operation_name: &str) -> Vec<PerformanceMetrics> {
        let performance = self.performance_tracker.read().await;
        performance
            .iter()
            .filter(|m| m.operation_name == operation_name)
            .cloned()
            .collect()
    }

    /// Update system health
    pub async fn update_health(&self, check_name: &str, status: HealthStatus, message: &str, details: Option<HashMap<String, String>>) {
        let mut metrics = self.metrics.write().await;
        let health_check = HealthCheck {
            status: status.clone(),
            message: message.to_string(),
            last_check: Utc::now(),
            details,
        };

        metrics.system_health.checks.insert(check_name.to_string(), health_check);
        metrics.system_health.last_check = Utc::now();

        // Update overall health status
        metrics.system_health.status = self.calculate_overall_health(&metrics.system_health.checks);
    }

    /// Calculate overall health status
    fn calculate_overall_health(&self, checks: &HashMap<String, HealthCheck>) -> HealthStatus {
        let mut critical_count = 0;
        let mut warning_count = 0;
        let mut healthy_count = 0;

        for check in checks.values() {
            match check.status {
                HealthStatus::Critical => critical_count += 1,
                HealthStatus::Warning => warning_count += 1,
                HealthStatus::Healthy => healthy_count += 1,
                HealthStatus::Unknown => {}
            }
        }

        if critical_count > 0 {
            HealthStatus::Critical
        } else if warning_count > 0 {
            HealthStatus::Warning
        } else if healthy_count > 0 {
            HealthStatus::Healthy
        } else {
            HealthStatus::Unknown
        }
    }

    /// Get system health
    pub async fn get_system_health(&self) -> SystemHealth {
        self.metrics.read().await.system_health.clone()
    }

    /// Export metrics in Prometheus format
    pub async fn export_prometheus_metrics(&self) -> String {
        let metrics = self.metrics.read().await;
        let mut output = String::new();

        // Operation counts
        output.push_str("# HELP apt_ostree_operations_total Total number of operations\n");
        output.push_str("# TYPE apt_ostree_operations_total counter\n");
        output.push_str(&format!("apt_ostree_operations_total {}\n", metrics.operation_count));

        output.push_str("# HELP apt_ostree_operations_success_total Total number of successful operations\n");
        output.push_str("# TYPE apt_ostree_operations_success_total counter\n");
        output.push_str(&format!("apt_ostree_operations_success_total {}\n", metrics.success_count));

        output.push_str("# HELP apt_ostree_operations_error_total Total number of failed operations\n");
        output.push_str("# TYPE apt_ostree_operations_error_total counter\n");
        output.push_str(&format!("apt_ostree_operations_error_total {}\n", metrics.error_count));

        // Operation times
        for (operation, times) in &metrics.operation_times {
            if !times.is_empty() {
                let avg_time = times.iter().sum::<f64>() / times.len() as f64;
                output.push_str("# HELP apt_ostree_operation_duration_seconds Average duration of operations\n");
                output.push_str("# TYPE apt_ostree_operation_duration_seconds gauge\n");
                output.push_str(&format!("apt_ostree_operation_duration_seconds{{operation=\"{}\"}} {}\n", operation, avg_time / 1000.0));
            }
        }

        // Health status
        let health_value = match metrics.system_health.status {
            HealthStatus::Healthy => 0,
            HealthStatus::Warning => 1,
            HealthStatus::Critical => 2,
            HealthStatus::Unknown => 3,
        };
        output.push_str("# HELP apt_ostree_system_health System health status\n");
        output.push_str("# TYPE apt_ostree_system_health gauge\n");
        output.push_str(&format!("apt_ostree_system_health {}\n", health_value));

        output
    }

    /// Export metrics in JSON format
    pub async fn export_json_metrics(&self) -> serde_json::Value {
        let metrics = self.metrics.read().await;
        serde_json::json!({
            "operation_count": metrics.operation_count,
            "error_count": metrics.error_count,
            "success_count": metrics.success_count,
            "last_operation": metrics.last_operation,
            "system_health": metrics.system_health,
            "operation_times": metrics.operation_times
        })
    }
}

impl Default for LoggingConfig {
    fn default() -> Self {
        Self {
            level: "info".to_string(),
            format: LogFormat::Text,
            output: LogOutput::Console,
            file_path: None,
            max_file_size: Some(100 * 1024 * 1024), // 100MB
            max_files: Some(7), // 7 days
            enable_metrics: true,
            enable_health_checks: true,
        }
    }
}

impl Metrics {
    fn new() -> Self {
        Self {
            operation_count: 0,
            error_count: 0,
            success_count: 0,
            operation_times: HashMap::new(),
            last_operation: None,
            system_health: SystemHealth::new(),
        }
    }
}

impl SystemHealth {
    fn new() -> Self {
        Self {
            status: HealthStatus::Unknown,
            last_check: Utc::now(),
            checks: HashMap::new(),
        }
    }
}

/// Macro for recording operation performance
#[macro_export]
macro_rules! record_operation {
    ($logging_manager:expr, $operation_name:expr, $result:expr, $start_time:expr) => {
        let duration = std::time::Instant::now().duration_since($start_time);
        let duration_ms = duration.as_secs_f64() * 1000.0;
        let success = $result.is_ok();
        
        if let Some(logging_manager) = $logging_manager {
            logging_manager.record_operation($operation_name, success, duration_ms).await;
        }
    };
}

/// Macro for health check updates
#[macro_export]
macro_rules! update_health {
    ($logging_manager:expr, $check_name:expr, $status:expr, $message:expr) => {
        if let Some(logging_manager) = $logging_manager {
            logging_manager.update_health($check_name, $status, $message, None).await;
        }
    };
}

/// Macro for health check updates with details
#[macro_export]
macro_rules! update_health_with_details {
    ($logging_manager:expr, $check_name:expr, $status:expr, $message:expr, $details:expr) => {
        if let Some(logging_manager) = $logging_manager {
            logging_manager.update_health($check_name, $status, $message, Some($details)).await;
        }
    };
}
