//! Nodes du pipeline audio
//!
//! Ce module contient tous les types de nodes disponibles pour construire
//! un pipeline audio, ainsi que les traits et structures de support.

use std::sync::Arc;

use crate::type_constraints::{TypeMismatch, TypeRequirement};
use crate::AudioSegment;

/// Taille par défaut du buffer de channel MPSC pour les nodes
/// Cette valeur détermine combien de segments audio peuvent être mis en attente
/// avant que le producteur soit bloqué (backpressure).
pub const DEFAULT_CHANNEL_SIZE: usize = 16;

/// Durée par défaut des chunks audio en millisecondes
/// Cette valeur détermine la latence de traitement et le compromis efficacité/réactivité.
/// 50ms offre un bon équilibre pour la plupart des applications de lecture audio.
pub const DEFAULT_CHUNK_DURATION_MS: f64 = 50.0;

// Modules actifs
pub mod audio_sink;
pub mod converter_nodes;
pub mod file_source;
pub mod flac_file_sink;
pub mod http_source;
pub mod resampling_node;
pub mod timer_buffer_node;
pub mod timer_node;
pub mod position_tracker_node;

// Modules temporairement désactivés
/*
pub mod buffer_node;
pub mod chromecast_sink;
pub mod decoder_node;
pub mod disk_sink;
pub mod dsp_node;
pub mod mpd_sink;
pub mod sink_node;
pub mod source_node;
pub mod volume_node;
*/

/// Trait de base pour tous les nodes audio
///
/// Tous les nodes du pipeline implémentent ce trait pour permettre
/// une interface uniforme de traitement des chunks audio.
#[async_trait::async_trait]
pub trait AudioNode: Send + Sync {
    /// Push un chunk vers ce node
    ///
    /// # Erreurs
    ///
    /// Retourne `AudioError::SendError` si l'envoi échoue
    async fn push(&mut self, chunk: Arc<AudioSegment>) -> Result<(), AudioError>;

    /// Ferme le node proprement
    async fn close(&mut self);
}

/// Trait pour les nodes qui déclarent leurs types acceptés/produits
///
/// Ce trait permet de vérifier la compatibilité des types entre nodes
/// avant de les connecter dans un pipeline.
///
/// # Exemples
///
/// ```no_run
/// use pmoaudio::{FileSource, FlacFileSink, TypedAudioNode};
/// use pmoaudio::type_constraints::check_compatibility;
///
/// // Vérifier la compatibilité avant de connecter
/// let source = FileSource::new("input.flac");
/// let (sink, tx) = FlacFileSink::new("output.flac");
///
/// let source_output = source.output_type();
/// let sink_input = sink.input_type();
///
/// match check_compatibility(&source_output, &sink_input) {
///     Ok(()) => println!("Types compatibles!"),
///     Err(e) => eprintln!("Types incompatibles: {}", e),
/// }
/// ```
pub trait TypedAudioNode {
    /// Retourne les types que ce node peut accepter en entrée
    ///
    /// Pour les sources (qui ne consomment rien), retourne `None`.
    fn input_type(&self) -> Option<TypeRequirement>;

    /// Retourne les types que ce node peut produire en sortie
    ///
    /// Pour les sinks (qui ne produisent rien), retourne `None`.
    fn output_type(&self) -> Option<TypeRequirement>;

    /// Vérifie si ce node peut accepter les chunks d'un producer donné
    ///
    /// # Erreurs
    ///
    /// Retourne `AudioError::TypeMismatch` si les types sont incompatibles
    fn can_accept_from(&self, producer: &dyn TypedAudioNode) -> Result<(), AudioError> {
        match (producer.output_type(), self.input_type()) {
            (Some(prod), Some(cons)) => crate::type_constraints::check_compatibility(&prod, &cons)
                .map_err(|e| AudioError::TypeMismatch(e)),
            (None, Some(_)) => Err(AudioError::TypeMismatch(TypeMismatch {
                producer: TypeRequirement::any(), // Placeholder
                consumer: self.input_type().unwrap(),
                incompatible_type: None,
            })),
            _ => Ok(()), // Si pas de contrainte, toujours compatible
        }
    }
}

/// Erreurs possibles dans le pipeline audio
#[derive(Debug, Clone)]
pub enum AudioError {
    /// Échec d'envoi d'un chunk à travers un channel
    SendError,
    /// Échec de réception d'un chunk depuis un channel
    ReceiveError,
    /// Erreur de traitement avec message descriptif
    ProcessingError(String),
    /// Incompatibilité de types entre nodes
    TypeMismatch(TypeMismatch),
    /// Un nœud enfant s'est terminé prématurément (anormal dans un pipeline descendant)
    ChildFinished,
    /// Un nœud enfant est mort (channel fermé pendant un send)
    ChildDied,
    /// Erreur d'I/O (fichier, réseau, etc.)
    IoError(String),
}

impl std::fmt::Display for AudioError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AudioError::SendError => write!(f, "Failed to send audio chunk"),
            AudioError::ReceiveError => write!(f, "Failed to receive audio chunk"),
            AudioError::ProcessingError(msg) => write!(f, "Processing error: {}", msg),
            AudioError::TypeMismatch(tm) => write!(f, "{}", tm),
            AudioError::ChildFinished => write!(f, "Child node finished prematurely"),
            AudioError::ChildDied => write!(f, "Child node died unexpectedly"),
            AudioError::IoError(msg) => write!(f, "I/O error: {}", msg),
        }
    }
}

impl std::error::Error for AudioError {}
