use clap::Parser;
use std::path::PathBuf;

#[derive(Parser, Debug)]
#[command(name = "rasa", version, about = "Plugin-based disk space cleaner")]
pub struct Cli {
    /// Directories to scan (default: home directory)
    #[arg()]
    pub paths: Vec<PathBuf>,

    /// Minimum target size to report (e.g. 50MB, 1GiB)
    #[arg(short = 'm', long = "min-size", default_value = "100MB")]
    pub min_size: String,

    /// Only run these plugins (comma-separated)
    #[arg(long = "only", value_delimiter = ',')]
    pub only: Vec<String>,

    /// Skip these plugins (comma-separated)
    #[arg(long = "skip", value_delimiter = ',')]
    pub skip: Vec<String>,

    /// Exclude these paths from scanning (comma-separated)
    #[arg(long = "exclude", value_delimiter = ',')]
    pub exclude: Vec<PathBuf>,

    /// Skip global targets, only scan project directories
    #[arg(long = "no-global")]
    pub no_global: bool,

    /// Maximum directory depth to scan (default: 8)
    #[arg(short = 'd', long = "max-depth", default_value = "8")]
    pub max_depth: usize,

    /// Ignore cache and force a full rescan
    #[arg(long = "rescan")]
    pub rescan: bool,

    /// Output targets as JSON lines (one per line) instead of interactive TUI
    #[arg(long = "json")]
    pub json: bool,
}

pub fn parse_size(s: &str) -> Option<u64> {
    let s = s.trim();
    let (num_str, unit) = s
        .find(|c: char| !c.is_ascii_digit() && c != '.')
        .map(|i| s.split_at(i))
        .unwrap_or((s, ""));

    let num: f64 = num_str.parse().ok()?;
    let multiplier: u64 = match unit.to_uppercase().as_str() {
        "B" | "" => 1,
        "KB" | "K" => 1_000,
        "KIB" => 1_024,
        "MB" | "M" => 1_000_000,
        "MIB" => 1_048_576,
        "GB" | "G" => 1_000_000_000,
        "GIB" => 1_073_741_824,
        "TB" | "T" => 1_000_000_000_000,
        "TIB" => 1_099_511_627_776,
        _ => return None,
    };

    Some((num * multiplier as f64) as u64)
}

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

    #[test]
    fn parse_bytes() {
        assert_eq!(parse_size("100B"), Some(100));
        assert_eq!(parse_size("100"), Some(100));
    }

    #[test]
    fn parse_kilobytes() {
        assert_eq!(parse_size("1KB"), Some(1_000));
        assert_eq!(parse_size("1K"), Some(1_000));
        assert_eq!(parse_size("1KiB"), Some(1_024));
    }

    #[test]
    fn parse_megabytes() {
        assert_eq!(parse_size("100MB"), Some(100_000_000));
        assert_eq!(parse_size("100M"), Some(100_000_000));
        assert_eq!(parse_size("1MiB"), Some(1_048_576));
    }

    #[test]
    fn parse_gigabytes() {
        assert_eq!(parse_size("1GB"), Some(1_000_000_000));
        assert_eq!(parse_size("1G"), Some(1_000_000_000));
        assert_eq!(parse_size("1GiB"), Some(1_073_741_824));
    }

    #[test]
    fn parse_terabytes() {
        assert_eq!(parse_size("1TB"), Some(1_000_000_000_000));
        assert_eq!(parse_size("1TiB"), Some(1_099_511_627_776));
    }

    #[test]
    fn parse_fractional() {
        assert_eq!(parse_size("1.5GB"), Some(1_500_000_000));
        assert_eq!(parse_size("0.5MB"), Some(500_000));
    }

    #[test]
    fn parse_with_whitespace() {
        assert_eq!(parse_size("  100MB  "), Some(100_000_000));
    }

    #[test]
    fn parse_invalid() {
        assert_eq!(parse_size("abc"), None);
        assert_eq!(parse_size("100XB"), None);
    }
}
