use std::io::Write;

use anyhow::{anyhow, Context, Result};

pub fn compress(data: &[u8], method: &str) -> Result<Vec<u8>> {
    match method {
        "none" | "" => Ok(data.to_vec()),
        #[cfg(feature = "lzma")]
        "lzma" => {
            let mut out = Vec::new();
            lzma_rs::lzma_compress(&mut std::io::Cursor::new(data), &mut out)
                .context("LZMA compression failed")?;
            Ok(out)
        }
        #[cfg(feature = "gzip")]
        "gzip" => {
            let mut encoder =
                flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::best());
            encoder.write_all(data).context("gzip write failed")?;
            encoder.finish().context("gzip finish failed")
        }
        #[cfg(feature = "bzip2")]
        "bzip2" => {
            let mut encoder = bzip2::write::BzEncoder::new(Vec::new(), bzip2::Compression::best());
            encoder.write_all(data).context("bzip2 write failed")?;
            encoder.finish().context("bzip2 finish failed")
        }
        other => Err(anyhow!("unsupported compression method: {other}")),
    }
}

pub fn decompress(data: &[u8], method: &str) -> Result<Vec<u8>> {
    use std::io::Read;
    match method {
        "none" | "" => Ok(data.to_vec()),
        #[cfg(feature = "lzma")]
        "lzma" => {
            let mut out = Vec::new();
            lzma_rs::lzma_decompress(&mut std::io::Cursor::new(data), &mut out)
                .context("LZMA decompression failed")?;
            Ok(out)
        }
        #[cfg(feature = "gzip")]
        "gzip" => {
            let mut out = Vec::new();
            flate2::read::GzDecoder::new(data)
                .read_to_end(&mut out)
                .context("gzip decompression failed")?;
            Ok(out)
        }
        #[cfg(feature = "bzip2")]
        "bzip2" => {
            let mut out = Vec::new();
            bzip2::read::BzDecoder::new(data)
                .read_to_end(&mut out)
                .context("bzip2 decompression failed")?;
            Ok(out)
        }
        other => Err(anyhow!("unsupported compression method: {other}")),
    }
}

pub fn validate_method(method: &str) -> Result<()> {
    match method {
        #[cfg(feature = "lzma")]
        "lzma" => Ok(()),
        #[cfg(feature = "gzip")]
        "gzip" => Ok(()),
        #[cfg(feature = "bzip2")]
        "bzip2" => Ok(()),
        "none" => Ok(()),
        other => Err(anyhow!(
            "unsupported compression method: {other} (choose lzma, gzip, bzip2, or none)"
        )),
    }
}
