use std::ffi::OsStr;

use crate::constants::{SKIP_DIR_NAMES, SKIP_FILE_NAMES};

pub fn friendly_size(size: u64) -> String {
    const UNITS: [(&str, u64); 5] = [
        ("B", 1),
        ("KB", 1024),
        ("MB", 1024 * 1024),
        ("GB", 1024 * 1024 * 1024),
        ("TB", 1024 * 1024 * 1024 * 1024),
    ];

    if size == 0 {
        return "0B".to_string();
    }

    let mut value = size as f64;
    let mut unit = "B";
    for (label, threshold) in UNITS.iter().rev() {
        if size >= *threshold {
            value = size as f64 / *threshold as f64;
            unit = label;
            break;
        }
    }

    format!("{:.2}{}", value, unit)
}

pub fn basename(path: &OsStr) -> String {
    path.to_string_lossy().to_string()
}

pub fn should_skip_dir(name: &str) -> bool {
    SKIP_DIR_NAMES
        .iter()
        .any(|item| item.eq_ignore_ascii_case(name))
}

pub fn should_skip_file(name: &str) -> bool {
    SKIP_FILE_NAMES
        .iter()
        .any(|item| item.eq_ignore_ascii_case(name))
        || name.starts_with("._")
        || name.starts_with("Thumb_")
}

pub fn hex_upper(bytes: impl AsRef<[u8]>) -> String {
    bytes
        .as_ref()
        .iter()
        .map(|b| format!("{:02X}", b))
        .collect::<String>()
}

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

    #[test]
    fn friendly_size_formats_units() {
        assert_eq!(friendly_size(0), "0B");
        assert_eq!(friendly_size(1), "1.00B");
        assert_eq!(friendly_size(1024), "1.00KB");
        assert_eq!(friendly_size(1024 * 1024), "1.00MB");
    }
}
