//! Logging structuré avec tracing pour le serveur de chat
//!
//! Ce module fournit un système de logging avancé avec:
//! - Logs structurés avec champs contextuels
//! - Rotation des logs
//! - Filtrage par niveau et module
//! - Export vers différents formats (JSON, Pretty, Compact)
//! - Intégration avec les métriques

use crate::config::{LogFormat, LogRotation, LoggingConfig, ServerConfig};
use crate::error::{ChatError, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Duration;
use tracing::{debug, error, info, trace, warn};
use tracing_appender::{
    non_blocking::{NonBlocking, WorkerGuard},
    rolling::{RollingFileAppender, Rotation},
};
use tracing_subscriber::{
    fmt::{self, format::Writer, time::ChronoUtc},
    layer::SubscriberExt,
    util::SubscriberInitExt,
    EnvFilter, Layer, Registry,
};

/// Configuration du logging structuré
#[derive(Debug)]
pub struct StructuredLogging {
    config: LoggingConfig,
    _guard: Option<WorkerGuard>,
}

impl StructuredLogging {
    /// Initialise le système de logging structuré
    pub fn new(config: LoggingConfig) -> Result<Self> {
        Ok(Self {
            config,
            _guard: None,
        })
    }

    /// Configure et initialise le subscriber tracing
    pub fn setup(&self) -> Result<()> {
        // Filtre d'environnement
        let env_filter = EnvFilter::try_from_default_env()
            .or_else(|_| EnvFilter::try_new(&self.config.level))
            .map_err(|e| {
                ChatError::configuration_error(&format!("Erreur configuration filtre: {e}"))
            })?;

        // Configuration du format
        let format_layer = match self.config.format {
            LogFormat::Json => fmt::layer()
                .json()
                .with_timer(ChronoUtc::rfc_3339())
                .with_target(true)
                .with_file(true)
                .with_line_number(true)
                .boxed(),
            LogFormat::Pretty => fmt::layer()
                .pretty()
                .with_timer(ChronoUtc::rfc_3339())
                .with_target(true)
                .with_file(true)
                .with_line_number(true)
                .boxed(),
            LogFormat::Compact => fmt::layer()
                .compact()
                .with_timer(ChronoUtc::rfc_3339())
                .with_target(true)
                .boxed(),
        };

        // Configuration de la sortie
        let registry = Registry::default().with(env_filter).with(format_layer);
        registry.init();

        info!(
            level = %self.config.level,
            format = ?self.config.format,
            file = ?self.config.file,
            "🔧 Système de logging structuré initialisé"
        );

        Ok(())
    }
}

/// Macros de logging contextuel pour le chat
pub mod chat_logs {
    use super::*;
    use std::collections::HashMap;
    use tracing::{debug, error, info, trace, warn};
    use uuid::Uuid;

    /// Log d'authentification
    pub fn auth_success(user_id: i32, username: &str, ip: &str) {
        info!(
            event = "auth_success",
            user_id = %user_id,
            username = %username,
            ip_address = %ip,
            "🔐 Utilisateur authentifié"
        );
    }

    /// Log d'échec d'authentification
    pub fn auth_failure(username: &str, ip: &str, reason: &str) {
        warn!(
            event = "auth_failure",
            username = %username,
            ip_address = %ip,
            reason = %reason,
            "❌ Échec d'authentification"
        );
    }

    /// Log de connexion WebSocket
    pub fn websocket_connected(connection_id: &str, user_id: i32, username: &str) {
        info!(
            event = "websocket_connected",
            connection_id = %connection_id,
            user_id = %user_id,
            username = %username,
            "🔌 Connexion WebSocket établie"
        );
    }

    /// Log de déconnexion WebSocket
    pub fn websocket_disconnected(connection_id: &str, user_id: i32, username: &str, reason: &str) {
        info!(
            event = "websocket_disconnected",
            connection_id = %connection_id,
            user_id = %user_id,
            username = %username,
            reason = %reason,
            "🔌 Connexion WebSocket fermée"
        );
    }

    /// Log d'envoi de message
    pub fn message_sent(
        message_id: String,
        user_id: i32,
        username: &str,
        room_id: &str,
        message_type: &str,
        content_length: usize,
    ) {
        info!(
            event = "message_sent",
            message_id = %message_id,
            user_id = %user_id,
            username = %username,
            room_id = %room_id,
            message_type = %message_type,
            content_length = %content_length,
            "💬 Message envoyé"
        );
    }

    /// Log de réception de message
    pub fn message_received(
        message_id: i32,
        user_id: i32,
        username: &str,
        room_id: &str,
        message_type: &str,
    ) {
        debug!(
            event = "message_received",
            message_id = %message_id,
            user_id = %user_id,
            username = %username,
            room_id = %room_id,
            message_type = %message_type,
            "📨 Message reçu"
        );
    }

    /// Log de création de salon
    pub fn room_created(room_id: &str, room_name: &str, creator_id: i32, creator_username: &str) {
        info!(
            event = "room_created",
            room_id = %room_id,
            room_name = %room_name,
            creator_id = %creator_id,
            creator_username = %creator_username,
            "🏠 Salon créé"
        );
    }

    /// Log de suppression de salon
    pub fn room_deleted(room_id: &str, room_name: &str, deleter_id: i32, deleter_username: &str) {
        warn!(
            event = "room_deleted",
            room_id = %room_id,
            room_name = %room_name,
            deleter_id = %deleter_id,
            deleter_username = %deleter_username,
            "🗑️ Salon supprimé"
        );
    }

    /// Log d'erreur système
    pub fn system_error(error_type: &str, context: &str, error: &str) {
        error!(
            event = "system_error",
            error_type = %error_type,
            context = %context,
            error = %error,
            "💥 Erreur système"
        );
    }

    /// Log d'erreur de validation
    pub fn validation_error(field: &str, value: &str, reason: &str) {
        warn!(
            event = "validation_error",
            field = %field,
            value = %value,
            reason = %reason,
            "⚠️ Erreur de validation"
        );
    }

    /// Log de rate limiting
    pub fn rate_limit_triggered(
        user_id: i32,
        username: &str,
        limit_type: &str,
        current_count: u32,
    ) {
        warn!(
            event = "rate_limit_triggered",
            user_id = %user_id,
            username = %username,
            limit_type = %limit_type,
            current_count = %current_count,
            "🚫 Rate limit déclenché"
        );
    }

    /// Log de métriques
    pub fn metrics_updated(metric_name: &str, value: f64, labels: &HashMap<String, String>) {
        trace!(
            event = "metrics_updated",
            metric_name = %metric_name,
            value = %value,
            labels = ?labels,
            "📊 Métrique mise à jour"
        );
    }

    /// Log de performance
    pub fn performance_measurement(
        operation: &str,
        duration_ms: f64,
        success: bool,
        additional_data: Option<&HashMap<String, String>>,
    ) {
        let level = if success { "info" } else { "warn" };
        let message = if success {
            "⚡ Opération terminée"
        } else {
            "🐌 Opération lente"
        };

        match level {
            "info" => {
                info!(
                    event = "performance_measurement",
                    operation = %operation,
                    duration_ms = %duration_ms,
                    success = %success,
                    additional_data = ?additional_data,
                    "{}", message
                );
            }
            _ => {
                warn!(
                    event = "performance_measurement",
                    operation = %operation,
                    duration_ms = %duration_ms,
                    success = %success,
                    additional_data = ?additional_data,
                    "{}", message
                );
            }
        }
    }

    /// Log de démarrage du serveur
    pub fn server_started(bind_addr: &str, environment: &str, version: &str) {
        info!(
            event = "server_started",
            bind_addr = %bind_addr,
            environment = %environment,
            version = %version,
            "🚀 Serveur de chat démarré"
        );
    }

    /// Log d'arrêt du serveur
    pub fn server_stopped(reason: &str, uptime_seconds: u64) {
        info!(
            event = "server_stopped",
            reason = %reason,
            uptime_seconds = %uptime_seconds,
            "🛑 Serveur de chat arrêté"
        );
    }

    /// Log de configuration
    pub fn config_loaded(config_source: &str, config_path: Option<&str>) {
        info!(
            event = "config_loaded",
            config_source = %config_source,
            config_path = ?config_path,
            "⚙️ Configuration chargée"
        );
    }

    /// Log de base de données
    pub fn database_operation(operation: &str, table: &str, duration_ms: f64, success: bool) {
        let level = if success { "debug" } else { "error" };
        let message = if success {
            "🗄️ Opération DB réussie"
        } else {
            "💥 Erreur DB"
        };

        match level {
            "debug" => {
                debug!(
                    event = "database_operation",
                    operation = %operation,
                    table = %table,
                    duration_ms = %duration_ms,
                    success = %success,
                    "{}", message
                );
            }
            _ => {
                error!(
                    event = "database_operation",
                    operation = %operation,
                    table = %table,
                    duration_ms = %duration_ms,
                    success = %success,
                    "{}", message
                );
            }
        }
    }

    /// Log de cache
    pub fn cache_operation(operation: &str, key: &str, hit: bool, duration_ms: f64) {
        debug!(
            event = "cache_operation",
            operation = %operation,
            key = %key,
            hit = %hit,
            duration_ms = %duration_ms,
            "💾 Opération de cache"
        );
    }

    /// Log de sécurité
    pub fn security_event(event_type: &str, user_id: Option<i32>, ip: &str, details: &str) {
        warn!(
            event = "security_event",
            event_type = %event_type,
            user_id = ?user_id,
            ip_address = %ip,
            details = %details,
            "🔒 Événement de sécurité"
        );
    }
}

/// Wrapper pour les logs avec contexte utilisateur
pub struct UserContextLogger {
    user_id: i32,
    username: String,
    ip_address: String,
}

impl UserContextLogger {
    pub fn new(user_id: i32, username: String, ip_address: String) -> Self {
        Self {
            user_id,
            username,
            ip_address,
        }
    }

    pub fn info(&self, message: &str, fields: Option<&HashMap<String, String>>) {
        info!(
            user_id = %self.user_id,
            username = %self.username,
            ip_address = %self.ip_address,
            additional_fields = ?fields,
            "{}", message
        );
    }

    pub fn warn(&self, message: &str, fields: Option<&HashMap<String, String>>) {
        warn!(
            user_id = %self.user_id,
            username = %self.username,
            ip_address = %self.ip_address,
            additional_fields = ?fields,
            "{}", message
        );
    }

    pub fn error(&self, message: &str, fields: Option<&HashMap<String, String>>) {
        error!(
            user_id = %self.user_id,
            username = %self.username,
            ip_address = %self.ip_address,
            additional_fields = ?fields,
            "{}", message
        );
    }
}

/// Initialise le système de logging depuis la configuration du serveur
pub fn init_logging_from_config(config: &ServerConfig) -> Result<StructuredLogging> {
    let logging_config = config.logging.clone();
    let structured_logging = StructuredLogging::new(logging_config)?;
    structured_logging.setup()?;

    chat_logs::config_loaded("server_config", None);
    chat_logs::server_started(
        &config.server.bind_addr.to_string(),
        &config.server.environment.to_string(),
        env!("CARGO_PKG_VERSION"),
    );

    Ok(structured_logging)
}

/// Configuration par défaut pour les tests
pub fn init_test_logging() -> Result<()> {
    let config = LoggingConfig {
        level: "debug".to_string(),
        format: LogFormat::Compact,
        file: None,
        rotation: None,
        filters: vec!["chat_server=debug".to_string()],
    };

    let structured_logging = StructuredLogging::new(config)?;
    structured_logging.setup()?;

    Ok(())
}

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

    #[test]
    fn test_user_context_logger() {
        let logger = UserContextLogger::new(1, "testuser".to_string(), "127.0.0.1".to_string());

        // Test que le logger peut être créé sans erreur
        assert_eq!(logger.user_id, 1);
        assert_eq!(logger.username, "testuser");
        assert_eq!(logger.ip_address, "127.0.0.1");
    }

    #[test]
    fn test_chat_logs_macros() {
        // Test que les macros de logging peuvent être appelées
        // (elles ne feront rien en mode test sans subscriber)
        chat_logs::auth_success(1, "testuser", "127.0.0.1");
        chat_logs::message_sent(1, 1, "testuser", "room1", "text", 10);
        chat_logs::system_error("test", "context", "error");
    }

    #[test]
    fn test_structured_logging_creation() {
        let config = LoggingConfig {
            level: "info".to_string(),
            format: LogFormat::Pretty,
            file: None,
            rotation: None,
            filters: vec![],
        };

        let result = StructuredLogging::new(config);
        assert!(result.is_ok());
    }
}
