use clap::{Parser, Subcommand};
use daemonize::Daemonize;
use nix::sys::signal::{self, Signal};
use nix::unistd::Pid;
use rodio::{Decoder, OutputStream, Sink};
use std::fs;
use std::io::{BufReader, Cursor};
use std::path::PathBuf;
use std::process::{Command, exit};
use std::thread;
use std::time::Duration;

const CHIME_SOUND: &[u8] = include_bytes!("../assets/chime.wav");

#[derive(Parser)]
#[command(name = "timesup")]
#[command(about = "A simple timer CLI that sends notifications")]
struct Cli {
    #[command(subcommand)]
    command: Option<Commands>,

    /// Time duration (e.g., 1h20m5s, 5s, 20m5s)
    #[arg(value_parser = parse_duration)]
    duration: Option<Duration>,

    /// Repeat the timer indefinitely
    #[arg(long, default_value_t = false)]
    repeat: bool,

    /// Custom notification message
    #[arg(long)]
    msg: Option<String>,

    /// Mute alarm sound
    #[arg(long, default_value_t = false)]
    mute: bool,

    /// Custom sound file path (supports WAV, MP3, FLAC, OGG)
    #[arg(long, value_name = "PATH")]
    sound: Option<String>,
}

#[derive(Subcommand)]
enum Commands {
    /// Kill any running timer
    Kill,
    /// Show status of current timer
    Status,
}

fn parse_duration(s: &str) -> Result<Duration, String> {
    let mut total_secs = 0u64;
    let mut current_num = String::new();

    for c in s.chars() {
        if c.is_ascii_digit() {
            current_num.push(c);
        } else if !current_num.is_empty() {
            let num: u64 = current_num.parse().map_err(|_| "Invalid number")?;
            match c {
                'h' => total_secs += num * 3600,
                'm' => total_secs += num * 60,
                's' => total_secs += num,
                _ => return Err(format!("Invalid time unit: {}", c)),
            }
            current_num.clear();
        } else {
            return Err(format!("Invalid format at character: {}", c));
        }
    }

    if !current_num.is_empty() {
        return Err("Number without unit".to_string());
    }

    if total_secs == 0 {
        return Err("Duration must be greater than 0".to_string());
    }

    Ok(Duration::from_secs(total_secs))
}

fn get_pid_file() -> PathBuf {
    let mut path = std::env::temp_dir();
    path.push("timesup.pid");
    path
}

fn get_status_file() -> PathBuf {
    let mut path = std::env::temp_dir();
    path.push("timesup.status");
    path
}

fn kill_existing_timer() -> Result<(), String> {
    let pid_file = get_pid_file();

    if !pid_file.exists() {
        return Err("No running timer found".to_string());
    }

    let pid_str =
        fs::read_to_string(&pid_file).map_err(|e| format!("Failed to read PID file: {}", e))?;

    let pid: i32 = pid_str.trim().parse().map_err(|_| "Invalid PID in file")?;

    match signal::kill(Pid::from_raw(pid), Signal::SIGTERM) {
        Ok(_) => {
            fs::remove_file(&pid_file).ok();
            fs::remove_file(get_status_file()).ok();
            send_warning_notification("Timer killed!");
            println!("Timer killed successfully");
            Ok(())
        }
        Err(_) => {
            fs::remove_file(&pid_file).ok();
            fs::remove_file(get_status_file()).ok();
            Err("Timer process not found (cleaned up stale PID file)".to_string())
        }
    }
}

fn show_timer_status() -> Result<(), String> {
    let pid_file = get_pid_file();
    let status_file = get_status_file();

    if !pid_file.exists() {
        return Err("No timer currently running".to_string());
    }

    // Check if process is actually running
    let pid_str = fs::read_to_string(&pid_file).ok();
    if let Some(pid_str) = pid_str
        && let Ok(pid) = pid_str.trim().parse::<i32>()
        && signal::kill(Pid::from_raw(pid), None).is_err()
    {
        fs::remove_file(&pid_file).ok();
        fs::remove_file(&status_file).ok();
        return Err("No timer currently running (cleaned up stale files)".to_string());
    }

    // Read status information
    if let Ok(status_content) = fs::read_to_string(&status_file) {
        let lines: Vec<&str> = status_content.lines().collect();
        if lines.len() >= 4 {
            let start_time: i64 = lines[0].parse().unwrap_or(0);
            let duration_secs: u64 = lines[1].parse().unwrap_or(0);
            let repeat = lines[2] == "true";
            let message = lines[3];

            let now = std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_secs() as i64;
            
            let elapsed = (now - start_time).max(0) as u64;
            let remaining = duration_secs.saturating_sub(elapsed);

            println!("⏱️  Timer Status:");
            println!("   Duration: {}s ({})", duration_secs, format_duration(duration_secs));
            println!("   Elapsed: {}s ({})", elapsed, format_duration(elapsed));
            println!("   Remaining: {}s ({})", remaining, format_duration(remaining));
            println!("   Repeat: {}", if repeat { "yes" } else { "no" });
            println!("   Message: {}", message);
            
            Ok(())
        } else {
            Err("Invalid status file format".to_string())
        }
    } else {
        Err("Timer is running but status information unavailable".to_string())
    }
}

