use std::collections::BTreeSet;
use std::path::{Path, PathBuf};

use rpki::ccr::{VapCompareRow, VrpCompareRow, decode_ccr_compare_views};

fn fixture(rel: &str) -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(rel)
}

fn run_case(
    name: &str,
    tal_ta_pairs: &[(&Path, &Path)],
) -> (
    serde_json::Value,
    BTreeSet<VrpCompareRow>,
    BTreeSet<VapCompareRow>,
    rpki::ccr::CcrContentInfo,
) {
    let db_dir = tempfile::tempdir().expect("db tempdir");
    let out_dir = tempfile::tempdir().expect("out tempdir");
    let report_path = out_dir.path().join(format!("{name}.report.json"));
    let ccr_path = out_dir.path().join(format!("{name}.ccr"));

    let mut argv = vec![
        "rpki".to_string(),
        "--db".to_string(),
        db_dir.path().to_string_lossy().to_string(),
        "--disable-rrdp".to_string(),
        "--rsync-local-dir".to_string(),
        fixture("tests/fixtures/repository")
            .to_string_lossy()
            .to_string(),
        "--validation-time".to_string(),
        "2026-04-07T00:00:00Z".to_string(),
        "--max-depth".to_string(),
        "0".to_string(),
        "--max-instances".to_string(),
        tal_ta_pairs.len().to_string(),
    ];
    for (tal_path, ta_path) in tal_ta_pairs {
        argv.push("--tal-path".to_string());
        argv.push(tal_path.to_string_lossy().to_string());
        argv.push("--ta-path".to_string());
        argv.push(ta_path.to_string_lossy().to_string());
    }
    argv.extend([
        "--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 report: serde_json::Value =
        serde_json::from_slice(&std::fs::read(&report_path).expect("read report"))
            .expect("parse report");
    let ccr = rpki::ccr::decode_content_info(&std::fs::read(&ccr_path).expect("read ccr"))
        .expect("decode ccr");
    let (vrps, vaps) = decode_ccr_compare_views(&ccr).expect("compare views");
    (report, vrps, vaps, ccr)
}

#[test]
fn multi_tal_parallel_output_matches_union_of_single_tal_outputs() {
    let apnic_tal = fixture("tests/fixtures/tal/apnic-rfc7730-https.tal");
    let apnic_ta = fixture("tests/fixtures/ta/apnic-ta.cer");
    let arin_tal = fixture("tests/fixtures/tal/arin.tal");
    let arin_ta = fixture("tests/fixtures/ta/arin-ta.cer");

    let (apnic_report, apnic_vrps, apnic_vaps, _) = run_case("apnic", &[(&apnic_tal, &apnic_ta)]);
    let (arin_report, arin_vrps, arin_vaps, _) = run_case("arin", &[(&arin_tal, &arin_ta)]);
    let (multi_report, multi_vrps, multi_vaps, multi_ccr) =
        run_case("multi", &[(&apnic_tal, &apnic_ta), (&arin_tal, &arin_ta)]);

    let expected_vrps = apnic_vrps
        .union(&arin_vrps)
        .cloned()
        .collect::<BTreeSet<_>>();
    let expected_vaps = apnic_vaps
        .union(&arin_vaps)
        .cloned()
        .collect::<BTreeSet<_>>();

    assert_eq!(multi_vrps, expected_vrps);
    assert_eq!(multi_vaps, expected_vaps);

    let single_point_count = apnic_report["publication_points"]
        .as_array()
        .expect("apnic points")
        .len()
        + arin_report["publication_points"]
            .as_array()
            .expect("arin points")
            .len();
    assert_eq!(
        multi_report["publication_points"]
            .as_array()
            .expect("multi points")
            .len(),
        single_point_count
    );

    let tas = multi_ccr
        .content
        .tas
        .as_ref()
        .expect("multi-TAL CCR must include trust anchors");
    assert_eq!(tas.skis.len(), 2);
}
