//! Métriques Prometheus avancées pour le serveur de streaming
//!
//! Ce module fournit une intégration complète avec Prometheus pour:
//! - Métriques de streaming audio (latence, débit, qualité)
//! - Métriques de codecs (encodage, décodage, transcodage)
//! - Métriques de performance (CPU, mémoire, réseau)
//! - Métriques d'erreurs et de sécurité
//! - Métriques business (utilisateurs, revenus, contenu)

// use crate::config::ServerConfig;
use crate::error::{AppError, Result};
use axum::{extract::State, http::StatusCode, response::Response, routing::get, Router};
// use metrics::{
//     counter, gauge, histogram, register_counter, register_gauge, register_histogram, Counter,
//     Gauge, Histogram,
// };
// use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
// Note: Use tracing::info! macro directly instead of importing

/// Gestionnaire des métriques Prometheus pour le streaming
pub struct StreamPrometheusMetrics {
    /// Handle Prometheus pour l'export
    handle: PrometheusHandle,

    /// Métriques de streaming audio
    pub active_streams: Gauge,
    pub streams_total: Counter,
    pub streams_ended: Counter,
    pub stream_duration_seconds: Histogram,
    pub stream_bitrate_bps: Histogram,
    pub stream_quality_changes: Counter,

    /// Métriques de codecs
    pub codec_encoding_duration_seconds: Histogram,
    pub codec_decoding_duration_seconds: Histogram,
    pub codec_transcoding_duration_seconds: Histogram,
    pub codec_encoding_errors: Counter,
    pub codec_decoding_errors: Counter,
    pub codec_transcoding_errors: Counter,

    /// Métriques de fichiers
    pub files_uploaded_total: Counter,
    pub files_deleted_total: Counter,
    pub files_served_total: Counter,
    pub file_upload_duration_seconds: Histogram,
    pub file_size_bytes: Histogram,

    /// Métriques de performance
    pub audio_processing_duration_seconds: Histogram,
    pub waveform_generation_duration_seconds: Histogram,
    pub metadata_extraction_duration_seconds: Histogram,
    pub cache_hit_ratio: Gauge,
    pub cache_operations_total: Counter,

    /// Métriques de réseau
    pub bytes_streamed_total: Counter,
    pub bytes_uploaded_total: Counter,
    pub network_bandwidth_bps: Gauge,
    pub connection_duration_seconds: Histogram,

    /// Métriques d'erreurs
    pub stream_errors_total: Counter,
    pub file_errors_total: Counter,
    pub codec_errors_total: Counter,
    pub network_errors_total: Counter,
    pub authentication_errors_total: Counter,

    /// Métriques de sécurité
    pub security_events_total: Counter,
    pub rate_limits_triggered_total: Counter,
    pub invalid_requests_total: Counter,

    /// Métriques système
    pub memory_usage_bytes: Gauge,
    pub cpu_usage_percent: Gauge,
    pub disk_usage_bytes: Gauge,
    pub uptime_seconds: Gauge,

    /// Métriques business
    pub active_users: Gauge,
    pub premium_users: Gauge,
    pub total_playtime_seconds: Counter,
    pub unique_tracks_played: Counter,
    pub revenue_total: Counter,
}

