use std::path::{Path, PathBuf};
use std::process::Command;
use std::fs;
use crate::types::Result;
use crate::utils::log::{success, info};
use super::commands::SpeedArgs;

fn detect_extension(input: &Path) -> &str {
    input.extension()
        .and_then(|e| e.to_str())
        .unwrap_or("mp4")
}

fn build_output_path(input: &Path, output: Option<PathBuf>, speed: f64) -> PathBuf {
    if let Some(out) = output {
        return out;
    }
    
    let parent = input.parent().unwrap_or_else(|| Path::new("."));
    let stem = input.file_stem()
        .and_then(|s| s.to_str())
        .unwrap_or("video");
    let ext = detect_extension(input);
    
    let speed_str = format!("{:.1}x", speed).replace('.', "_");
    parent.join(format!("{}_{}.{}", stem, speed_str, ext))
}

pub async fn execute(args: SpeedArgs) -> Result<()> {
    let input = &args.input;
    
    if !input.exists() {
        return Err(format!("Input file not found: {}", input.display()).into());
    }
    
    let speed = args.speed;
    if speed <= 0.0 {
        return Err("Speed must be positive".into());
    }
    if speed < 0.25 || speed > 4.0 {
        return Err("Speed must be between 0.25 and 4.0".into());
    }
    
    let output = build_output_path(input, args.output, speed);
    
    if output.exists() {
        return Err(format!("Output file already exists: {}", output.display()).into());
    }
    
    if let Some(parent) = output.parent() {
        fs::create_dir_all(parent)?;
    }
    
    info(&format!("Adjusting playback speed to {:.2}x...", speed));
    
    let video_pts = 1.0 / speed;
    let video_filter = format!("setpts={}*PTS", video_pts);
    
    let status = if args.keep_audio {
        let audio_tempo = calculate_audio_tempo(speed);
        let audio_filter = build_audio_filter(&audio_tempo);
        
        Command::new("ffmpeg")
            .args(["-y", "-hide_banner", "-loglevel", "warning"])
            .args(["-i"])
            .arg(input)
            .args(["-filter_complex", &format!(
                "[0:v]{}[v];[0:a]{}[a]",
                video_filter,
                audio_filter
            )])
            .args(["-map", "[v]", "-map", "[a]"])
            .arg(&output)
            .status()?
    } else {
        Command::new("ffmpeg")
            .args(["-y", "-hide_banner", "-loglevel", "warning"])
            .args(["-i"])
            .arg(input)
            .args(["-vf", &video_filter])
            .args(["-an"])
            .arg(&output)
            .status()?
    };
    
    if !status.success() {
        if output.exists() {
            let _ = fs::remove_file(&output);
        }
        return Err("FFmpeg speed adjustment failed".into());
    }
    
    let output_size = fs::metadata(&output)?.len();
    
    println!();
    success("Speed adjustment complete!");
    println!("  Speed:  {:.2}x", speed);
    println!("  Audio:  {}", if args.keep_audio { "preserved (tempo adjusted)" } else { "removed" });
    println!("  Output: {}", output.display());
    println!("  Size:   {} bytes ({:.2} MB)", output_size, output_size as f64 / 1024.0 / 1024.0);
    
    Ok(())
}

fn calculate_audio_tempo(speed: f64) -> Vec<f64> {
    let mut tempos = Vec::new();
    let mut remaining = speed;
    
    while remaining > 2.0 {
        tempos.push(2.0);
        remaining /= 2.0;
    }
    while remaining < 0.5 {
        tempos.push(0.5);
        remaining /= 0.5;
    }
    tempos.push(remaining);
    
    tempos
}

fn build_audio_filter(tempos: &[f64]) -> String {
    tempos.iter()
        .map(|t| format!("atempo={:.4}", t))
        .collect::<Vec<_>>()
        .join(",")
}
