use rpki::ccr::{
    decode_content_info, verify::verify_content_info, verify_against_report_json_path,
    verify_against_vcir_store_path,
};

#[derive(Debug, Default, PartialEq, Eq)]
struct Args {
    ccr_path: Option<std::path::PathBuf>,
    report_json: Option<std::path::PathBuf>,
    db_path: Option<std::path::PathBuf>,
}

fn usage() -> &'static str {
    "Usage: ccr_verify --ccr <path> [--report-json <path>] [--db <path>]"
}

fn parse_args(argv: &[String]) -> Result<Args, String> {
    let mut args = Args::default();
    let mut i = 1usize;
    while i < argv.len() {
        match argv[i].as_str() {
            "--help" | "-h" => return Err(usage().to_string()),
            "--ccr" => {
                i += 1;
                let v = argv.get(i).ok_or("--ccr requires a value")?;
                args.ccr_path = Some(v.into());
            }
            "--report-json" => {
                i += 1;
                let v = argv.get(i).ok_or("--report-json requires a value")?;
                args.report_json = Some(v.into());
            }
            "--db" => {
                i += 1;
                let v = argv.get(i).ok_or("--db requires a value")?;
                args.db_path = Some(v.into());
            }
            other => return Err(format!("unknown argument: {other}\n{}", usage())),
        }
        i += 1;
    }
    if args.ccr_path.is_none() {
        return Err(format!("--ccr is required\n{}", usage()));
    }
    Ok(args)
}

fn main() -> Result<(), String> {
    let args = parse_args(&std::env::args().collect::<Vec<_>>())?;
    let ccr_path = args.ccr_path.as_ref().unwrap();
    let bytes = std::fs::read(ccr_path)
        .map_err(|e| format!("read ccr failed: {}: {e}", ccr_path.display()))?;
    let ci = decode_content_info(&bytes).map_err(|e| e.to_string())?;
    let summary = verify_content_info(&ci).map_err(|e| e.to_string())?;
    if let Some(report_json) = args.report_json.as_ref() {
        verify_against_report_json_path(&ci, report_json).map_err(|e| e.to_string())?;
    }
    if let Some(db_path) = args.db_path.as_ref() {
        verify_against_vcir_store_path(&ci, db_path).map_err(|e| e.to_string())?;
    }
    println!(
        "{}",
        serde_json::to_string_pretty(&summary).map_err(|e| e.to_string())?
    );
    Ok(())
}

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

    #[test]
    fn parse_args_accepts_all_flags() {
        let argv = vec![
            "ccr_verify".to_string(),
            "--ccr".to_string(),
            "a.ccr".to_string(),
            "--report-json".to_string(),
            "report.json".to_string(),
            "--db".to_string(),
            "db".to_string(),
        ];
        let args = parse_args(&argv).expect("parse");
        assert_eq!(
            args.ccr_path.as_deref(),
            Some(std::path::Path::new("a.ccr"))
        );
        assert_eq!(
            args.report_json.as_deref(),
            Some(std::path::Path::new("report.json"))
        );
        assert_eq!(args.db_path.as_deref(), Some(std::path::Path::new("db")));
    }

    #[test]
    fn parse_args_rejects_missing_required_ccr() {
        let argv = vec!["ccr_verify".to_string()];
        let err = parse_args(&argv).unwrap_err();
        assert!(err.contains("--ccr is required"), "{err}");
    }
}
