use flate2::read::GzDecoder;
use flate2::{Compression, GzBuilder};
use std::io::{Read, Write};

/// Gzip-compress data with reproducible output (mtime zeroed).
///
/// Uses `GzBuilder::new().mtime(0)` to zero the gzip timestamp,
/// ensuring reproducible compressed output for testing.
pub fn compress(data: &[u8]) -> anyhow::Result<Vec<u8>> {
    let mut encoder = GzBuilder::new()
        .mtime(0)
        .write(Vec::new(), Compression::default());
    encoder.write_all(data)?;
    let compressed = encoder.finish()?;
    Ok(compressed)
}

/// Gzip-decompress data.
pub fn decompress(data: &[u8]) -> anyhow::Result<Vec<u8>> {
    let mut decoder = GzDecoder::new(data);
    let mut decompressed = Vec::new();
    decoder.read_to_end(&mut decompressed)?;
    Ok(decompressed)
}

/// Determine if a file should be compressed based on filename and exclusion list.
///
/// Returns false for:
/// - Files matching any entry in `no_compress_list` (by suffix or exact match)
/// - Files with known compressed extensions (apk, zip, gz, etc.)
///
/// Returns true otherwise.
pub fn should_compress(filename: &str, no_compress_list: &[String]) -> bool {
    // Check explicit exclusion list
    if no_compress_list
        .iter()
        .any(|nc| filename.ends_with(nc) || filename == nc)
    {
        return false;
    }

    // Check known compressed extensions
    let ext = filename.rsplit('.').next().unwrap_or("").to_lowercase();
    !matches!(
        ext.as_str(),
        "apk" | "zip" | "gz" | "bz2" | "xz" | "zst"
            | "png" | "jpg" | "jpeg" | "gif" | "webp"
            | "mp4" | "mp3" | "aac" | "ogg" | "flac"
            | "7z" | "rar" | "jar"
    )
}

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

    #[test]
    fn test_compress_decompress_roundtrip() {
        let data = b"Hello, World! This is test data for compression.";
        let compressed = compress(data).unwrap();
        let decompressed = decompress(&compressed).unwrap();
        assert_eq!(decompressed, data);
    }

    #[test]
    fn test_compress_decompress_empty() {
        let data = b"";
        let compressed = compress(data).unwrap();
        let decompressed = decompress(&compressed).unwrap();
        assert_eq!(decompressed, data.as_slice());
    }

    #[test]
    fn test_compress_decompress_large() {
        // 10000 bytes of pattern data
        let data: Vec<u8> = (0..10000).map(|i| (i % 256) as u8).collect();
        let compressed = compress(&data).unwrap();
        let decompressed = decompress(&compressed).unwrap();
        assert_eq!(decompressed, data);
    }

    #[test]
    fn test_should_compress_text() {
        assert!(should_compress("readme.txt", &[]));
        assert!(should_compress("data.json", &[]));
    }

    #[test]
    fn test_should_not_compress_known_extensions() {
        assert!(!should_compress("app.apk", &[]));
        assert!(!should_compress("photo.jpg", &[]));
        assert!(!should_compress("archive.zip", &[]));
    }

    #[test]
    fn test_should_not_compress_excluded() {
        assert!(!should_compress("special.dat", &["special.dat".into()]));
    }
}