impl StreamPrometheusMetrics {
    /// Crée un nouveau gestionnaire de métriques Prometheus
    pub fn new(config: &ServerConfig) -> Result<Self> {
        // Configuration du builder Prometheus
        let builder = PrometheusBuilder::new()
            .with_http_listener(([0, 0, 0, 0], config.monitoring.metrics_port))
            .map_err(|e| AppError::Configuration {
                message: format!("Erreur configuration Prometheus: {e}"),
            })?;

        let handle = builder.install().map_err(|e| AppError::Configuration {
            message: format!("Erreur installation Prometheus: {e}"),
        })?;

        tracing::info!(
            metrics_port = %config.monitoring.metrics_port,
            "📊 Métriques Prometheus configurées"
        );

        // Enregistrement des métriques
        let active_streams = register_gauge!("active_streams", "Nombre de streams actifs");
        let streams_total = register_counter!("streams_total", "Total des streams créés");
        let streams_ended = register_counter!("streams_ended", "Total des streams terminés");
        let stream_duration_seconds = register_histogram!(
            "stream_duration_seconds",
            "Durée des streams en secondes",
            &["codec", "quality"]
        );
        let stream_bitrate_bps = register_histogram!(
            "stream_bitrate_bps",
            "Bitrate des streams en bps",
            &["codec", "quality"]
        );
        let stream_quality_changes = register_counter!(
            "stream_quality_changes_total",
            "Total des changements de qualité"
        );

        let codec_encoding_duration_seconds = register_histogram!(
            "codec_encoding_duration_seconds",
            "Durée d'encodage des codecs",
            &["codec", "quality"]
        );
        let codec_decoding_duration_seconds = register_histogram!(
            "codec_decoding_duration_seconds",
            "Durée de décodage des codecs",
            &["codec"]
        );
        let codec_transcoding_duration_seconds = register_histogram!(
            "codec_transcoding_duration_seconds",
            "Durée de transcodage",
            &["input_codec", "output_codec"]
        );
        let codec_encoding_errors = register_counter!(
            "codec_encoding_errors_total",
            "Erreurs d'encodage",
            &["codec", "error_type"]
        );
        let codec_decoding_errors = register_counter!(
            "codec_decoding_errors_total",
            "Erreurs de décodage",
            &["codec", "error_type"]
        );
        let codec_transcoding_errors = register_counter!(
            "codec_transcoding_errors_total",
            "Erreurs de transcodage",
            &["input_codec", "output_codec", "error_type"]
        );

        let files_uploaded_total = register_counter!(
            "files_uploaded_total",
            "Total des fichiers uploadés",
            &["codec", "quality"]
        );
        let files_deleted_total = register_counter!(
            "files_deleted_total",
            "Total des fichiers supprimés",
            &["reason"]
        );
        let files_served_total = register_counter!(
            "files_served_total",
            "Total des fichiers servis",
            &["codec", "quality"]
        );
        let file_upload_duration_seconds = register_histogram!(
            "file_upload_duration_seconds",
            "Durée d'upload des fichiers",
            &["codec", "file_size_range"]
        );
        let file_size_bytes = register_histogram!(
            "file_size_bytes",
            "Taille des fichiers en bytes",
            &["codec"]
        );

        let audio_processing_duration_seconds = register_histogram!(
            "audio_processing_duration_seconds",
            "Durée de traitement audio",
            &["operation"]
        );
        let waveform_generation_duration_seconds = register_histogram!(
            "waveform_generation_duration_seconds",
            "Durée de génération de waveform"
        );
        let metadata_extraction_duration_seconds = register_histogram!(
            "metadata_extraction_duration_seconds",
            "Durée d'extraction de métadonnées",
            &["codec"]
        );
        let cache_hit_ratio = register_gauge!("cache_hit_ratio", "Ratio de hits du cache");
        let cache_operations_total = register_counter!(
            "cache_operations_total",
            "Total des opérations de cache",
            &["operation", "result"]
        );

        let bytes_streamed_total = register_counter!(
            "bytes_streamed_total",
            "Total des bytes streamés",
            &["codec", "quality"]
        );
        let bytes_uploaded_total = register_counter!(
            "bytes_uploaded_total",
            "Total des bytes uploadés",
            &["codec"]
        );
        let network_bandwidth_bps =
            register_gauge!("network_bandwidth_bps", "Bande passante réseau en bps");
        let connection_duration_seconds = register_histogram!(
            "connection_duration_seconds",
            "Durée des connexions",
            &["client_type"]
        );

        let stream_errors_total = register_counter!(
            "stream_errors_total",
            "Erreurs de streaming",
            &["error_type", "codec"]
        );
        let file_errors_total = register_counter!(
            "file_errors_total",
            "Erreurs de fichiers",
            &["error_type", "operation"]
        );
        let codec_errors_total = register_counter!(
            "codec_errors_total",
            "Erreurs de codecs",
            &["codec", "error_type"]
        );
        let network_errors_total =
            register_counter!("network_errors_total", "Erreurs réseau", &["error_type"]);
        let authentication_errors_total =
            register_counter!("authentication_errors_total", "Erreurs d'authentification");

        let security_events_total = register_counter!(
            "security_events_total",
            "Événements de sécurité",
            &["event_type"]
        );
        let rate_limits_triggered_total = register_counter!(
            "rate_limits_triggered_total",
            "Rate limits déclenchés",
            &["limit_type"]
        );
        let invalid_requests_total = register_counter!(
            "invalid_requests_total",
            "Requêtes invalides",
            &["request_type"]
        );

        let memory_usage_bytes =
            register_gauge!("memory_usage_bytes", "Utilisation mémoire en bytes");
        let cpu_usage_percent =
            register_gauge!("cpu_usage_percent", "Utilisation CPU en pourcentage");
        let disk_usage_bytes = register_gauge!("disk_usage_bytes", "Utilisation disque en bytes");
        let uptime_seconds =
            register_gauge!("uptime_seconds", "Temps de fonctionnement en secondes");

        let active_users = register_gauge!("active_users", "Utilisateurs actifs");
        let premium_users = register_gauge!("premium_users", "Utilisateurs premium");
        let total_playtime_seconds =
            register_counter!("total_playtime_seconds", "Temps total d'écoute en secondes");
        let unique_tracks_played =
            register_counter!("unique_tracks_played_total", "Pistes uniques jouées");
        let revenue_total = register_counter!("revenue_total", "Revenus totaux", &["currency"]);

        Ok(Self {
            handle,
            active_streams,
            streams_total,
            streams_ended,
            stream_duration_seconds,
            stream_bitrate_bps,
            stream_quality_changes,
            codec_encoding_duration_seconds,
            codec_decoding_duration_seconds,
            codec_transcoding_duration_seconds,
            codec_encoding_errors,
            codec_decoding_errors,
            codec_transcoding_errors,
            files_uploaded_total,
            files_deleted_total,
            files_served_total,
            file_upload_duration_seconds,
            file_size_bytes,
            audio_processing_duration_seconds,
            waveform_generation_duration_seconds,
            metadata_extraction_duration_seconds,
            cache_hit_ratio,
            cache_operations_total,
            bytes_streamed_total,
            bytes_uploaded_total,
            network_bandwidth_bps,
            connection_duration_seconds,
            stream_errors_total,
            file_errors_total,
            codec_errors_total,
            network_errors_total,
            authentication_errors_total,
            security_events_total,
            rate_limits_triggered_total,
            invalid_requests_total,
            memory_usage_bytes,
            cpu_usage_percent,
            disk_usage_bytes,
            uptime_seconds,
            active_users,
            premium_users,
            total_playtime_seconds,
            unique_tracks_played,
            revenue_total,
        })
    }

