use axum::{
    extract::{Path as AxumPath, Query, State},
    http::{header, StatusCode},
    response::Response,
};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, sync::Arc, time::SystemTime};
use tokio::sync::RwLock;

use crate::{config::Config, utils::validate_signature};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdaptiveProfile {
    pub quality_id: String,
    pub bitrate_kbps: u32,
    pub sample_rate: u32,
    pub channels: u8,
    pub codec: String,
    pub file_extension: String,
    pub bandwidth_estimate_kbps: u32,
}

impl AdaptiveProfile {
    pub fn high_quality() -> Self {
        Self {
            quality_id: "high".to_string(),
            bitrate_kbps: 320,
            sample_rate: 44100,
            channels: 2,
            codec: "mp3".to_string(),
            file_extension: "mp3".to_string(),
            bandwidth_estimate_kbps: 400,
        }
    }

    pub fn medium_quality() -> Self {
        Self {
            quality_id: "medium".to_string(),
            bitrate_kbps: 192,
            sample_rate: 44100,
            channels: 2,
            codec: "mp3".to_string(),
            file_extension: "mp3".to_string(),
            bandwidth_estimate_kbps: 250,
        }
    }

    pub fn low_quality() -> Self {
        Self {
            quality_id: "low".to_string(),
            bitrate_kbps: 128,
            sample_rate: 22050,
            channels: 1,
            codec: "mp3".to_string(),
            file_extension: "mp3".to_string(),
            bandwidth_estimate_kbps: 160,
        }
    }

