//! Acceptance tests for the BFLD JSON wire spec `rf_signature_hash` format
//! (`"blake3:<64-hex>"`) and the end-to-end emitter → hasher → event → JSON path.

#![cfg(all(feature = "std", feature = "serde-json"))]

use wifi_densepose_bfld::{
    BfldEmitter, BfldEvent, IdentityEmbedding, PrivacyClass, SensingInputs, SignatureHasher,
    EMBEDDING_DIM, SITE_SALT_LEN,
};

fn manual_event(hash: Option<[u8; 32]>) -> BfldEvent {
    BfldEvent::with_privacy_gating(
        "seed-01".into(),
        1_700_000_000_000_000_000,
        true,
        0.5,
        1,
        0.9,
        None,
        PrivacyClass::Anonymous,
        Some(0.3),
        hash,
    )
}

#[test]
fn rf_signature_hash_serializes_as_blake3_prefixed_lowercase_hex() {
    let hash = [
        0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x11, 0x22, 0x33,
        0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
        0xCC, 0xDD, 0xEE, 0xFF, 0x12, 0x34, 0x56, 0x78,
        0x9A, 0xBC, 0xDE, 0xF0, 0x0F, 0xED, 0xCB, 0xA9,
    ];
    // Build expected hex programmatically — manual typing is error-prone.
    let mut expected_hex = String::from("blake3:");
    for b in &hash {
        expected_hex.push_str(&format!("{b:02x}"));
    }
    let json = manual_event(Some(hash)).to_json().unwrap();
    let needle = format!("\"rf_signature_hash\":\"{expected_hex}\"");
    assert!(
        json.contains(&needle),
        "JSON: {json}\nexpected substring: {needle}",
    );
}

#[test]
fn hex_string_is_always_64_chars_when_present() {
    let json = manual_event(Some([0x00; 32])).to_json().unwrap();
    // Find the substring after "blake3:" inside the rf_signature_hash field.
    let key = "\"rf_signature_hash\":\"blake3:";
    let start = json.find(key).expect("hash field present") + key.len();
    let end = json[start..].find('"').expect("closing quote") + start;
    let hex = &json[start..end];
    assert_eq!(hex.len(), 64, "hash hex must be exactly 64 chars, got {}", hex.len());
    assert!(
        hex.chars().all(|c| c.is_ascii_hexdigit() && !c.is_uppercase()),
        "hash hex must be lowercase only, got {hex}",
    );
}

#[test]
fn hash_field_omitted_entirely_when_none() {
    let json = manual_event(None).to_json().unwrap();
    assert!(
        !json.contains("rf_signature_hash"),
        "None hash must be omitted entirely, got: {json}",
    );
}

// --- Cross-iter integration test ----------------------------------------

fn salt() -> [u8; SITE_SALT_LEN] {
    let mut s = [0u8; SITE_SALT_LEN];
    for (i, b) in s.iter_mut().enumerate() {
        *b = i as u8;
    }
    s
}

fn embedding() -> IdentityEmbedding {
    let mut a = [0.0f32; EMBEDDING_DIM];
    for (i, v) in a.iter_mut().enumerate() {
        *v = (i as f32) * 0.01;
    }
    IdentityEmbedding::from_raw(a)
}

fn inputs() -> SensingInputs {
    SensingInputs {
        timestamp_ns: 1_700_000_000_000_000_000,
        presence: true,
        motion: 0.42,
        person_count: 1,
        sensing_confidence: 0.91,
        sep: 0.2,
        stab: 0.2,
        consist: 0.2,
        risk_conf: 0.2,
        rf_signature_hash: None, // hasher will derive
    }
}

#[test]
fn end_to_end_emitter_hasher_to_json_emits_blake3_hex_hash() {
    let mut e = BfldEmitter::new("seed-01")
        .with_signature_hasher(SignatureHasher::new(salt()));
    let event = e
        .emit(inputs(), Some(embedding()))
        .expect("low-risk emit must succeed");
    let json = event.to_json().expect("JSON serialization");
    assert!(
        json.contains("\"rf_signature_hash\":\"blake3:"),
        "end-to-end JSON missing derived hash: {json}",
    );
    assert!(json.contains("\"type\":\"bfld_update\""));
    assert!(json.contains("\"node_id\":\"seed-01\""));
    assert!(json.contains("\"privacy_class\":\"anonymous\""));
}

#[test]
fn end_to_end_restricted_class_omits_hash_even_with_hasher_set() {
    let mut e = BfldEmitter::new("seed-01")
        .with_privacy_class(PrivacyClass::Restricted)
        .with_signature_hasher(SignatureHasher::new(salt()));
    let event = e
        .emit(inputs(), Some(embedding()))
        .expect("low-risk emit must succeed");
    let json = event.to_json().expect("JSON serialization");
    assert!(
        !json.contains("rf_signature_hash"),
        "Restricted class must strip rf_signature_hash from JSON, got: {json}",
    );
    assert!(
        !json.contains("identity_risk_score"),
        "Restricted class must also strip identity_risk_score, got: {json}",
    );
}