    /// Met à jour le nombre de streams actifs
    pub fn update_active_streams(&self, count: u64) {
        self.active_streams.set(count as f64);
    }

    /// Enregistre un nouveau stream
    pub fn record_stream_started(&self, codec: &str, quality: &str) {
        counter!(
            "streams_total",
            1,
            &[("codec", codec), ("quality", quality)]
        );
    }

    /// Enregistre la fin d'un stream
    pub fn record_stream_ended(&self, codec: &str, quality: &str, duration_seconds: f64) {
        counter!(
            "streams_ended",
            1,
            &[("codec", codec), ("quality", quality)]
        );
        self.stream_duration_seconds
            .record(duration_seconds, &[codec, quality]);
    }

    /// Enregistre le bitrate d'un stream
    pub fn record_stream_bitrate(&self, codec: &str, quality: &str, bitrate_bps: u32) {
        self.stream_bitrate_bps
            .record(bitrate_bps as f64, &[codec, quality]);
    }

    /// Enregistre un changement de qualité
    pub fn record_quality_change(&self) {
        self.stream_quality_changes.increment(1);
    }

    /// Enregistre la durée d'encodage
    pub fn record_encoding_duration(&self, codec: &str, quality: &str, duration: Duration) {
        self.codec_encoding_duration_seconds
            .record(duration.as_secs_f64(), &[codec, quality]);
    }

