use std::path::PathBuf;

#[derive(Debug, Default, PartialEq, Eq)]
struct Args {
    cir_path: Option<PathBuf>,
    limit: usize,
}

fn usage() -> &'static str {
    "Usage: cir_dump_reject_list --cir <path> [--limit <n>]"
}

fn parse_args(argv: &[String]) -> Result<Args, String> {
    let mut args = Args {
        cir_path: None,
        limit: 10,
    };
    let mut index = 1usize;
    while index < argv.len() {
        match argv[index].as_str() {
            "--cir" => {
                index += 1;
                let value = argv.get(index).ok_or("--cir requires a value")?;
                args.cir_path = Some(PathBuf::from(value));
            }
            "--limit" => {
                index += 1;
                let value = argv.get(index).ok_or("--limit requires a value")?;
                args.limit = value
                    .parse::<usize>()
                    .map_err(|_| format!("invalid --limit: {value}"))?;
            }
            "-h" | "--help" => return Err(usage().to_string()),
            other => return Err(format!("unknown argument: {other}\n{}", usage())),
        }
        index += 1;
    }
    if args.cir_path.is_none() {
        return Err(format!("--cir is required\n{}", usage()));
    }
    Ok(args)
}

fn main() {
    if let Err(err) = real_main() {
        eprintln!("{err}");
        std::process::exit(1);
    }
}

fn real_main() -> Result<(), String> {
    let argv: Vec<String> = std::env::args().collect();
    let args = parse_args(&argv)?;
    let cir_path = args.cir_path.expect("validated");
    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| format!("decode cir failed: {e}"))?;

    println!("version={}", cir.version);
    println!("object_count={}", cir.validated_object_count());
    println!("fresh_object_count={}", cir.fresh_validated_objects.len());
    println!("cached_object_count={}", cir.cached_validated_objects.len());
    println!(
        "object_list_sha256={}",
        hex::encode(&cir.object_list_sha256)
    );
    println!(
        "fresh_object_list_sha256={}",
        hex::encode(&cir.fresh_object_list_sha256)
    );
    println!(
        "cached_object_list_sha256={}",
        hex::encode(&cir.cached_object_list_sha256)
    );
    println!("trust_anchor_count={}", cir.trust_anchors.len());
    for (index, item) in cir.validated_objects().take(args.limit).enumerate() {
        println!(
            "{:04} object={} sha256={}",
            index + 1,
            item.rsync_uri,
            hex::encode(&item.sha256)
        );
    }
    println!(
        "reject_list_sha256={}",
        hex::encode(&cir.reject_list_sha256)
    );
    println!("reject_count={}", cir.rejected_object_count());
    println!("fresh_reject_count={}", cir.fresh_rejected_objects.len());
    println!("cached_reject_count={}", cir.cached_rejected_objects.len());
    println!(
        "fresh_reject_list_sha256={}",
        hex::encode(&cir.fresh_reject_list_sha256)
    );
    println!(
        "cached_reject_list_sha256={}",
        hex::encode(&cir.cached_reject_list_sha256)
    );
    for (index, item) in cir.rejected_objects().take(args.limit).enumerate() {
        println!(
            "{:04} uri={} reason={}",
            index + 1,
            item.object_uri,
            item.reason.as_deref().unwrap_or("<none>")
        );
    }
    Ok(())
}

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

    #[test]
    fn parse_requires_cir_path() {
        let err = parse_args(&["cir_dump_reject_list".to_string()]).expect_err("missing cir");
        assert!(err.contains("--cir is required"), "{err}");
    }

    #[test]
    fn parse_accepts_limit() {
        let args = parse_args(&[
            "cir_dump_reject_list".to_string(),
            "--cir".to_string(),
            "input.cir".to_string(),
            "--limit".to_string(),
            "5".to_string(),
        ])
        .expect("parse args");
        assert_eq!(
            args.cir_path.as_deref(),
            Some(std::path::Path::new("input.cir"))
        );
        assert_eq!(args.limit, 5);
    }
}
