//! Integration test: `KvHandoff::with_audit` emits `OpsEvent`s for package,
//! deliver, verify-success, and verify-failure state changes.

use ed25519_dalek::SigningKey;
use klieo_bus_memory::MemoryBus;
use klieo_core::memory::{Episode, EpisodicMemory};
use klieo_core::test_utils::InMemoryEpisodic;
use klieo_core::RunId;
use klieo_ops::handoff::{Handoff, HandoffEnvelope, HandoffState, KvHandoff};
use klieo_ops::ops_event::OpsEvent;
use klieo_ops::types::{AgentId, TenantId};
use klieo_ops::{SoftwareSigner, SourceIdentityRegistry, StaticSourceIdentityRegistry};
use std::collections::HashMap;
use std::sync::Arc;

const EMITTER_AGENT: &str = "emitter-agent";

fn fresh_state() -> HandoffState {
    HandoffState::new(
        "rn_test_0001",
        "aabbccdd",
        b"[REDACTED]".to_vec(),
        AgentId(EMITTER_AGENT.into()),
        Some(TenantId("BL_test".into())),
        None,
    )
}

fn audited_handoff(episodic: Arc<InMemoryEpisodic>, run_id: RunId) -> KvHandoff {
    let bus = MemoryBus::new();
    let sk = SigningKey::from_bytes(&[42u8; 32]);
    let mut keys = HashMap::new();
    keys.insert(AgentId(EMITTER_AGENT.into()), sk.verifying_key());
    let registry: Arc<dyn SourceIdentityRegistry> =
        Arc::new(StaticSourceIdentityRegistry::from_map(keys));
    KvHandoff::new(bus.kv, registry).with_audit(episodic as Arc<dyn EpisodicMemory>, run_id)
}

fn collect_ops(events: Vec<Episode>) -> Vec<OpsEvent> {
    events
        .into_iter()
        .filter_map(|e| match e {
            Episode::Ops(payload) => OpsEvent::from_episode_payload(&payload),
            _ => None,
        })
        .collect()
}

#[tokio::test]
async fn package_deliver_verify_emit_ops_events() {
    let episodic = Arc::new(InMemoryEpisodic::default());
    let run_id = RunId::new();
    let h = audited_handoff(episodic.clone(), run_id);

    let signer = SoftwareSigner::from_bytes([42u8; 32]);
    let envelope = h
        .package(fresh_state(), &signer, chrono::Duration::hours(1))
        .await
        .expect("package");
    h.deliver(envelope.clone(), AgentId("receiver-agent".into()))
        .await
        .expect("deliver");
    h.verify(&envelope).await.expect("verify");

    let events = episodic.replay(run_id).await.expect("replay");
    let ops = collect_ops(events);

    assert!(
        ops.iter()
            .any(|e| matches!(e, OpsEvent::HandoffPackaged { .. })),
        "expected HandoffPackaged event"
    );
    assert!(
        ops.iter()
            .any(|e| matches!(e, OpsEvent::HandoffDelivered { .. })),
        "expected HandoffDelivered event"
    );
    assert!(
        ops.iter()
            .any(|e| matches!(e, OpsEvent::HandoffVerified { .. })),
        "expected HandoffVerified event"
    );
}

#[tokio::test]
async fn verify_failure_emits_verification_failed_event() {
    let episodic = Arc::new(InMemoryEpisodic::default());
    let run_id = RunId::new();
    let h = audited_handoff(episodic.clone(), run_id);

    let signer = SoftwareSigner::from_bytes([42u8; 32]);
    let mut envelope: HandoffEnvelope = h
        .package(fresh_state(), &signer, chrono::Duration::hours(1))
        .await
        .expect("package");
    // Tamper with the redacted state to break the signature.
    envelope.redacted_state[0] ^= 0xff;
    let _ = h.verify(&envelope).await;

    let events = episodic.replay(run_id).await.expect("replay");
    let ops = collect_ops(events);

    assert!(
        ops.iter()
            .any(|e| matches!(e, OpsEvent::HandoffVerificationFailed { .. })),
        "expected HandoffVerificationFailed event after tamper"
    );
}
