use std::path::PathBuf;
use bch_bindgen::c;
use bch_bindgen::opt_set;
use anyhow::bail;
use crate::wrappers::bch_err_str;

fn strip_alloc_usage() {
    println!("bcachefs strip-alloc - remove alloc info and journal from a filesystem");
    println!("Usage: bcachefs strip-alloc [OPTION]... <devices>\n");
    println!("Removes metadata unneeded for running in read-only mode");
    println!("Alloc info and journal will be recreated on first RW mount\n");
    println!("Options:");
    println!("  -h, --help              Display this help and exit\n");
    println!("Report bugs to <linux-bcachefs@vger.kernel.org>");
}

pub fn cmd_strip_alloc(argv: Vec<String>) -> anyhow::Result<()> {
    let mut devs: Vec<PathBuf> = Vec::new();

    for arg in argv.iter().skip(1) { // skip "strip-alloc"
        match arg.as_str() {
            "-h" | "--help" => {
                strip_alloc_usage();
                return Ok(());
            }
            s if s.starts_with('-') => {
                bail!("Unknown option: {}", s);
            }
            _ => {
                devs.push(PathBuf::from(arg));
            }
        }
    }

    if devs.is_empty() {
        strip_alloc_usage();
        bail!("Please supply device(s)");
    }

    loop {
        let mut opts: c::bch_opts = Default::default();
        opt_set!(opts, nostart, 1);

        let fs = crate::device_scan::open_scan(&devs, opts)
            .map_err(|e| anyhow::anyhow!("Error opening filesystem: {}", e))?;

        if unsafe { (*fs.raw).sb.clean } == 0 {
            println!("Filesystem not clean, running recovery");
            let ret = unsafe { c::bch2_fs_start(fs.raw) };
            if ret != 0 {
                bail!("Error starting filesystem: {}", bch_err_str(ret));
            }
            drop(fs);
            continue;
        }

        // Check total capacity — reconstruction is limited to ~1TB
        let mut capacity: u64 = 0;
        for dev in 0..fs.nr_devices() {
            if let Some(ca) = fs.dev_get(dev) {
                capacity += (ca.mi.nbuckets * ca.mi.bucket_size as u64) << 9;
            }
        }
        if capacity > 1u64 << 40 {
            bail!("capacity too large for alloc info reconstruction");
        }

        println!("Stripping alloc info from {}", devs[0].display());
        unsafe { c::rust_strip_alloc_do(fs.raw) };
        return Ok(());
    }
}
