//! Logging structuré avec tracing pour le serveur de streaming
//!
//! 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;
use crate::error::{AppError, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Duration;
// Note: Use tracing::info! macro directly instead of importing
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
#[derive(Debug, Clone)]
pub struct LoggingConfig {
    pub level: String,
    pub format: LogFormat,
    pub file: Option<PathBuf>,
    pub rotation: Option<LogRotation>,
    pub filters: Vec<String>,
}

/// Configuration de la rotation des logs
#[derive(Debug, Clone)]
pub struct LogRotation {
    pub max_size: u64,
    pub max_files: usize,
}

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

impl StructuredLogging {
    /// Initialise le système de logging structuré
    pub fn new(config: LoggingConfig) -> Result<Self> {
        let (appender, guard) = if let Some(file_path) = &config.file {
            // Configuration de la rotation des logs
            let rotation = match &config.rotation {
                Some(LogRotation { max_size, .. }) => {
                    if *max_size > 100 * 1024 * 1024 {
                        // > 100MB -> rotation quotidienne
                        Rotation::DAILY
                    } else {
                        // <= 100MB -> rotation horaire
                        Rotation::HOURLY
                    }
                }
                None => Rotation::DAILY,
            };

            let file_appender =
                RollingFileAppender::new(rotation, file_path.parent().unwrap(), "stream-server");
            let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
            (Some(non_blocking), Some(guard))
        } else {
            (None, None)
        };

        Ok(Self {
            config,
            _guard: guard,
            appender,
        })
    }

    /// 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| AppError::ConfigError {
                message: 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);

        if let Some(appender) = &self.appender {
            let file_layer = fmt::layer()
                .json()
                .with_writer(appender.clone())
                .with_timer(ChronoUtc::rfc_3339())
                .with_target(true)
                .with_file(true)
                .with_line_number(true)
                .boxed();

            registry.with(file_layer).init();
        } else {
            registry.init();
        }

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

        Ok(())
    }
}

/// Log d'une requête de streaming
pub fn log_stream_request(
    track_id: &str,
    user_id: &str,
    bitrate: u32,
    metadata: HashMap<String, String>,
) {
    tracing::info!(
        track_id = track_id,
        user_id = user_id,
        bitrate = bitrate,
        metadata = ?metadata,
        "Stream request initiated"
    );
}

/// Trace d'un chunk audio traité
pub fn trace_audio_chunk(chunk_id: usize, size: usize) {
    tracing::trace!(chunk_id = chunk_id, size = size, "Audio chunk processed");
}

/// Log d'erreur avec contexte
pub fn log_error(error: &str, context: HashMap<String, String>) {
    tracing::error!(
        error = error,
        context = ?context,
        "Error occurred in stream server"
    );
}

/// Macros de logging contextuel pour le streaming
pub mod stream_logs {
    use super::*;
    use std::collections::HashMap;
    // Note: Use tracing::info! macro directly instead of importing
    use uuid::Uuid;

    /// Log de connexion de streaming
    pub fn stream_connected(stream_id: &str, client_ip: &str, user_agent: &str, codec: &str) {
        tracing::info!(
            event = "stream_connected",
            stream_id = %stream_id,
            client_ip = %client_ip,
            user_agent = %user_agent,
            codec = %codec,
            "🎵 Connexion de streaming établie"
        );
    }

    /// Log de déconnexion de streaming
    pub fn stream_disconnected(
        stream_id: &str,
        client_ip: &str,
        duration_seconds: u64,
        reason: &str,
    ) {
        tracing::info!(
            event = "stream_disconnected",
            stream_id = %stream_id,
            client_ip = %client_ip,
            duration_seconds = %duration_seconds,
            reason = %reason,
            "🎵 Connexion de streaming fermée"
        );
    }

    /// Log de début de lecture
    pub fn playback_started(stream_id: &str, track_id: &str, codec: &str, bitrate: u32) {
        tracing::info!(
            event = "playback_started",
            stream_id = %stream_id,
            track_id = %track_id,
            codec = %codec,
            bitrate = %bitrate,
            "▶️ Lecture démarrée"
        );
    }

    /// Log de pause de lecture
    pub fn playback_paused(stream_id: &str, track_id: &str, position_seconds: f64) {
        tracing::info!(
            event = "playback_paused",
            stream_id = %stream_id,
            track_id = %track_id,
            position_seconds = %position_seconds,
            "⏸️ Lecture en pause"
        );
    }

    /// Log de reprise de lecture
    pub fn playback_resumed(stream_id: &str, track_id: &str, position_seconds: f64) {
        tracing::info!(
            event = "playback_resumed",
            stream_id = %stream_id,
            track_id = %track_id,
            position_seconds = %position_seconds,
            "▶️ Lecture reprise"
        );
    }

