use std::process::Command;

fn skip_heavy_blackbox_test() -> bool {
    std::env::var_os("RPKI_SKIP_HEAVY_BLACKBOX_TESTS").is_some()
}

#[test]
fn cli_run_offline_mode_executes_and_writes_json_and_ccr() {
    let db_dir = tempfile::tempdir().expect("db tempdir");
    let repo_dir = tempfile::tempdir().expect("repo tempdir");
    let out_dir = tempfile::tempdir().expect("out tempdir");
    let report_path = out_dir.path().join("report.json");
    let ccr_path = out_dir.path().join("result.ccr");

    let policy_path = out_dir.path().join("policy.toml");
    std::fs::write(&policy_path, "sync_preference = \"rsync_only\"\n").expect("write policy");

    let tal_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/tal/apnic-rfc7730-https.tal");
    let ta_path =
        std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer");

    let argv = vec![
        "rpki".to_string(),
        "--db".to_string(),
        db_dir.path().to_string_lossy().to_string(),
        "--policy".to_string(),
        policy_path.to_string_lossy().to_string(),
        "--tal-path".to_string(),
        tal_path.to_string_lossy().to_string(),
        "--ta-path".to_string(),
        ta_path.to_string_lossy().to_string(),
        "--rsync-local-dir".to_string(),
        repo_dir.path().to_string_lossy().to_string(),
        "--max-depth".to_string(),
        "0".to_string(),
        "--max-instances".to_string(),
        "1".to_string(),
        "--report-json".to_string(),
        report_path.to_string_lossy().to_string(),
        "--ccr-out".to_string(),
        ccr_path.to_string_lossy().to_string(),
    ];

    rpki::cli::run(&argv).expect("cli run");

    let bytes = std::fs::read(&report_path).expect("read report json");
    let v: serde_json::Value = serde_json::from_slice(&bytes).expect("parse report json");
    assert_eq!(v["format_version"], 2);
}

#[test]
fn cli_run_offline_mode_writes_decodable_ccr() {
    let db_dir = tempfile::tempdir().expect("db tempdir");
    let repo_dir = tempfile::tempdir().expect("repo tempdir");
    let out_dir = tempfile::tempdir().expect("out tempdir");
    let ccr_path = out_dir.path().join("result.ccr");

    let tal_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/tal/apnic-rfc7730-https.tal");
    let ta_path =
        std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer");

    let argv = vec![
        "rpki".to_string(),
        "--db".to_string(),
        db_dir.path().to_string_lossy().to_string(),
        "--tal-path".to_string(),
        tal_path.to_string_lossy().to_string(),
        "--ta-path".to_string(),
        ta_path.to_string_lossy().to_string(),
        "--rsync-local-dir".to_string(),
        repo_dir.path().to_string_lossy().to_string(),
        "--max-depth".to_string(),
        "0".to_string(),
        "--max-instances".to_string(),
        "1".to_string(),
        "--ccr-out".to_string(),
        ccr_path.to_string_lossy().to_string(),
    ];

    rpki::cli::run(&argv).expect("cli run");

    let bytes = std::fs::read(&ccr_path).expect("read ccr");
    let ccr = rpki::ccr::decode_content_info(&bytes).expect("decode ccr");
    assert!(ccr.content.tas.is_some());
}

#[test]
fn cli_run_offline_mode_writes_cir_and_static_pool() {
    let db_dir = tempfile::tempdir().expect("db tempdir");
    let repo_dir = tempfile::tempdir().expect("repo tempdir");
    let out_dir = tempfile::tempdir().expect("out tempdir");
    let cir_path = out_dir.path().join("result.cir");
    let policy_path = out_dir.path().join("policy.toml");
    std::fs::write(&policy_path, "sync_preference = \"rsync_only\"\n").expect("write policy");

    let tal_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/tal/apnic-rfc7730-https.tal");
    let ta_path =
        std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer");

    let argv = vec![
        "rpki".to_string(),
        "--db".to_string(),
        db_dir.path().to_string_lossy().to_string(),
        "--policy".to_string(),
        policy_path.to_string_lossy().to_string(),
        "--tal-path".to_string(),
        tal_path.to_string_lossy().to_string(),
        "--ta-path".to_string(),
        ta_path.to_string_lossy().to_string(),
        "--rsync-local-dir".to_string(),
        repo_dir.path().to_string_lossy().to_string(),
        "--max-depth".to_string(),
        "0".to_string(),
        "--max-instances".to_string(),
        "1".to_string(),
        "--cir-enable".to_string(),
        "--cir-out".to_string(),
        cir_path.to_string_lossy().to_string(),
        "--cir-tal-uri".to_string(),
        "https://example.test/root.tal".to_string(),
    ];

    rpki::cli::run(&argv).expect("cli run");

    let bytes = std::fs::read(&cir_path).expect("read cir");
    let cir = rpki::cir::decode_cir(&bytes).expect("decode cir");
    assert_eq!(cir.trust_anchors.len(), 1);
    assert_eq!(
        cir.trust_anchors[0].tal_uri,
        "https://example.test/root.tal"
    );
    assert!(!cir.trust_anchors[0].ta_certificate_der.is_empty());
    assert!(
        !cir.validated_objects()
            .any(|item| item.rsync_uri == cir.trust_anchors[0].ta_rsync_uri)
    );
}

