//! Data units with proper binary vs decimal distinction and case-sensitive aliases.
//!
//! - Decimal (SI): KB, MB, GB, TB, PB, EB (powers of 1000)
//! - Binary (IEC): KiB, MiB, GiB, TiB, PiB (powers of 1024)
//! - Case-sensitive: "B" = byte, "b" = bit, "MB" = megabyte, "Mb" = megabit

use super::categories::UnitCategory;
use super::{Conversion, UnitDef, UnitRegistry};

/// Helper to register a linear data unit.
fn linear(
    reg: &mut UnitRegistry,
    name: &'static str,
    abbrev: &'static str,
    factor: f64,
    aliases: &[&str],
) {
    reg.register(
        UnitDef {
            name,
            abbreviation: abbrev,
            category: UnitCategory::Data,
            conversion: Conversion::Linear(factor),
        },
        aliases,
    );
}

/// Register all data units and case-sensitive aliases.
pub(crate) fn register_data(reg: &mut UnitRegistry) {
    // Base units
    linear(reg, "bit", "bit", 0.125, &["bits"]);
    linear(reg, "byte", "B", 1.0, &["bytes"]);

    // Decimal byte units (powers of 1000)
    linear(reg, "kilobyte", "KB", 1000.0, &["kilobytes"]);
    linear(reg, "megabyte", "MB", 1e6, &["megabytes"]);
    linear(reg, "gigabyte", "GB", 1e9, &["gigabytes"]);
    linear(reg, "terabyte", "TB", 1e12, &["terabytes"]);
    linear(reg, "petabyte", "PB", 1e15, &["petabytes"]);
    linear(reg, "exabyte", "EB", 1e18, &["exabytes"]);

    // Binary byte units (powers of 1024)
    linear(reg, "kibibyte", "KiB", 1024.0, &["kibibytes"]);
    linear(reg, "mebibyte", "MiB", 1_048_576.0, &["mebibytes"]);
    linear(reg, "gibibyte", "GiB", 1_073_741_824.0, &["gibibytes"]);
    linear(reg, "tebibyte", "TiB", 1_099_511_627_776.0, &["tebibytes"]);
    linear(reg, "pebibyte", "PiB", 1_125_899_906_842_624.0, &["pebibytes"]);

    // Decimal bit units (powers of 1000, in bytes: factor / 8)
    linear(reg, "kilobit", "Kbit", 125.0, &["kilobits"]);
    linear(reg, "megabit", "Mbit", 125_000.0, &["megabits"]);
    linear(reg, "gigabit", "Gbit", 125_000_000.0, &["gigabits"]);
    linear(reg, "terabit", "Tbit", 125_000_000_000.0, &["terabits"]);

    // Case-sensitive aliases: uppercase B = bytes, lowercase b = bits.
    // Without these, "MB" and "Mb" both lowercase to "mb" -> megabyte (wrong for megabit).
    reg.register_case_sensitive_alias("b", "bit");
    reg.register_case_sensitive_alias("B", "byte");

    reg.register_case_sensitive_alias("Kb", "kilobit");
    reg.register_case_sensitive_alias("Mb", "megabit");
    reg.register_case_sensitive_alias("Gb", "gigabit");
    reg.register_case_sensitive_alias("Tb", "terabit");

    reg.register_case_sensitive_alias("KB", "kilobyte");
    reg.register_case_sensitive_alias("MB", "megabyte");
    reg.register_case_sensitive_alias("GB", "gigabyte");
    reg.register_case_sensitive_alias("TB", "terabyte");
    reg.register_case_sensitive_alias("PB", "petabyte");
    reg.register_case_sensitive_alias("EB", "exabyte");

    reg.register_case_sensitive_alias("KiB", "kibibyte");
    reg.register_case_sensitive_alias("MiB", "mebibyte");
    reg.register_case_sensitive_alias("GiB", "gibibyte");
    reg.register_case_sensitive_alias("TiB", "tebibyte");
    reg.register_case_sensitive_alias("PiB", "pebibyte");
}

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

    // ─── Decimal units ──────────────────────────────────────────────────

    #[test]
    fn test_decimal_mb_to_kb() {
        let result = convert(1.0, "MB", "KB").unwrap();
        assert!((result - 1000.0).abs() < 1e-10, "Expected 1000, got {}", result);
    }

    #[test]
    fn test_decimal_gb_to_mb() {
        let result = convert(1.0, "GB", "MB").unwrap();
        assert!((result - 1000.0).abs() < 1e-10, "Expected 1000, got {}", result);
    }

    // ─── Binary units ───────────────────────────────────────────────────

    #[test]
    fn test_binary_mib_to_kib() {
        let result = convert(1.0, "MiB", "KiB").unwrap();
        assert!((result - 1024.0).abs() < 1e-10, "Expected 1024, got {}", result);
    }

    #[test]
    fn test_binary_gib_to_mib() {
        let result = convert(1.0, "GiB", "MiB").unwrap();
        assert!((result - 1024.0).abs() < 1e-10, "Expected 1024, got {}", result);
    }

    // ─── Bit/byte distinction ───────────────────────────────────────────

    #[test]
    fn test_byte_to_bits() {
        let result = convert(1.0, "byte", "bits").unwrap();
        assert!((result - 8.0).abs() < 1e-10, "Expected 8, got {}", result);
    }

    #[test]
    fn test_megabytes_to_megabits_case_sensitive() {
        let result = convert(1.0, "MB", "Mb").unwrap();
        assert!((result - 8.0).abs() < 1e-10, "Expected 8, got {}", result);
    }

    #[test]
    fn test_kilobytes_to_kilobits() {
        let result = convert(1.0, "KB", "Kb").unwrap();
        assert!((result - 8.0).abs() < 1e-10, "Expected 8, got {}", result);
    }

    // ─── Cross-conversion (binary <-> decimal) ─────────────────────────

    #[test]
    fn test_cross_convert_gib_to_gb() {
        let result = convert(5.0, "GiB", "GB").unwrap();
        let expected = 5.0 * 1_073_741_824.0 / 1e9;
        assert!((result - expected).abs() < 1e-6, "Expected {}, got {}", expected, result);
    }

    #[test]
    fn test_cross_convert_tb_to_tib() {
        let result = convert(1.0, "TB", "TiB").unwrap();
        let expected = 1e12 / 1_099_511_627_776.0;
        assert!((result - expected).abs() < 1e-6, "Expected {}, got {}", expected, result);
    }

    // ─── Case-sensitive lookups ─────────────────────────────────────────

    #[test]
    fn test_case_sensitive_b_vs_big_b() {
        let reg = super::super::registry();
        let big_b = reg.lookup("B").unwrap();
        assert_eq!(big_b.name, "byte");
        let small_b = reg.lookup("b").unwrap();
        assert_eq!(small_b.name, "bit");
    }

    #[test]
    fn test_case_sensitive_mb_vs_big_mb() {
        let reg = super::super::registry();
        let big_mb = reg.lookup("MB").unwrap();
        assert_eq!(big_mb.name, "megabyte");
        let small_mb = reg.lookup("Mb").unwrap();
        assert_eq!(small_mb.name, "megabit");
    }

    // ─── Edge cases ─────────────────────────────────────────────────────

    #[test]
    fn test_zero_data_conversion() {
        let result = convert(0.0, "MB", "KB").unwrap();
        assert!(result.abs() < 1e-10, "Expected 0, got {}", result);
    }
}