    /// Log de fin de lecture
    pub fn playback_finished(stream_id: &str, track_id: &str, duration_seconds: f64) {
        tracing::info!(
            event = "playback_finished",
            stream_id = %stream_id,
            track_id = %track_id,
            duration_seconds = %duration_seconds,
            "🏁 Lecture terminée"
        );
    }

    /// Log de changement de qualité
    pub fn quality_changed(stream_id: &str, old_quality: &str, new_quality: &str, reason: &str) {
        tracing::info!(
            event = "quality_changed",
            stream_id = %stream_id,
            old_quality = %old_quality,
            new_quality = %new_quality,
            reason = %reason,
            "📊 Qualité changée"
        );
    }

    /// Log d'encodage audio
    pub fn audio_encoded(
        stream_id: &str,
        codec: &str,
        input_samples: usize,
        output_bytes: usize,
        encoding_time_ms: u64,
    ) {
        tracing::debug!(
            event = "audio_encoded",
            stream_id = %stream_id,
            codec = %codec,
            input_samples = %input_samples,
            output_bytes = %output_bytes,
            encoding_time_ms = %encoding_time_ms,
            "🎼 Audio encodé"
        );
    }

    /// Log de décodage audio
    pub fn audio_decoded(
        stream_id: &str,
        codec: &str,
        input_bytes: usize,
        output_samples: usize,
        decoding_time_ms: u64,
    ) {
        tracing::debug!(
            event = "audio_decoded",
            stream_id = %stream_id,
            codec = %codec,
            input_bytes = %input_bytes,
            output_samples = %output_samples,
            decoding_time_ms = %decoding_time_ms,
            "🎼 Audio décodé"
        );
    }

    /// Log d'upload de fichier
    pub fn file_uploaded(
        file_id: &str,
        filename: &str,
        file_size: u64,
        codec: &str,
        duration_seconds: f64,
    ) {
        tracing::info!(
            event = "file_uploaded",
            file_id = %file_id,
            filename = %filename,
            file_size = %file_size,
            codec = %codec,
            duration_seconds = %duration_seconds,
            "📁 Fichier uploadé"
        );
    }

    /// Log de suppression de fichier
    pub fn file_deleted(file_id: &str, filename: &str, reason: &str) {
        tracing::warn!(
            event = "file_deleted",
            file_id = %file_id,
            filename = %filename,
            reason = %reason,
            "🗑️ Fichier supprimé"
        );
    }

    /// Log de transcodage
    pub fn transcoding_started(
        file_id: &str,
        input_codec: &str,
        output_codec: &str,
        quality: &str,
    ) {
        tracing::info!(
            event = "transcoding_started",
            file_id = %file_id,
            input_codec = %input_codec,
            output_codec = %output_codec,
            quality = %quality,
            "🔄 Transcodage démarré"
        );
    }

    /// Log de fin de transcodage
    pub fn transcoding_finished(
        file_id: &str,
        input_codec: &str,
        output_codec: &str,
        duration_seconds: f64,
        success: bool,
    ) {
        let level = if success { "info" } else { "error" };
        let message = if success {
            "✅ Transcodage terminé"
        } else {
            "❌ Transcodage échoué"
        };

        match level {
            "info" => {
                tracing::info!(
                    event = "transcoding_finished",
                    file_id = %file_id,
                    input_codec = %input_codec,
                    output_codec = %output_codec,
                    duration_seconds = %duration_seconds,
                    success = %success,
                    "{}", message
                );
            }
            _ => {
                tracing::error!(
                    event = "transcoding_finished",
                    file_id = %file_id,
                    input_codec = %input_codec,
                    output_codec = %output_codec,
                    duration_seconds = %duration_seconds,
                    success = %success,
                    "{}", message
                );
            }
        }
    }

    /// Log d'erreur de streaming
    pub fn stream_error(stream_id: &str, error_type: &str, error_message: &str, context: &str) {
        tracing::error!(
            event = "stream_error",
            stream_id = %stream_id,
            error_type = %error_type,
            error_message = %error_message,
            context = %context,
            "💥 Erreur de streaming"
        );
    }

    /// Log d'erreur de codec
    pub fn codec_error(codec: &str, operation: &str, error_message: &str) {
        tracing::error!(
            event = "codec_error",
            codec = %codec,
            operation = %operation,
            error_message = %error_message,
            "🎼 Erreur de codec"
        );
    }

    /// Log d'erreur de fichier
    pub fn file_error(file_id: &str, operation: &str, error_message: &str) {
        tracing::error!(
            event = "file_error",
            file_id = %file_id,
            operation = %operation,
            error_message = %error_message,
            "📁 Erreur de fichier"
        );
    }

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