#[test]
fn cli_run_blackbox_rsync_wrapper_mode_matches_reference_ccr_without_ta_path() {
    if skip_heavy_blackbox_test() {
        return;
    }
    let real_rsync = std::path::Path::new("/usr/bin/rsync");
    if !real_rsync.exists() {
        return;
    }

    let db_dir = tempfile::tempdir().expect("db tempdir");
    let out_dir = tempfile::tempdir().expect("out tempdir");
    let mirror_root = out_dir.path().join("mirror");
    let ref_ccr_path = out_dir.path().join("reference.ccr");
    let actual_ccr_path = out_dir.path().join("actual.ccr");

    let tal_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/tal/apnic-rfc7730-https.tal");
    let ta_path =
        std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer");
    let ta_bytes = std::fs::read(&ta_path).expect("read ta");

    std::fs::create_dir_all(mirror_root.join("rpki.apnic.net").join("repository"))
        .expect("mkdir mirror");
    std::fs::write(
        mirror_root
            .join("rpki.apnic.net")
            .join("repository")
            .join("apnic-rpki-root-iana-origin.cer"),
        ta_bytes,
    )
    .expect("write ta into mirror");

    let bin = env!("CARGO_BIN_EXE_rpki");
    let wrapper =
        std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/cir-rsync-wrapper");
    let reference = Command::new(bin)
        .env("REAL_RSYNC_BIN", real_rsync)
        .env("CIR_MIRROR_ROOT", &mirror_root)
        .args([
            "--db",
            db_dir
                .path()
                .join("reference-db")
                .to_string_lossy()
                .as_ref(),
            "--tal-path",
            tal_path.to_string_lossy().as_ref(),
            "--ta-path",
            ta_path.to_string_lossy().as_ref(),
            "--disable-rrdp",
            "--rsync-command",
            wrapper.to_string_lossy().as_ref(),
            "--validation-time",
            "2026-04-07T00:00:00Z",
            "--max-depth",
            "0",
            "--max-instances",
            "1",
            "--ccr-out",
            ref_ccr_path.to_string_lossy().as_ref(),
        ])
        .output()
        .expect("run reference wrapper mode");
    assert!(
        reference.status.success(),
        "stderr={}",
        String::from_utf8_lossy(&reference.stderr)
    );

    let out = Command::new(bin)
        .env("REAL_RSYNC_BIN", real_rsync)
        .env("CIR_MIRROR_ROOT", &mirror_root)
        .args([
            "--db",
            db_dir.path().join("actual-db").to_string_lossy().as_ref(),
            "--tal-path",
            tal_path.to_string_lossy().as_ref(),
            "--disable-rrdp",
            "--rsync-command",
            wrapper.to_string_lossy().as_ref(),
            "--validation-time",
            "2026-04-07T00:00:00Z",
            "--max-depth",
            "0",
            "--max-instances",
            "1",
            "--ccr-out",
            actual_ccr_path.to_string_lossy().as_ref(),
        ])
        .output()
        .expect("run blackbox wrapper mode");
    assert!(
        out.status.success(),
        "stderr={}",
        String::from_utf8_lossy(&out.stderr)
    );

    let reference = rpki::ccr::decode_content_info(&std::fs::read(&ref_ccr_path).unwrap())
        .expect("decode reference ccr");
    let actual = rpki::ccr::decode_content_info(&std::fs::read(&actual_ccr_path).unwrap())
        .expect("decode actual ccr");

    assert_eq!(actual.content.version, reference.content.version);
    assert_eq!(actual.content.hash_alg, reference.content.hash_alg);
    assert_eq!(actual.content.mfts, reference.content.mfts);
    assert_eq!(actual.content.vrps, reference.content.vrps);
    assert_eq!(actual.content.vaps, reference.content.vaps);
    assert_eq!(actual.content.tas, reference.content.tas);
    assert_eq!(actual.content.rks, reference.content.rks);
}