    /// Enregistre la durée de décodage
    pub fn record_decoding_duration(&self, codec: &str, duration: Duration) {
        self.codec_decoding_duration_seconds
            .record(duration.as_secs_f64(), &[codec]);
    }

    /// Enregistre la durée de transcodage
    pub fn record_transcoding_duration(
        &self,
        input_codec: &str,
        output_codec: &str,
        duration: Duration,
    ) {
        self.codec_transcoding_duration_seconds
            .record(duration.as_secs_f64(), &[input_codec, output_codec]);
    }

    /// Enregistre une erreur d'encodage
    pub fn record_encoding_error(&self, codec: &str, error_type: &str) {
        counter!(
            "codec_encoding_errors_total",
            1,
            &[("codec", codec), ("error_type", error_type)]
        );
    }

    /// Enregistre une erreur de décodage
    pub fn record_decoding_error(&self, codec: &str, error_type: &str) {
        counter!(
            "codec_decoding_errors_total",
            1,
            &[("codec", codec), ("error_type", error_type)]
        );
    }

    /// Enregistre une erreur de transcodage
    pub fn record_transcoding_error(
        &self,
        input_codec: &str,
        output_codec: &str,
        error_type: &str,
    ) {
        counter!(
            "codec_transcoding_errors_total",
            1,
            &[
                ("input_codec", input_codec),
                ("output_codec", output_codec),
                ("error_type", error_type)
            ]
        );
    }

    /// Enregistre l'upload d'un fichier
    pub fn record_file_uploaded(
        &self,
        codec: &str,
        quality: &str,
        file_size_bytes: u64,
        duration: Duration,
    ) {
        counter!(
            "files_uploaded_total",
            1,
            &[("codec", codec), ("quality", quality)]
        );
        self.file_upload_duration_seconds.record(
            duration.as_secs_f64(),
            &[codec, &self.get_file_size_range(file_size_bytes)],
        );
        self.file_size_bytes
            .record(file_size_bytes as f64, &[codec]);
    }

    /// Enregistre la suppression d'un fichier
    pub fn record_file_deleted(&self, reason: &str) {
        counter!("files_deleted_total", 1, &[("reason", reason)]);
    }

    /// Enregistre le service d'un fichier
    pub fn record_file_served(&self, codec: &str, quality: &str) {
        counter!(
            "files_served_total",
            1,
            &[("codec", codec), ("quality", quality)]
        );
    }

    /// Enregistre la durée de traitement audio
    pub fn record_audio_processing_duration(&self, operation: &str, duration: Duration) {
        self.audio_processing_duration_seconds
            .record(duration.as_secs_f64(), &[operation]);
    }

    /// Enregistre la durée de génération de waveform
    pub fn record_waveform_generation_duration(&self, duration: Duration) {
        self.waveform_generation_duration_seconds
            .record(duration.as_secs_f64());
    }

    /// Enregistre la durée d'extraction de métadonnées
    pub fn record_metadata_extraction_duration(&self, codec: &str, duration: Duration) {
        self.metadata_extraction_duration_seconds
            .record(duration.as_secs_f64(), &[codec]);
    }

    /// Met à jour le ratio de hits du cache
    pub fn update_cache_hit_ratio(&self, ratio: f64) {
        self.cache_hit_ratio.set(ratio);
    }

    /// Enregistre une opération de cache
    pub fn record_cache_operation(&self, operation: &str, result: &str) {
        counter!(
            "cache_operations_total",
            1,
            &[("operation", operation), ("result", result)]
        );
    }