        match level {
            "debug" => {
                tracing::debug!(
                    event = "performance_measurement",
                    operation = %operation,
                    duration_ms = %duration_ms,
                    success = %success,
                    additional_data = ?additional_data,
                    "{}", message
                );
            }
            _ => {
                tracing::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) {
        tracing::info!(
            event = "server_started",
            bind_addr = %bind_addr,
            environment = %environment,
            version = %version,
            "🚀 Serveur de streaming démarré"
        );
    }

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

    /// Log de configuration
    pub fn config_loaded(config_source: &str, config_path: Option<&str>) {
        tracing::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" => {
                tracing::debug!(
                    event = "database_operation",
                    operation = %operation,
                    table = %table,
                    duration_ms = %duration_ms,
                    success = %success,
                    "{}", message
                );
            }
            _ => {
                tracing::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) {
        tracing::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, client_ip: &str, details: &str) {
        tracing::warn!(
            event = "security_event",
            event_type = %event_type,
            client_ip = %client_ip,
            details = %details,
            "🔒 Événement de sécurité"
        );
    }

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

/// Wrapper pour les logs avec contexte de streaming
pub struct StreamContextLogger {
    stream_id: String,
    client_ip: String,
    user_agent: Option<String>,
}

impl StreamContextLogger {
    pub fn new(stream_id: String, client_ip: String, user_agent: Option<String>) -> Self {
        Self {
            stream_id,
            client_ip,
            user_agent,
        }
    }

    pub fn info(&self, message: &str, fields: Option<&HashMap<String, String>>) {
        tracing::info!(
            stream_id = %self.stream_id,
            client_ip = %self.client_ip,
            user_agent = ?self.user_agent,
            additional_fields = ?fields,
            "{}", message
        );
    }

    pub fn warn(&self, message: &str, fields: Option<&HashMap<String, String>>) {
        tracing::warn!(
            stream_id = %self.stream_id,
            client_ip = %self.client_ip,
            user_agent = ?self.user_agent,
            additional_fields = ?fields,
            "{}", message
        );
    }

    pub fn error(&self, message: &str, fields: Option<&HashMap<String, String>>) {
        tracing::error!(
            stream_id = %self.stream_id,
            client_ip = %self.client_ip,
            user_agent = ?self.user_agent,
            additional_fields = ?fields,
            "{}", message
        );
    }
}

/// Initialise le système de logging depuis la configuration du serveur
pub fn init_logging_from_config(config: &crate::config::Config) -> Result<StructuredLogging> {
    let logging_config = LoggingConfig {
        level: config.monitoring.log_level.clone(),
        format: config.monitoring.log_format.clone(),
        file: None, // TODO: Ajouter configuration de fichier si nécessaire
        rotation: None,
        filters: vec![],
    };
    let structured_logging = StructuredLogging::new(logging_config)?;
    structured_logging.setup()?;

    stream_logs::config_loaded("server_config", None);
    stream_logs::server_started(
        &format!("0.0.0.0:{}", config.port),
        &format!("{:?}", config.environment),
        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!["stream_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_stream_context_logger() {
        let logger = StreamContextLogger::new(
            "test_stream".to_string(),
            "127.0.0.1".to_string(),
            Some("TestAgent".to_string()),
        );

        // Test que le logger peut être créé sans erreur
        assert_eq!(logger.stream_id, "test_stream");
        assert_eq!(logger.client_ip, "127.0.0.1");
        assert_eq!(logger.user_agent, Some("TestAgent".to_string()));
    }

    #[test]
    fn test_stream_logs_macros() {
        // Test que les macros de logging peuvent être appelées
        // (elles ne feront rien en mode test sans subscriber)
        stream_logs::stream_connected("test", "127.0.0.1", "TestAgent", "mp3");
        stream_logs::playback_started("test", "track1", "mp3", 128000);
        stream_logs::stream_error("test", "codec", "Test error", "encoding");
    }

    #[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());
    }

    #[test]
    fn test_log_stream_request() {
        let mut metadata = HashMap::new();
        metadata.insert("ip".to_string(), "192.168.1.1".to_string());

        // Should not panic
        log_stream_request("track-123", "user-456", 320, metadata);
    }

    #[test]
    fn test_trace_audio_chunk() {
        // Should not panic
        trace_audio_chunk(1, 1024);
    }

    #[test]
    fn test_log_error() {
        let mut context = HashMap::new();
        context.insert("operation".to_string(), "encoding".to_string());

        // Should not panic
        log_error("Test error message", context);
    }
}
