use rpki::ccr::{
    CcrContentInfo, CcrDigestAlgorithm, ManifestInstance, ManifestState, RoaPayloadSet,
    RoaPayloadState, RouterKey, RouterKeySet, RouterKeyState, TrustAnchorState, compute_state_hash,
    decode_content_info, dump_content_info_json_value,
    encode::{
        encode_aspa_payload_state_payload_der, encode_content_info,
        encode_manifest_state_payload_der, encode_roa_payload_state_payload_der,
        encode_router_key_state_payload_der, encode_trust_anchor_state_payload_der,
    },
    verify::{
        verify_against_report_json_path, verify_against_vcir_store, verify_content_info_bytes,
    },
};
use rpki::data_model::common::BigUnsigned;
use rpki::storage::{
    PackTime, RocksStore, ValidatedCaInstanceResult, ValidatedManifestMeta, VcirArtifactKind,
    VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary, VcirCcrManifestProjection,
    VcirChildEntry, VcirInstanceGate, VcirRelatedArtifact, VcirSummary,
};

fn sample_time() -> time::OffsetDateTime {
    time::OffsetDateTime::parse(
        "2026-03-24T00:00:00Z",
        &time::format_description::well_known::Rfc3339,
    )
    .expect("valid rfc3339")
}

fn sample_manifest_state() -> ManifestState {
    let mis = vec![ManifestInstance {
        hash: vec![0x10; 32],
        size: 2048,
        aki: vec![0x20; 20],
        manifest_number: BigUnsigned { bytes_be: vec![1] },
        this_update: sample_time(),
        locations: vec![vec![0x30, 0x00]],
        subordinates: vec![vec![0x30; 20]],
    }];
    let der = encode_manifest_state_payload_der(&mis).expect("encode mis");
    ManifestState {
        mis,
        most_recent_update: sample_time(),
        hash: compute_state_hash(&der),
    }
}

fn sample_roa_state() -> RoaPayloadState {
    let rps = vec![RoaPayloadSet {
        as_id: 64496,
        ip_addr_blocks: vec![vec![
            0x30, 0x08, 0x04, 0x02, 0x00, 0x01, 0x30, 0x02, 0x03, 0x00,
        ]],
    }];
    let der = encode_roa_payload_state_payload_der(&rps).expect("encode rps");
    RoaPayloadState {
        rps,
        hash: compute_state_hash(&der),
    }
}

fn sample_aspa_state() -> rpki::ccr::AspaPayloadState {
    let aps = vec![rpki::ccr::AspaPayloadSet {
        customer_as_id: 64496,
        providers: vec![64497],
    }];
    let der = encode_aspa_payload_state_payload_der(&aps).expect("encode aps");
    rpki::ccr::AspaPayloadState {
        aps,
        hash: compute_state_hash(&der),
    }
}

fn sample_ta_state() -> TrustAnchorState {
    let skis = vec![vec![0x11; 20]];
    let der = encode_trust_anchor_state_payload_der(&skis).expect("encode skis");
    TrustAnchorState {
        skis,
        hash: compute_state_hash(&der),
    }
}

fn sample_rks() -> RouterKeyState {
    let rksets = vec![RouterKeySet {
        as_id: 64496,
        router_keys: vec![RouterKey {
            ski: vec![0x22; 20],
            spki_der: vec![0x30, 0x00],
        }],
    }];
    let der = encode_router_key_state_payload_der(&rksets).expect("encode rk");
    RouterKeyState {
        rksets,
        hash: compute_state_hash(&der),
    }
}

fn sample_ccr() -> Vec<u8> {
    let ccr = rpki::ccr::RpkiCanonicalCacheRepresentation {
        version: 0,
        hash_alg: CcrDigestAlgorithm::Sha256,
        produced_at: sample_time(),
        mfts: Some(sample_manifest_state()),
        vrps: Some(sample_roa_state()),
        vaps: Some(sample_aspa_state()),
        tas: Some(sample_ta_state()),
        rks: Some(sample_rks()),
    };
    encode_content_info(&CcrContentInfo::new(ccr)).expect("encode ccr")
}

fn sample_ccr_manifest_projection(
    manifest_rsync_uri: &str,
    manifest_sha256: Vec<u8>,
) -> VcirCcrManifestProjection {
    VcirCcrManifestProjection {
        manifest_rsync_uri: manifest_rsync_uri.to_string(),
        manifest_sha256,
        manifest_size: 2048,
        manifest_ee_aki: vec![0x55; 20],
        manifest_number_be: vec![1],
        manifest_this_update: PackTime::from_utc_offset_datetime(sample_time()),
        manifest_sia_locations_der: vec![vec![
            0x30, 0x11, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x05, 0x86, 0x05,
            b'r', b's', b'y', b'n', b'c',
        ]],
        subordinate_skis: vec![vec![0x33; 20]],
    }
}

#[test]
fn verify_content_info_bytes_succeeds_for_valid_ccr() {
    let der = sample_ccr();
    let summary = verify_content_info_bytes(&der).expect("verify ccr");
    assert!(summary.state_hashes_ok);
    assert_eq!(summary.manifest_instances, 1);
    assert_eq!(summary.roa_payload_sets, 1);
    assert_eq!(summary.aspa_payload_sets, 1);
    assert_eq!(summary.trust_anchor_ski_count, 1);
    assert_eq!(summary.router_key_sets, 1);
    assert_eq!(summary.router_key_count, 1);
}

#[test]
fn verify_content_info_bytes_rejects_tampered_manifest_hash() {
    let mut content_info = decode_content_info(&sample_ccr()).expect("decode ccr");
    content_info.content.mfts.as_mut().unwrap().hash[0] ^= 0x01;
    let der = encode_content_info(&content_info).expect("encode tampered ccr");
    let err = verify_content_info_bytes(&der).expect_err("tampered hash must fail");
    assert!(
        err.to_string().contains("ManifestState hash mismatch"),
        "{err}"
    );
}