fn format_duration(secs: u64) -> String {
    let hours = secs / 3600;
    let minutes = (secs % 3600) / 60;
    let seconds = secs % 60;

    if hours > 0 {
        format!("{}h {}m {}s", hours, minutes, seconds)
    } else if minutes > 0 {
        format!("{}m {}s", minutes, seconds)
    } else {
        format!("{}s", seconds)
    }
}

fn check_existing_timer() -> Result<(), String> {
    let pid_file = get_pid_file();

    if pid_file.exists() {
        let pid_str = fs::read_to_string(&pid_file).ok();
        if let Some(pid_str) = pid_str
            && let Ok(pid) = pid_str.trim().parse::<i32>()
            && signal::kill(Pid::from_raw(pid), None).is_ok()
        {
            return Err("A timer is already running. Use 'timesup kill' to stop it.".to_string());
        }
        fs::remove_file(&pid_file).ok();
    }

    Ok(())
}

fn send_warning_notification(message: &str) {
    Command::new("notify-send")
        .arg("-u")
        .arg("critical")
        .arg("TimesUp")
        .arg(message)
        .output()
        .ok();
}

fn play_sound(custom_path: Option<&str>) {
    if let Ok((_stream, stream_handle)) = OutputStream::try_default()
        && let Ok(sink) = Sink::try_new(&stream_handle)
    {
        if let Some(path) = custom_path {
            // Try to load custom sound file
            if let Ok(file) = fs::File::open(path) {
                let buf_reader = BufReader::new(file);
                if let Ok(source) = Decoder::new(buf_reader) {
                    sink.append(source);
                    sink.sleep_until_end();
                }
            }
        } else {
            // Use embedded sound
            let cursor = Cursor::new(CHIME_SOUND);
            if let Ok(source) = Decoder::new(cursor) {
                sink.append(source);
                sink.sleep_until_end();
            }
        }
    }
}

fn run_timer(duration: Duration, repeat: bool, message: String, mute: bool, sound_path: Option<String>) {
    let pid_file = get_pid_file();
    let status_file = get_status_file();
    let pid = std::process::id();
    fs::write(&pid_file, pid.to_string()).ok();

    // Write status information
    let start_time = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_secs();
    
    let status_content = format!(
        "{}\n{}\n{}\n{}",
        start_time,
        duration.as_secs(),
        repeat,
        message
    );
    fs::write(&status_file, status_content).ok();

    loop {
        thread::sleep(duration);

        if !mute {
            play_sound(sound_path.as_deref());
        }

        let output = Command::new("notify-send")
            .arg("-A")
            .arg("kill=Kill Timer")
            .arg("TimesUp")
            .arg(&message)
            .output();

        if let Ok(output) = output
            && let Ok(response) = String::from_utf8(output.stdout)
            && response.trim() == "kill"
        {
            send_warning_notification("Timer killed!");
            break;
        }

        if !repeat {
            break;
        }
        
        // Update start time for next iteration
        let start_time = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();
        
        let status_content = format!(
            "{}\n{}\n{}\n{}",
            start_time,
            duration.as_secs(),
            repeat,
            message
        );
        fs::write(&status_file, status_content).ok();
    }

    fs::remove_file(&pid_file).ok();
    fs::remove_file(&status_file).ok();
}

fn main() {
    let cli = Cli::parse();

    match cli.command {
        Some(Commands::Kill) => {
            match kill_existing_timer() {
                Ok(_) => exit(0),
                Err(e) => {
                    eprintln!("Error: {}", e);
                    exit(1);
                }
            }
        }
        Some(Commands::Status) => {
            match show_timer_status() {
                Ok(_) => exit(0),
                Err(e) => {
                    eprintln!("Error: {}", e);
                    exit(1);
                }
            }
        }
        None => {}
    }

    let duration = match cli.duration {
        Some(d) => d,
        None => {
            eprintln!("Error: Duration is required (e.g., 1h20m5s, 5s, 20m5s)");
            exit(1);
        }
    };

    if let Err(e) = check_existing_timer() {
        eprintln!("Error: {}", e);
        exit(1);
    }

    let stdout = fs::File::create("/tmp/timesup.out").ok();
    let stderr = fs::File::create("/tmp/timesup.err").ok();

    let daemonize = Daemonize::new()
        .pid_file(get_pid_file())
        .stdout(stdout.unwrap())
        .stderr(stderr.unwrap());

    let message = cli.msg.unwrap_or_else(|| "Timer finished!".to_string());

    match daemonize.start() {
        Ok(_) => {
            run_timer(duration, cli.repeat, message, cli.mute, cli.sound);
        }
        Err(e) => {
            eprintln!("Failed to daemonize: {}", e);
            exit(1);
        }
    }
}
