//! Formatting helpers for display values.

use ratatui::style::Color;

/// Format a duration since a timestamp as a human-readable string.
pub fn format_duration(started_at: chrono::DateTime<chrono::Utc>) -> String {
    let duration = chrono::Utc::now() - started_at;
    let secs = duration.num_seconds();

    if secs < 60 {
        format!("{secs}s")
    } else if secs < 3600 {
        format!("{}m", secs / 60)
    } else if secs < 86400 {
        format!("{}h", secs / 3600)
    } else {
        format!("{}d", secs / 86400)
    }
}

/// Format bytes as a human-readable string (B, KB, MB, GB).
pub fn format_bytes(bytes: u64) -> String {
    if bytes < 1024 {
        format!("{bytes}B")
    } else if bytes < 1024 * 1024 {
        format!("{:.1}KB", bytes as f64 / 1024.0)
    } else if bytes < 1024 * 1024 * 1024 {
        format!("{:.1}MB", bytes as f64 / (1024.0 * 1024.0))
    } else {
        format!("{:.1}GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
    }
}

/// Format a number with K/M suffixes for large values.
pub fn format_number(n: i64) -> String {
    if n < 1000 {
        format!("{n}")
    } else if n < 1_000_000 {
        format!("{:.1}K", n as f64 / 1000.0)
    } else {
        format!("{:.1}M", n as f64 / 1_000_000.0)
    }
}

/// Truncate a string and append ellipsis if truncated.
/// The ellipsis counts toward max_len, so content is max_len - 1 chars + "…".
pub fn truncate_with_ellipsis(s: &str, max_len: usize) -> String {
    let char_count = s.chars().count();
    if char_count <= max_len {
        s.to_string()
    } else if max_len <= 1 {
        "…".to_string()
    } else {
        let truncated: String = s.chars().take(max_len - 1).collect();
        format!("{truncated}…")
    }
}

/// Return a color based on CPU usage percentage.
pub fn cpu_color(cpu: f32) -> Color {
    if cpu < 25.0 {
        Color::Green
    } else if cpu < 50.0 {
        Color::Yellow
    } else if cpu < 75.0 {
        Color::Rgb(255, 165, 0) // Orange
    } else {
        Color::Magenta // High CPU - use magenta instead of red (red = dead)
    }
}

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

    #[test]
    fn test_format_bytes() {
        assert_eq!(format_bytes(0), "0B");
        assert_eq!(format_bytes(512), "512B");
        assert_eq!(format_bytes(1024), "1.0KB");
        assert_eq!(format_bytes(1536), "1.5KB");
        assert_eq!(format_bytes(1024 * 1024), "1.0MB");
        assert_eq!(format_bytes(1024 * 1024 * 1024), "1.0GB");
    }

    #[test]
    fn test_format_number() {
        assert_eq!(format_number(0), "0");
        assert_eq!(format_number(999), "999");
        assert_eq!(format_number(1000), "1.0K");
        assert_eq!(format_number(1500), "1.5K");
        assert_eq!(format_number(1_000_000), "1.0M");
    }

    #[test]
    fn test_truncate_with_ellipsis() {
        assert_eq!(truncate_with_ellipsis("hello", 10), "hello");
        assert_eq!(truncate_with_ellipsis("hello", 5), "hello");
        assert_eq!(truncate_with_ellipsis("hello", 4), "hel…");
        assert_eq!(truncate_with_ellipsis("hello", 1), "…");
        // Unicode: char-based, not byte-based
        assert_eq!(truncate_with_ellipsis("héllo", 4), "hél…");
    }

    #[test]
    fn test_cpu_color() {
        assert_eq!(cpu_color(0.0), Color::Green);
        assert_eq!(cpu_color(24.9), Color::Green);
        assert_eq!(cpu_color(25.0), Color::Yellow);
        assert_eq!(cpu_color(49.9), Color::Yellow);
        assert_eq!(cpu_color(50.0), Color::Rgb(255, 165, 0));
        assert_eq!(cpu_color(75.0), Color::Magenta);
    }
}