#[test]
fn dump_content_info_json_value_contains_expected_counts() {
    let json = dump_content_info_json_value(&sample_ccr()).expect("dump ccr");
    assert_eq!(json["version"], 0);
    assert_eq!(json["state_aspects"]["mfts"]["manifest_instances"], 1);
    assert_eq!(json["state_aspects"]["vrps"]["payload_sets"], 1);
    assert_eq!(json["state_aspects"]["vaps"]["payload_sets"], 1);
    assert_eq!(json["state_aspects"]["tas"]["ski_count"], 1);
    assert_eq!(json["state_aspects"]["rks"]["router_key_sets"], 1);
}

#[test]
fn verify_against_report_json_path_rejects_mismatching_report() {
    let td = tempfile::tempdir().expect("tempdir");
    let report = serde_json::json!({
        "vrps": [{"asn": 64496, "prefix": "0.0.0.0/0", "max_length": 0}],
        "aspas": [{"customer_as_id": 64496, "provider_as_ids": [64497]}]
    });
    let report_path = td.path().join("report.json");
    std::fs::write(&report_path, serde_json::to_vec(&report).unwrap()).expect("write report");

    let mut ci = decode_content_info(&sample_ccr()).expect("decode ccr");
    ci.content.vrps = Some(RoaPayloadState {
        rps: vec![RoaPayloadSet {
            as_id: 64496,
            ip_addr_blocks: vec![vec![
                0x30, 0x08, 0x04, 0x02, 0x00, 0x01, 0x30, 0x02, 0x03, 0x00,
            ]],
        }],
        hash: compute_state_hash(
            &encode_roa_payload_state_payload_der(&[RoaPayloadSet {
                as_id: 64496,
                ip_addr_blocks: vec![vec![
                    0x30, 0x08, 0x04, 0x02, 0x00, 0x01, 0x30, 0x02, 0x03, 0x00,
                ]],
            }])
            .unwrap(),
        ),
    });
    verify_against_report_json_path(&ci, &report_path).expect_err("report mismatch expected");
}

#[test]
fn verify_against_vcir_store_matches_manifest_hashes() {
    let td = tempfile::tempdir().expect("tempdir");
    let store = RocksStore::open(td.path()).expect("open rocksdb");
    let manifest_hash = hex::encode(vec![0x10; 32]);
    let vcir = ValidatedCaInstanceResult {
        manifest_rsync_uri: "rsync://example.test/current.mft".to_string(),
        parent_manifest_rsync_uri: None,
        tal_id: "apnic".to_string(),
        ca_subject_name: "CN=test".to_string(),
        ca_ski: "11".repeat(20),
        issuer_ski: "22".repeat(20),
        last_successful_validation_time: PackTime::from_utc_offset_datetime(sample_time()),
        current_manifest_rsync_uri: "rsync://example.test/current.mft".to_string(),
        current_crl_rsync_uri: "rsync://example.test/current.crl".to_string(),
        validated_manifest_meta: ValidatedManifestMeta {
            validated_manifest_number: vec![1],
            validated_manifest_this_update: PackTime::from_utc_offset_datetime(sample_time()),
            validated_manifest_next_update: PackTime::from_utc_offset_datetime(sample_time()),
        },
        ccr_manifest_projection: sample_ccr_manifest_projection(
            "rsync://example.test/current.mft",
            vec![0x10; 32],
        ),
        instance_gate: VcirInstanceGate {
            manifest_next_update: PackTime::from_utc_offset_datetime(sample_time()),
            current_crl_next_update: PackTime::from_utc_offset_datetime(sample_time()),
            self_ca_not_after: PackTime::from_utc_offset_datetime(sample_time()),
            instance_effective_until: PackTime::from_utc_offset_datetime(sample_time()),
        },
        child_entries: vec![VcirChildEntry {
            child_manifest_rsync_uri: "rsync://example.test/child.mft".to_string(),
            child_cert_rsync_uri: "rsync://example.test/child.cer".to_string(),
            child_cert_hash: "aa".repeat(32),
            child_ski: "33".repeat(20),
            child_rsync_base_uri: "rsync://example.test/".to_string(),
            child_publication_point_rsync_uri: "rsync://example.test/".to_string(),
            child_rrdp_notification_uri: None,
            child_effective_ip_resources: None,
            child_effective_as_resources: None,
            accepted_at_validation_time: PackTime::from_utc_offset_datetime(sample_time()),
        }],
        local_outputs: Vec::new(),
        related_artifacts: vec![VcirRelatedArtifact {
            artifact_role: VcirArtifactRole::Manifest,
            artifact_kind: VcirArtifactKind::Mft,
            uri: Some("rsync://example.test/current.mft".to_string()),
            sha256: manifest_hash.clone(),
            object_type: Some("mft".to_string()),
            validation_status: VcirArtifactValidationStatus::Accepted,
            reject_reason: None,
        }],
        summary: VcirSummary {
            local_vrp_count: 0,
            local_aspa_count: 0,
            local_router_key_count: 0,
            child_count: 1,
            accepted_object_count: 1,
            rejected_object_count: 0,
        },
        audit_summary: VcirAuditSummary {
            failed_fetch_eligible: true,
            last_failed_fetch_reason: None,
            warning_count: 0,
            audit_flags: Vec::new(),
        },
    };
    store.put_vcir(&vcir).expect("put vcir");
    let ci = decode_content_info(&sample_ccr()).expect("decode ccr");
    verify_against_vcir_store(&ci, &store).expect("vcir cross-check");
}
