use super::job::{TranscodingJob, JobPriority};
use tokio::sync::mpsc::{self, Sender, Receiver};
use std::sync::Arc;
use tokio::sync::Mutex;

/// Taille maximale par défaut de la file d'attente
const DEFAULT_QUEUE_CAPACITY: usize = 100;

/// Erreurs de la file d'attente
#[derive(Debug, thiserror::Error)]
pub enum QueueError {
    #[error("Queue is full (backpressure active)")]
    QueueFull,
    #[error("Queue is closed")]
    QueueClosed,
}

/// Gestionnaire de files d'attente prioritaires
pub struct PriorityQueue {
    urgent_tx: Sender<TranscodingJob>,
    normal_tx: Sender<TranscodingJob>,
    background_tx: Sender<TranscodingJob>,
    
    // On garde les receivers dans un Mutex pour que les workers puissent les partager/consommer
    receivers: Arc<Mutex<QueueReceivers>>,
}

struct QueueReceivers {
    urgent_rx: Receiver<TranscodingJob>,
    normal_rx: Receiver<TranscodingJob>,
    background_rx: Receiver<TranscodingJob>,
}

impl PriorityQueue {
    pub fn new(capacity: usize) -> Self {
        let (urgent_tx, urgent_rx) = mpsc::channel(capacity);
        let (normal_tx, normal_rx) = mpsc::channel(capacity);
        let (background_tx, background_rx) = mpsc::channel(capacity);

        Self {
            urgent_tx,
            normal_tx,
            background_tx,
            receivers: Arc::new(Mutex::new(QueueReceivers {
                urgent_rx,
                normal_rx,
                background_rx,
            })),
        }
    }

    /// Soumet un job avec backpressure
    pub async fn submit(&self, job: TranscodingJob) -> Result<(), QueueError> {
        let tx = match job.priority {
            JobPriority::Urgent => &self.urgent_tx,
            JobPriority::Normal => &self.normal_tx,
            JobPriority::Background => &self.background_tx,
        };

        match tx.try_send(job) {
            Ok(_) => Ok(()),
            Err(mpsc::error::TrySendError::Full(_)) => Err(QueueError::QueueFull),
            Err(mpsc::error::TrySendError::Closed(_)) => Err(QueueError::QueueClosed),
        }
    }

    /// Récupère le prochain job disponible selon la priorité
    /// Urgent > Normal > Background
    pub async fn next_job(&self) -> Option<TranscodingJob> {
        let mut rxs = self.receivers.lock().await;

        // Stratégie de priorité stricte : on vide d'abord Urgent, puis Normal, etc.
        
        // 1. Tenter Urgent (non-bloquant)
        if let Ok(job) = rxs.urgent_rx.try_recv() {
            return Some(job);
        }

        // 2. Tenter Normal (non-bloquant)
        if let Ok(job) = rxs.normal_rx.try_recv() {
            return Some(job);
        }

        // 3. Tenter Background (non-bloquant)
        if let Ok(job) = rxs.background_rx.try_recv() {
            return Some(job);
        }

        // 4. Si rien, on doit attendre sur l'un des trois.
        // Pour contourner l'erreur E0499, on doit "splitter" le borrow ou utiliser une autre approche.
        // Une approche simple avec tokio::select! sur des &mut Receiver est délicate si ils sont dans un MutexGuard.
        // Solution: On utilise `recv()` séquentiellement avec un timeout très court ou `tokio::select!` sur les champs
        // MAIS comme on a le Mutex, on est le seul thread à lire.
        
        // On va extraire les références mutables pour le select
        let QueueReceivers {
            urgent_rx,
            normal_rx,
            background_rx,
        } = &mut *rxs;

        tokio::select! {
            Some(job) = urgent_rx.recv() => Some(job),
            Some(job) = normal_rx.recv() => Some(job),
            Some(job) = background_rx.recv() => Some(job),
            else => None // Tous les channels fermés
        }
    }
}

impl Default for PriorityQueue {
    fn default() -> Self {
        Self::new(DEFAULT_QUEUE_CAPACITY)
    }
}