    /// Enregistre des bytes streamés
    pub fn record_bytes_streamed(&self, codec: &str, quality: &str, bytes: u64) {
        counter!(
            "bytes_streamed_total",
            bytes,
            &[("codec", codec), ("quality", quality)]
        );
    }

    /// Enregistre des bytes uploadés
    pub fn record_bytes_uploaded(&self, codec: &str, bytes: u64) {
        counter!("bytes_uploaded_total", bytes, &[("codec", codec)]);
    }

    /// Met à jour la bande passante réseau
    pub fn update_network_bandwidth(&self, bandwidth_bps: u64) {
        self.network_bandwidth_bps.set(bandwidth_bps as f64);
    }

    /// Enregistre la durée d'une connexion
    pub fn record_connection_duration(&self, client_type: &str, duration: Duration) {
        self.connection_duration_seconds
            .record(duration.as_secs_f64(), &[client_type]);
    }

    /// Enregistre une erreur de streaming
    pub fn record_stream_error(&self, error_type: &str, codec: &str) {
        counter!(
            "stream_errors_total",
            1,
            &[("error_type", error_type), ("codec", codec)]
        );
    }

    /// Enregistre une erreur de fichier
    pub fn record_file_error(&self, error_type: &str, operation: &str) {
        counter!(
            "file_errors_total",
            1,
            &[("error_type", error_type), ("operation", operation)]
        );
    }

    /// Enregistre une erreur de codec
    pub fn record_codec_error(&self, codec: &str, error_type: &str) {
        counter!(
            "codec_errors_total",
            1,
            &[("codec", codec), ("error_type", error_type)]
        );
    }

    /// Enregistre une erreur réseau
    pub fn record_network_error(&self, error_type: &str) {
        counter!("network_errors_total", 1, &[("error_type", error_type)]);
    }

    /// Enregistre une erreur d'authentification
    pub fn record_authentication_error(&self) {
        self.authentication_errors_total.increment(1);
    }

    /// Enregistre un événement de sécurité
    pub fn record_security_event(&self, event_type: &str) {
        counter!("security_events_total", 1, &[("event_type", event_type)]);
    }

    /// Enregistre un rate limit déclenché
    pub fn record_rate_limit_triggered(&self, limit_type: &str) {
        counter!(
            "rate_limits_triggered_total",
            1,
            &[("limit_type", limit_type)]
        );
    }

    /// Enregistre une requête invalide
    pub fn record_invalid_request(&self, request_type: &str) {
        counter!(
            "invalid_requests_total",
            1,
            &[("request_type", request_type)]
        );
    }

    /// Met à jour les métriques système
    pub fn update_system_metrics(
        &self,
        memory_bytes: u64,
        cpu_percent: f64,
        disk_bytes: u64,
        uptime_seconds: u64,
    ) {
        self.memory_usage_bytes.set(memory_bytes as f64);
        self.cpu_usage_percent.set(cpu_percent);
        self.disk_usage_bytes.set(disk_bytes as f64);
        self.uptime_seconds.set(uptime_seconds as f64);
    }

    /// Met à jour les métriques business
    pub fn update_business_metrics(
        &self,
        active_users: u64,
        premium_users: u64,
        playtime_seconds: u64,
        unique_tracks: u64,
        revenue: f64,
    ) {
        self.active_users.set(active_users as f64);
        self.premium_users.set(premium_users as f64);
        counter!("total_playtime_seconds", playtime_seconds);
        counter!("unique_tracks_played_total", unique_tracks);
        counter!("revenue_total", revenue as u64, &[("currency", "USD")]);
    }

    /// Obtient le handle Prometheus pour l'export
    pub fn get_handle(&self) -> &PrometheusHandle {
        &self.handle
    }

    /// Génère les métriques au format Prometheus
    pub fn gather_metrics(&self) -> String {
        self.handle.render()
    }

