use crate::transcoding::codecs::{AudioCodec, ContainerFormat};
use std::path::{Path, PathBuf};
use std::process::Stdio;
use tokio::process::Command;

#[derive(Debug, thiserror::Error)]
pub enum BuilderError {
    #[error("Input file not set")]
    MissingInput,
    #[error("Output file not set")]
    MissingOutput,
    #[error("Invalid input path: {0}")]
    InvalidInputPath(String),
    #[error("Invalid output path: {0}")]
    InvalidOutputPath(String),
}

pub struct FfmpegCommandBuilder {
    input: Option<PathBuf>,
    output: Option<PathBuf>,
    audio_codec: Option<AudioCodec>,
    bitrate: Option<u32>,
    sample_rate: Option<u32>,
    channels: Option<u8>,
    container: Option<ContainerFormat>,
    hls_time: Option<u32>,
    overwrite: bool,
}

impl FfmpegCommandBuilder {
    pub fn new() -> Self {
        Self {
            input: None,
            output: None,
            audio_codec: None,
            bitrate: None,
            sample_rate: None,
            channels: None,
            container: None,
            hls_time: None,
            overwrite: true, // Par défaut on écrase
        }
    }

    pub fn input<P: AsRef<Path>>(mut self, path: P) -> Self {
        self.input = Some(path.as_ref().to_path_buf());
        self
    }

    pub fn output<P: AsRef<Path>>(mut self, path: P) -> Self {
        self.output = Some(path.as_ref().to_path_buf());
        self
    }

    pub fn audio_codec(mut self, codec: AudioCodec) -> Self {
        self.audio_codec = Some(codec);
        self
    }

    pub fn bitrate(mut self, bitrate: u32) -> Self {
        self.bitrate = Some(bitrate);
        self
    }

    pub fn sample_rate(mut self, sample_rate: u32) -> Self {
        self.sample_rate = Some(sample_rate);
        self
    }

    pub fn channels(mut self, channels: u8) -> Self {
        self.channels = Some(channels);
        self
    }

    pub fn container(mut self, container: ContainerFormat) -> Self {
        self.container = Some(container);
        self
    }

    pub fn hls_time(mut self, seconds: u32) -> Self {
        self.hls_time = Some(seconds);
        self
    }

    /// Valide et construit la commande Tokio
    pub fn build(self) -> Result<Command, BuilderError> {
        let input_path = self.input.ok_or(BuilderError::MissingInput)?;
        let output_path = self.output.ok_or(BuilderError::MissingOutput)?;

        // Validation anti-traversal basique (doit être renforcée par le validateur global)
        if input_path.to_string_lossy().contains("..") {
            return Err(BuilderError::InvalidInputPath("Path traversal detected".to_string()));
        }

        let mut cmd = Command::new("ffmpeg");

        // Options globales
        if self.overwrite {
            cmd.arg("-y");
        }
        cmd.arg("-hide_banner");
        cmd.arg("-nostats"); // On parse manuellement la progression si besoin, ou on utilise -progress
        
        // Isolation CPU: 1 thread par processus FFmpeg
        cmd.arg("-threads").arg("1");
        
        // Forcer audio-only (évite les problèmes avec vidéo)
        cmd.arg("-map").arg("0:a");

        // Input
        cmd.arg("-i").arg(input_path);

        // Codec Audio
        if let Some(codec) = self.audio_codec {
            cmd.arg("-c:a").arg(codec.to_string());
        }

        // Bitrate
        if let Some(bitrate) = self.bitrate {
            cmd.arg("-b:a").arg(format!("{}", bitrate));
        }

        // Sample Rate
        if let Some(sr) = self.sample_rate {
            cmd.arg("-ar").arg(sr.to_string());
        }

        // Channels
        if let Some(ch) = self.channels {
            cmd.arg("-ac").arg(ch.to_string());
        }

        // Container specific flags
        if let Some(ContainerFormat::HLS) = self.container {
            cmd.arg("-f").arg("hls");
            
            if let Some(time) = self.hls_time {
                cmd.arg("-hls_time").arg(time.to_string());
            }
            
            cmd.arg("-hls_playlist_type").arg("vod");
            // Pour VOD, on veut la liste complète des segments
            cmd.arg("-hls_list_size").arg("0");
            
            // Segment naming pattern
            // output_path est supposé être /path/to/playlist.m3u8
            // On doit déduire le pattern de segment
            let segment_filename = output_path.with_file_name("segment_%05d.ts");
            cmd.arg("-hls_segment_filename").arg(segment_filename);
        }

        // Output
        cmd.arg(output_path);

        // Configuration process
        cmd.stdout(Stdio::piped());
        cmd.stderr(Stdio::piped());

        Ok(cmd)
    }
}

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