    pub fn mobile_quality() -> Self {
        Self {
            quality_id: "mobile".to_string(),
            bitrate_kbps: 96,
            sample_rate: 22050,
            channels: 1,
            codec: "mp3".to_string(),
            file_extension: "mp3".to_string(),
            bandwidth_estimate_kbps: 120,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientCapabilities {
    pub estimated_bandwidth_kbps: u32,
    pub buffer_duration_ms: u32,
    pub connection_type: ConnectionType,
    pub device_type: DeviceType,
    pub preferred_quality: Option<String>,
    pub adaptive_enabled: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum ConnectionType {
    Wifi,
    Cellular4G,
    Cellular3G,
    Cellular2G,
    Ethernet,
    Unknown,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum DeviceType {
    Desktop,
    Mobile,
    Tablet,
    SmartSpeaker,
    CarAudio,
    Unknown,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamingSession {
    pub session_id: String,
    pub track_id: String,
    pub client_id: String,
    pub current_quality: String,
    pub client_capabilities: ClientCapabilities,
    pub performance_metrics: PerformanceMetrics,
    pub created_at: SystemTime,
    pub last_updated: SystemTime,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
    pub buffer_health_percentage: f32,
    pub download_speed_kbps: u32,
    pub packet_loss_percentage: f32,
    pub latency_ms: u32,
    pub rebuffer_count: u32,
    pub rebuffer_duration_ms: u32,
    pub quality_switches: u32,
}

impl Default for PerformanceMetrics {
    fn default() -> Self {
        Self {
            buffer_health_percentage: 100.0,
            download_speed_kbps: 1000,
            packet_loss_percentage: 0.0,
            latency_ms: 50,
            rebuffer_count: 0,
            rebuffer_duration_ms: 0,
            quality_switches: 0,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HLSManifest {
    pub version: u8,
    pub target_duration: u32,
    pub media_sequence: u32,
    pub segments: Vec<HLSSegment>,
    pub end_list: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HLSSegment {
    pub duration: f32,
    pub url: String,
    pub byte_range: Option<(u64, u64)>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MasterPlaylist {
    pub version: u8,
    pub streams: Vec<StreamInfo>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamInfo {
    pub bandwidth: u32,
    pub codecs: String,
    pub resolution: Option<String>,
    pub url: String,
}

#[derive(Clone)]
pub struct AdaptiveStreamingManager {
    config: Arc<Config>,
    sessions: Arc<RwLock<HashMap<String, StreamingSession>>>,
    profiles: Vec<AdaptiveProfile>,
}

impl AdaptiveStreamingManager {
    pub fn new(config: Arc<Config>) -> Self {
        let profiles = vec![
            AdaptiveProfile::high_quality(),
            AdaptiveProfile::medium_quality(),
            AdaptiveProfile::low_quality(),
            AdaptiveProfile::mobile_quality(),
        ];

        Self {
            config,
            sessions: Arc::new(RwLock::new(HashMap::new())),
            profiles,
        }
    }

    pub async fn start_quality_monitor(&self) {
        let _sessions = self.sessions.clone();
        tokio::spawn(async move {
            loop {
                tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
                // Logique de surveillance de qualité ici
            }
        });
    }

    pub async fn create_session(&self, session_id: String, track_id: String) -> AdaptiveProfile {
        let session = StreamingSession {
            session_id: session_id.clone(),
            track_id,
            client_id: "default".to_string(),
            current_quality: "medium".to_string(),
            client_capabilities: ClientCapabilities {
                estimated_bandwidth_kbps: 1000,
                buffer_duration_ms: 5000,
                connection_type: ConnectionType::Wifi,
                device_type: DeviceType::Desktop,
                preferred_quality: None,
                adaptive_enabled: true,
            },
            performance_metrics: PerformanceMetrics::default(),
            created_at: SystemTime::now(),
            last_updated: SystemTime::now(),
        };

        self.sessions.write().await.insert(session_id, session);
        AdaptiveProfile::medium_quality()
    }

    pub async fn update_session_quality(&self, session_id: &str, quality: String) {
        if let Some(session) = self.sessions.write().await.get_mut(session_id) {
            session.current_quality = quality;
            session.last_updated = SystemTime::now();
        }
    }

    pub async fn generate_master_playlist(
        &self,
        track_id: &str,
    ) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
        let mut playlist = String::from("#EXTM3U\n#EXT-X-VERSION:6\n");

        for profile in &self.profiles {
            playlist.push_str(&format!(
                "#EXT-X-STREAM-INF:BANDWIDTH={},CODECS=\"{}\"\n{}/{}/playlist.m3u8\n",
                profile.bandwidth_estimate_kbps * 1000,
                profile.codec,
                track_id,
                profile.quality_id
            ));
        }

        Ok(playlist)
    }

    pub async fn generate_quality_playlist(
        &self,
        track_id: &str,
        quality: &str,
    ) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
        let playlist = format!(
            "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:10\n#EXTINF:10.0,\n{}/{}/segment0.ts\n#EXT-X-ENDLIST\n",
            track_id, quality
        );
        Ok(playlist)
    }

    pub async fn get_streaming_stats(&self) -> serde_json::Value {
        let sessions = self.sessions.read().await;
        serde_json::json!({
            "active_sessions": sessions.len(),
            "profiles": self.profiles.iter().map(|p| &p.quality_id).collect::<Vec<_>>()
        })
    }

    async fn update_quality_decisions(&self) {
        let mut sessions = self.sessions.write().await;
        
        for session in sessions.values_mut() {
            // 1. Get current metrics
            let metrics = &session.performance_metrics;
            let current_quality = &session.current_quality;
            
            // 2. Determine current profile bandwidth
            let current_profile = self.profiles.iter()
                .find(|p| &p.quality_id == current_quality);
                
            if let Some(profile) = current_profile {
                let required_bandwidth = profile.bandwidth_estimate_kbps;
                
                // 3. Decision logic
                let mut new_quality = current_quality.clone();
                
                // DOWNGRADE Logic
                // If buffer is critical (< 20%) or bandwidth is insufficient
                if metrics.buffer_health_percentage < 20.0 || metrics.download_speed_kbps < required_bandwidth {
                    // Find a lower quality profile
                    if let Some(lower) = self.get_lower_quality(current_quality) {
                        new_quality = lower.quality_id.clone();
                        tracing::info!("📉 Downgrading session {} to {}", session.session_id, new_quality);
                    }
                }
                // UPGRADE Logic
                // If buffer is healthy (> 80%) AND bandwidth is plentiful (> 1.5x required)
                else if metrics.buffer_health_percentage > 80.0 && metrics.download_speed_kbps > (required_bandwidth as f32 * 1.5) as u32 {
                    // Find a higher quality profile
                    // But verify that the NEW profile's bandwidth is also covered
                    if let Some(higher) = self.get_higher_quality(current_quality) {
                        if metrics.download_speed_kbps > (higher.bandwidth_estimate_kbps as f32 * 1.2) as u32 {
                            new_quality = higher.quality_id.clone();
                            tracing::info!("📈 Upgrading session {} to {}", session.session_id, new_quality);
                        }
                    }
                }
                
                // 4. Apply change
                if new_quality != *current_quality {
                    session.current_quality = new_quality;
                    session.performance_metrics.quality_switches += 1;
                    session.last_updated = SystemTime::now();
                }
            }
        }
    }

    fn get_lower_quality(&self, current_quality: &str) -> Option<&AdaptiveProfile> {
        // Profiles are sorted High -> Low? No, assuming standard list
        // We can sort them by bandwidth to find the next lower
        let mut sorted_profiles = self.profiles.clone();
        sorted_profiles.sort_by_key(|p| p.bitrate_kbps); // Ascending (Mobile -> High)
        
        // Find current index
        if let Some(idx) = sorted_profiles.iter().position(|p| p.quality_id == current_quality) {
            if idx > 0 {
                return Some(&self.profiles[self.profiles.iter().position(|p| p.quality_id == sorted_profiles[idx-1].quality_id).unwrap()]);
            }
        }
        None
    }

    fn get_higher_quality(&self, current_quality: &str) -> Option<&AdaptiveProfile> {
        let mut sorted_profiles = self.profiles.clone();
        sorted_profiles.sort_by_key(|p| p.bitrate_kbps); // Ascending
        
        if let Some(idx) = sorted_profiles.iter().position(|p| p.quality_id == current_quality) {
            if idx < sorted_profiles.len() - 1 {
                return Some(&self.profiles[self.profiles.iter().position(|p| p.quality_id == sorted_profiles[idx+1].quality_id).unwrap()]);
            }
        }
        None
    }
}

#[derive(Clone)]
pub struct QualityProfile {
    pub name: String,
    pub bandwidth_kbps: u32,
    pub codec: String,
    pub bitrate_kbps: u32,
    pub sample_rate: u32,
}

impl QualityProfile {
    pub fn high() -> Self {
        Self {
            name: "high".to_string(),
            bandwidth_kbps: 320,
            codec: "mp3".to_string(),
            bitrate_kbps: 320,
            sample_rate: 44100,
        }
    }

    pub fn medium() -> Self {
        Self {
            name: "medium".to_string(),
            bandwidth_kbps: 192,
            codec: "mp3".to_string(),
            bitrate_kbps: 192,
            sample_rate: 44100,
        }
    }

    pub fn low() -> Self {
        Self {
            name: "low".to_string(),
            bandwidth_kbps: 128,
            codec: "mp3".to_string(),
            bitrate_kbps: 128,
            sample_rate: 22050,
        }
    }

    pub fn mobile() -> Self {
        Self {
            name: "mobile".to_string(),
            bandwidth_kbps: 96,
            codec: "opus".to_string(),
            bitrate_kbps: 96,
            sample_rate: 48000,
        }
    }
}

/// Query parameters pour le streaming adaptatif
#[derive(Debug, Deserialize)]
pub struct AdaptiveStreamQuery {
    pub expires: String,
    pub sig: String,
    pub quality: Option<String>,
    pub session_id: Option<String>,
    pub bandwidth: Option<u32>,
    pub buffer_ms: Option<u32>,
    pub connection: Option<String>,
    pub device: Option<String>,
}

/// Handler pour le master playlist HLS
pub async fn hls_master_playlist(
    AxumPath(track_id): AxumPath<String>,
    Query(params): Query<AdaptiveStreamQuery>,
    State(streaming_manager): State<Arc<AdaptiveStreamingManager>>,
) -> Result<Response, (StatusCode, String)> {
    // Valider la signature
    if !validate_signature(
        &streaming_manager.config,
        &track_id,
        &params.expires,
        &params.sig,
    ) {
        return Err((StatusCode::FORBIDDEN, "Signature invalide".to_string()));
    }

    let _base_url = format!("http://localhost:{}", streaming_manager.config.port);

    match streaming_manager.generate_master_playlist(&track_id).await {
        Ok(playlist) => {
            let response = Response::builder()
                .status(StatusCode::OK)
                .header(header::CONTENT_TYPE, "application/vnd.apple.mpegurl")
                .header(header::CACHE_CONTROL, "no-cache")
                .body(playlist.into())
                .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

            Ok(response)
        }
        Err(e) => Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())),
    }
}

/// Handler pour les playlists de qualité spécifique
pub async fn hls_quality_playlist(
    AxumPath((track_id, quality)): AxumPath<(String, String)>,
    Query(params): Query<AdaptiveStreamQuery>,
    State(streaming_manager): State<Arc<AdaptiveStreamingManager>>,
) -> Result<Response, (StatusCode, String)> {
    // Valider la signature
    if !validate_signature(
        &streaming_manager.config,
        &track_id,
        &params.expires,
        &params.sig,
    ) {
        return Err((StatusCode::FORBIDDEN, "Signature invalide".to_string()));
    }

    let _base_url = format!("http://localhost:{}", streaming_manager.config.port);

    match streaming_manager
        .generate_quality_playlist(&track_id, &quality)
        .await
    {
        Ok(playlist) => {
            let response = Response::builder()
                .status(StatusCode::OK)
                .header(header::CONTENT_TYPE, "application/vnd.apple.mpegurl")
                .header(header::CACHE_CONTROL, "no-cache")
                .body(playlist.into())
                .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

            Ok(response)
        }
        Err(e) => Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())),
    }
}