    /// Détermine la plage de taille de fichier pour les métriques
    fn get_file_size_range(&self, size_bytes: u64) -> &'static str {
        match size_bytes {
            0..=1024 => "0-1KB",
            1025..=10240 => "1-10KB",
            10241..=102400 => "10-100KB",
            102401..=1048576 => "100KB-1MB",
            1048577..=10485760 => "1-10MB",
            10485761..=104857600 => "10-100MB",
            _ => "100MB+",
        }
    }
}

/// Wrapper pour mesurer le temps d'exécution d'une opération
pub struct MetricsTimer {
    start: Instant,
    histogram: Histogram,
    labels: Vec<String>,
}

impl MetricsTimer {
    pub fn new(histogram: Histogram, labels: Vec<String>) -> Self {
        Self {
            start: Instant::now(),
            histogram,
            labels,
        }
    }

    pub fn finish(self) {
        let duration = self.start.elapsed();
        let label_refs: Vec<&str> = self.labels.iter().map(|s| s.as_str()).collect();
        self.histogram.record(duration.as_secs_f64(), &label_refs);
    }
}

/// Fonction utilitaire pour créer un timer d'encodage
pub fn time_encoding(
    metrics: &StreamPrometheusMetrics,
    codec: &str,
    quality: &str,
) -> MetricsTimer {
    let labels = vec![codec.to_string(), quality.to_string()];
    MetricsTimer::new(metrics.codec_encoding_duration_seconds.clone(), labels)
}

/// Fonction utilitaire pour créer un timer de décodage
pub fn time_decoding(metrics: &StreamPrometheusMetrics, codec: &str) -> MetricsTimer {
    let labels = vec![codec.to_string()];
    MetricsTimer::new(metrics.codec_decoding_duration_seconds.clone(), labels)
}

/// Fonction utilitaire pour créer un timer de transcodage
pub fn time_transcoding(
    metrics: &StreamPrometheusMetrics,
    input_codec: &str,
    output_codec: &str,
) -> MetricsTimer {
    let labels = vec![input_codec.to_string(), output_codec.to_string()];
    MetricsTimer::new(metrics.codec_transcoding_duration_seconds.clone(), labels)
}

/// Handler pour l'endpoint de métriques Prometheus
pub async fn metrics_handler(
    State(metrics): State<Arc<StreamPrometheusMetrics>>,
) -> Response<String> {
    let metrics_data = metrics.gather_metrics();

    tracing::debug!("📊 Métriques Prometheus exportées");

    Response::builder()
        .status(StatusCode::OK)
        .header("Content-Type", "text/plain; version=0.0.4; charset=utf-8")
        .body(metrics_data)
        .unwrap()
}

/// Crée un routeur pour les endpoints de métriques
pub fn create_metrics_router(metrics: Arc<StreamPrometheusMetrics>) -> Router {
    Router::new()
        .route("/metrics", get(metrics_handler))
        .with_state(metrics)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::ServerConfig;

    #[test]
    fn test_metrics_timer() {
        let histogram = register_histogram!("test_histogram", "Test histogram");
        let timer = MetricsTimer::new(histogram, vec!["test".to_string()]);

        // Simuler une opération
        std::thread::sleep(std::time::Duration::from_millis(10));
        timer.finish();
    }

    #[test]
    fn test_file_size_range() {
        let config = ServerConfig::default();
        let metrics = StreamPrometheusMetrics::new(&config).unwrap();

        assert_eq!(metrics.get_file_size_range(500), "0-1KB");
        assert_eq!(metrics.get_file_size_range(5000), "1-10KB");
        assert_eq!(metrics.get_file_size_range(50000), "10-100KB");
        assert_eq!(metrics.get_file_size_range(500000), "100KB-1MB");
        assert_eq!(metrics.get_file_size_range(5000000), "1-10MB");
        assert_eq!(metrics.get_file_size_range(50000000), "10-100MB");
        assert_eq!(metrics.get_file_size_range(500000000), "100MB+");
    }
}
