use std::path::PathBuf;

fn usage() -> &'static str {
    "Usage: cir_materialize --cir <path> --repo-bytes-db <path> --mirror-root <path> [--keep-db]"
}

fn main() {
    if let Err(err) = run(std::env::args().collect()) {
        eprintln!("error: {err}");
        std::process::exit(2);
    }
}

fn run(argv: Vec<String>) -> Result<(), String> {
    let mut cir_path: Option<PathBuf> = None;
    let mut repo_bytes_db: Option<PathBuf> = None;
    let mut mirror_root: Option<PathBuf> = None;
    let mut keep_db = false;

    let mut i = 1usize;
    while i < argv.len() {
        match argv[i].as_str() {
            "--help" | "-h" => return Err(usage().to_string()),
            "--cir" => {
                i += 1;
                cir_path = Some(PathBuf::from(argv.get(i).ok_or("--cir requires a value")?));
            }
            "--repo-bytes-db" => {
                i += 1;
                repo_bytes_db = Some(PathBuf::from(
                    argv.get(i).ok_or("--repo-bytes-db requires a value")?,
                ));
            }
            "--mirror-root" => {
                i += 1;
                mirror_root = Some(PathBuf::from(
                    argv.get(i).ok_or("--mirror-root requires a value")?,
                ));
            }
            "--keep-db" => keep_db = true,
            other => return Err(format!("unknown argument: {other}\n\n{}", usage())),
        }
        i += 1;
    }

    let cir_path = cir_path.ok_or_else(|| format!("--cir is required\n\n{}", usage()))?;
    let repo_bytes_db =
        repo_bytes_db.ok_or_else(|| format!("--repo-bytes-db is required\n\n{}", usage()))?;
    let mirror_root =
        mirror_root.ok_or_else(|| format!("--mirror-root is required\n\n{}", usage()))?;

    let bytes = std::fs::read(&cir_path)
        .map_err(|e| format!("read CIR failed: {}: {e}", cir_path.display()))?;
    let cir = rpki::cir::decode_cir(&bytes).map_err(|e| e.to_string())?;

    let result =
        rpki::cir::materialize_cir_from_repo_bytes(&cir, &repo_bytes_db, &mirror_root, true)
            .map_err(|e| e.to_string());
    match result {
        Ok(summary) => {
            eprintln!(
                "materialized CIR: mirror={} objects={} trust_anchors={} materialized_files={} linked={} copied={} keep_db={}",
                mirror_root.display(),
                summary.object_count,
                summary.trust_anchor_count,
                summary.materialized_file_count,
                summary.linked_files,
                summary.copied_files,
                keep_db
            );
            Ok(())
        }
        Err(err) => {
            if !keep_db && mirror_root.exists() {
                let _ = std::fs::remove_dir_all(&mirror_root);
            }
            Err(err.to_string())
        }
    }
}
