//! Module de conversion audio en FLAC
//!
//! Ce module gère la conversion de divers formats audio vers FLAC
//! pour standardiser le stockage dans le cache.

use anyhow::{anyhow, Result};
use std::io::Cursor;
use symphonia::core::audio::SampleBuffer;
use symphonia::core::codecs::{DecoderOptions, CODEC_TYPE_NULL};
use symphonia::core::errors::Error as SymphoniaError;
use symphonia::core::formats::FormatOptions;
use symphonia::core::io::MediaSourceStream;
use symphonia::core::meta::MetadataOptions;
use symphonia::core::probe::Hint;

/// Convertit des données audio en FLAC
///
/// Cette fonction accepte n'importe quel format audio supporté par Symphonia
/// et le convertit en FLAC pour un stockage standardisé.
///
/// # Arguments
///
/// * `data` - Données audio brutes (n'importe quel format)
/// * `extension` - Extension du fichier source (optionnel, aide à la détection)
///
/// # Returns
///
/// Données audio au format FLAC
///
/// # Exemple
///
/// ```rust,no_run
/// use pmoaudiocache::flac::convert_to_flac;
///
/// let mp3_data = std::fs::read("track.mp3").unwrap();
/// let flac_data = convert_to_flac(&mp3_data, Some("mp3")).unwrap();
/// ```
pub fn convert_to_flac(data: &[u8], extension: Option<&str>) -> Result<Vec<u8>> {
    // Si c'est déjà du FLAC, on le retourne tel quel
    if is_flac(data) {
        return Ok(data.to_vec());
    }

    // Créer un MediaSource depuis les données (en clonant pour avoir 'static)
    let data_owned = data.to_vec();
    let cursor = Cursor::new(data_owned);
    let mss = MediaSourceStream::new(Box::new(cursor), Default::default());

    // Créer un hint si on a l'extension
    let mut hint = Hint::new();
    if let Some(ext) = extension {
        hint.with_extension(ext);
    }

    // Prober le format
    let probed = symphonia::default::get_probe()
        .format(
            &hint,
            mss,
            &FormatOptions::default(),
            &MetadataOptions::default(),
        )
        .map_err(|e| anyhow!("Impossible de détecter le format audio: {}", e))?;

    let mut format = probed.format;

    // Obtenir le premier track audio
    let track = format
        .tracks()
        .iter()
        .find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
        .ok_or_else(|| anyhow!("Aucune piste audio trouvée"))?;

    // Créer un décodeur
    let mut decoder = symphonia::default::get_codecs()
        .make(&track.codec_params, &DecoderOptions::default())
        .map_err(|e| anyhow!("Impossible de créer le décodeur: {}", e))?;

    // Buffer pour stocker les samples décodés
    let mut samples = Vec::new();
    let track_id = track.id;

    // Décoder tous les packets
    loop {
        let packet = match format.next_packet() {
            Ok(packet) => packet,
            Err(SymphoniaError::ResetRequired) => {
                // Reset du décodeur requis
                decoder.reset();
                continue;
            }
            Err(SymphoniaError::IoError(e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
                break;
            }
            Err(e) => return Err(anyhow!("Erreur lors de la lecture: {}", e)),
        };

        // Ignorer les packets qui ne sont pas de notre track
        if packet.track_id() != track_id {
            continue;
        }

        match decoder.decode(&packet) {
            Ok(decoded) => {
                // Convertir les samples en format standard
                let spec = *decoded.spec();
                let duration = decoded.capacity() as u64;

                let mut sample_buf = SampleBuffer::<i16>::new(duration, spec);
                sample_buf.copy_interleaved_ref(decoded);

                samples.extend_from_slice(sample_buf.samples());
            }
            Err(SymphoniaError::DecodeError(_)) => continue,
            Err(e) => return Err(anyhow!("Erreur de décodage: {}", e)),
        }
    }

    if samples.is_empty() {
        return Err(anyhow!("Aucun sample décodé"));
    }

    // Note: Pour l'encodage FLAC, on aurait besoin d'une bibliothèque comme
    // `flacenc` qui n'existe pas encore en Rust. Pour l'instant, on stocke
    // les données telles quelles si c'est déjà du FLAC, sinon on retourne
    // les données originales avec un warning.

    // TODO: Implémenter l'encodage FLAC quand une bibliothèque sera disponible
    tracing::warn!("Encodage FLAC non implémenté, stockage du format original");
    Ok(data.to_vec())
}

/// Vérifie si les données sont déjà au format FLAC
fn is_flac(data: &[u8]) -> bool {
    data.len() >= 4 && &data[0..4] == b"fLaC"
}

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

    #[test]
    fn test_is_flac() {
        let flac_header = b"fLaC\x00\x00\x00\x22";
        assert!(is_flac(flac_header));

        let not_flac = b"RIFF\x00\x00\x00\x00";
        assert!(!is_flac(not_flac));
    }
}
