//! Shannon entropy and charset variety calculations for secret detection.

/// Calculate Shannon entropy of a string.
///
/// Higher entropy indicates more randomness, typical of secrets.
/// - UUIDs: ~3.8 bits
/// - AWS keys: ~5.0+ bits
/// - Random base64: ~5.5+ bits
pub fn shannon_entropy(s: &str) -> f32 {
    if s.is_empty() {
        return 0.0;
    }

    let mut freq = [0u32; 256];
    for b in s.bytes() {
        freq[b as usize] += 1;
    }

    let len = s.len() as f32;
    freq.iter()
        .filter(|&&c| c > 0)
        .map(|&c| {
            let p = c as f32 / len;
            -p * p.log2()
        })
        .sum()
}

/// Calculate charset variety (unique chars / total chars).
///
/// Secrets typically have high variety (0.4+), while UUIDs are lower (~0.25).
pub fn charset_variety(s: &str) -> f32 {
    if s.is_empty() {
        return 0.0;
    }

    let mut seen = [false; 256];
    let mut unique = 0u32;

    for b in s.bytes() {
        if !seen[b as usize] {
            seen[b as usize] = true;
            unique += 1;
        }
    }

    unique as f32 / s.len() as f32
}

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

    #[test]
    fn test_shannon_entropy_calculation() {
        // All same character - entropy = 0
        assert!(shannon_entropy("aaaaaaaaaa") < 0.1);

        // Alternating two characters - entropy ~1.0
        let entropy_two = shannon_entropy("ababababab");
        assert!(entropy_two > 0.9 && entropy_two < 1.1);

        // High entropy random-looking string
        let entropy_high = shannon_entropy("xK9mN2pQ7rS4tU8vW3xY6zA5bC1dE0fG");
        assert!(entropy_high > 4.0);
    }

    #[test]
    fn test_charset_variety_calculation() {
        // All same character
        assert!(charset_variety("aaaaaaaaaa") < 0.2);

        // High variety
        let variety = charset_variety("abcdefghij");
        assert!((variety - 1.0).abs() < 0.01);
    }
}
