use std::path::PathBuf;
use std::process::Command;

use rpki::blob_store::ExternalRepoBytesDb;
use rpki::ccr::{
    CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation, TrustAnchorState,
    encode_content_info,
};
use rpki::cir::{CanonicalInputRepresentation, CirObject, CirTrustAnchor, encode_cir, sha256};

#[test]
fn cir_drop_report_counts_dropped_roa_objects_and_vrps() {
    let td = tempfile::tempdir().expect("tempdir");
    let cir_path = td.path().join("input.cir");
    let ccr_path = td.path().join("output.ccr");
    let report_path = td.path().join("report.json");
    let repo_bytes_db = td.path().join("repo-bytes.db");
    let json_out = td.path().join("drop.json");
    let md_out = td.path().join("drop.md");

    let roa_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/AS4538.roa");
    let roa_bytes = std::fs::read(&roa_path).expect("read roa fixture");
    let hash = {
        use sha2::{Digest, Sha256};
        hex::encode(Sha256::digest(&roa_bytes))
    };
    ExternalRepoBytesDb::open(&repo_bytes_db)
        .expect("open repo bytes")
        .put_blob_bytes_batch(&[(hash.clone(), roa_bytes.clone())])
        .expect("write repo bytes");

    let cir = CanonicalInputRepresentation::new_v4(
        time::OffsetDateTime::parse(
            "2026-04-09T00:00:00Z",
            &time::format_description::well_known::Rfc3339,
        )
        .unwrap(),
        vec![CirObject {
            rsync_uri: "rsync://example.net/repo/AS4538.roa".to_string(),
            sha256: hex::decode(&hash).unwrap(),
        }],
        Vec::new(),
        vec![test_trust_anchor()],
        Vec::new(),
        Vec::new(),
    );
    std::fs::write(&cir_path, encode_cir(&cir).unwrap()).unwrap();

    let ccr = CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
        version: 0,
        hash_alg: CcrDigestAlgorithm::Sha256,
        produced_at: cir.validation_time,
        mfts: None,
        vrps: None,
        vaps: None,
        tas: Some(TrustAnchorState {
            skis: vec![vec![0x11; 20]],
            hash: vec![0x22; 32],
        }),
        rks: None,
    });
    std::fs::write(&ccr_path, encode_content_info(&ccr).unwrap()).unwrap();

    let report = serde_json::json!({
        "format_version": 2,
        "publication_points": [
            {
                "manifest_rsync_uri": "rsync://example.net/repo/example.mft",
                "publication_point_rsync_uri": "rsync://example.net/repo/",
                "objects": [
                    {
                        "rsync_uri": "rsync://example.net/repo/AS4538.roa",
                        "sha256_hex": hash,
                        "kind": "roa",
                        "result": "error",
                        "detail": "policy rejected"
                    }
                ]
            }
        ]
    });
    std::fs::write(&report_path, serde_json::to_vec_pretty(&report).unwrap()).unwrap();

    let bin = env!("CARGO_BIN_EXE_cir_drop_report");
    let out = Command::new(bin)
        .args([
            "--cir",
            cir_path.to_string_lossy().as_ref(),
            "--ccr",
            ccr_path.to_string_lossy().as_ref(),
            "--report-json",
            report_path.to_string_lossy().as_ref(),
            "--repo-bytes-db",
            repo_bytes_db.to_string_lossy().as_ref(),
            "--json-out",
            json_out.to_string_lossy().as_ref(),
            "--md-out",
            md_out.to_string_lossy().as_ref(),
        ])
        .output()
        .expect("run cir_drop_report");
    assert!(
        out.status.success(),
        "stderr={}",
        String::from_utf8_lossy(&out.stderr)
    );

    let output: serde_json::Value =
        serde_json::from_slice(&std::fs::read(&json_out).unwrap()).unwrap();
    assert_eq!(output["summary"]["droppedObjectCount"], 1);
    assert!(output["summary"]["droppedVrpCount"].as_u64().unwrap_or(0) >= 1);
    assert_eq!(output["summary"]["droppedByKind"]["roa"], 1);
    assert!(
        std::fs::read_to_string(&md_out)
            .unwrap()
            .contains("Dropped By Reason")
    );
}

fn test_trust_anchor() -> CirTrustAnchor {
    let ta_rsync_uri = "rsync://example.net/repo/root.cer";
    let ta_certificate_der = b"ta-der".to_vec();
    CirTrustAnchor {
        ta_rsync_uri: ta_rsync_uri.to_string(),
        tal_uri: "https://example.test/root.tal".to_string(),
        tal_bytes: format!("{ta_rsync_uri}\n\nAQID\n").into_bytes(),
        ta_certificate_sha256: sha256(&ta_certificate_der),
        ta_certificate_der,
    }
}
