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::SplitArgs;

fn get_video_duration(input: &Path) -> Result<f64> {
    let output = Command::new("ffprobe")
        .args([
            "-v", "error",
            "-show_entries", "format=duration",
            "-of", "default=noprint_wrappers=1:nokey=1",
        ])
        .arg(input)
        .output()?;
    
    if !output.status.success() {
        return Err("Failed to get video duration".into());
    }
    
    let duration_str = String::from_utf8_lossy(&output.stdout);
    duration_str.trim()
        .parse::<f64>()
        .map_err(|_| format!("Failed to parse duration: {}", duration_str).into())
}

fn parse_interval(interval: &str) -> Result<f64> {
    let trimmed = interval.trim();
    
    if let Some(secs) = trimmed.strip_suffix('s') {
        return secs.parse::<f64>()
            .map_err(|_| format!("Invalid interval: {}", interval).into());
    }
    
    if let Some(mins) = trimmed.strip_suffix('m') {
        let m: f64 = mins.parse()
            .map_err(|_| format!("Invalid interval: {}", interval))?;
        return Ok(m * 60.0);
    }
    
    if let Some(hours) = trimmed.strip_suffix('h') {
        let h: f64 = hours.parse()
            .map_err(|_| format!("Invalid interval: {}", interval))?;
        return Ok(h * 3600.0);
    }
    
    let parts: Vec<&str> = trimmed.split(':').collect();
    match parts.len() {
        1 => {
            parts[0].parse::<f64>()
                .map_err(|_| format!("Invalid interval: {}", interval).into())
        }
        2 => {
            let mins: f64 = parts[0].parse()
                .map_err(|_| format!("Invalid interval: {}", interval))?;
            let secs: f64 = parts[1].parse()
                .map_err(|_| format!("Invalid interval: {}", interval))?;
            Ok(mins * 60.0 + secs)
        }
        3 => {
            let hours: f64 = parts[0].parse()
                .map_err(|_| format!("Invalid interval: {}", interval))?;
            let mins: f64 = parts[1].parse()
                .map_err(|_| format!("Invalid interval: {}", interval))?;
            let secs: f64 = parts[2].parse()
                .map_err(|_| format!("Invalid interval: {}", interval))?;
            Ok(hours * 3600.0 + mins * 60.0 + secs)
        }
        _ => Err(format!("Invalid interval: {}. Use 30s, 5m, 1h, or HH:MM:SS format", interval).into())
    }
}

fn format_time(seconds: f64) -> String {
    let hours = (seconds / 3600.0).floor() as u32;
    let mins = ((seconds % 3600.0) / 60.0).floor() as u32;
    let secs = seconds % 60.0;
    format!("{:02}:{:02}:{:06.3}", hours, mins, secs)
}

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

fn build_output_dir(input: &Path, out_dir: Option<PathBuf>) -> PathBuf {
    if let Some(dir) = out_dir {
        return dir;
    }
    
    let parent = input.parent().unwrap_or_else(|| Path::new("."));
    let stem = input.file_stem()
        .and_then(|s| s.to_str())
        .unwrap_or("video");
    
    parent.join(format!("{}_split", stem))
}

fn build_segment_path(output_dir: &Path, stem: &str, index: usize, ext: &str) -> PathBuf {
    output_dir.join(format!("{}_{:04}.{}", stem, index + 1, ext))
}

pub async fn execute(args: SplitArgs) -> Result<()> {
    let input = &args.input;
    
    if !input.exists() {
        return Err(format!("Input file not found: {}", input.display()).into());
    }
    
    let interval_secs = parse_interval(&args.interval)?;
    if interval_secs <= 0.0 {
        return Err("Interval must be positive".into());
    }
    
    info("Getting video duration...");
    let total_duration = get_video_duration(input)?;
    
    let segment_count = (total_duration / interval_secs).ceil() as usize;
    if segment_count == 0 {
        return Err("Video is too short for the specified interval".into());
    }
    
    info(&format!("Video duration: {:.2}s ({:.2} min)", total_duration, total_duration / 60.0));
    info(&format!("Interval: {:.2}s", interval_secs));
    info(&format!("Expected segments: {}", segment_count));
    
    let output_dir = build_output_dir(input, args.out_dir);
    fs::create_dir_all(&output_dir)?;
    
    let stem = input.file_stem()
        .and_then(|s| s.to_str())
        .unwrap_or("segment");
    let ext = detect_extension(input);
    
    println!();
    info("Splitting video...");
    
    let mut created_files: Vec<PathBuf> = Vec::new();
    let mut current_start = 0.0;
    let mut index = 0;
    
    while current_start < total_duration {
        let current_end = (current_start + interval_secs).min(total_duration);
        let segment_path = build_segment_path(&output_dir, stem, index, ext);
        
        info(&format!("  Segment {}: {} - {}", 
            index + 1, 
            format_time(current_start), 
            format_time(current_end)));
        
        let status = Command::new("ffmpeg")
            .args(["-y", "-hide_banner", "-loglevel", "warning"])
            .args(["-i"])
            .arg(input)
            .args(["-ss", &format_time(current_start)])
            .args(["-t", &format!("{:.3}", current_end - current_start)])
            .args(["-c", "copy"])
            .arg(&segment_path)
            .status()?;
        
        if !status.success() {
            for file in &created_files {
                let _ = fs::remove_file(file);
            }
            return Err(format!("FFmpeg split failed at segment {}", index + 1).into());
        }
        
        created_files.push(segment_path);
        current_start = current_end;
        index += 1;
    }
    
    println!();
    success(&format!("Video split into {} segments!", created_files.len()));
    println!("  Output directory: {}", output_dir.display());
    println!("  Segments:");
    
    let mut total_size = 0u64;
    for (i, file) in created_files.iter().enumerate() {
        if let Ok(meta) = fs::metadata(file) {
            let size = meta.len();
            total_size += size;
            println!("    {:04}: {} ({:.2} MB)", 
                i + 1, 
                file.file_name().unwrap_or_default().to_string_lossy(),
                size as f64 / 1024.0 / 1024.0);
        }
    }
    println!("  Total size: {:.2} MB", total_size as f64 / 1024.0 / 1024.0);
    
    Ok(())
}
