//! Integration test: `KvWorkLog::with_audit` emits `OpsEvent`s for plan,
//! depend, dispatch, and transition state changes.

use klieo_bus_memory::MemoryBus;
use klieo_core::memory::{Episode, EpisodicMemory};
use klieo_core::test_utils::InMemoryEpisodic;
use klieo_core::RunId;
use klieo_ops::ops_event::OpsEvent;
use klieo_ops::worklog::{KvWorkLog, WorkItem, WorkLog, WorkStatus};
use std::sync::Arc;

fn fresh_item(title: &str) -> WorkItem {
    WorkItem::new(title, serde_json::json!({}), None, vec![])
}

#[tokio::test]
async fn worklog_state_changes_emit_ops_events() {
    let episodic = Arc::new(InMemoryEpisodic::default());
    let run_id = RunId::new();
    let bus = MemoryBus::new();
    let wl = KvWorkLog::new(bus.kv.clone())
        .with_audit(episodic.clone() as Arc<dyn EpisodicMemory>, run_id);

    // Plan a parent and a child with a dependency.
    let parent_id = wl.plan(fresh_item("parent")).await.expect("plan parent");
    let child_item = WorkItem::new(
        "child",
        serde_json::json!({}),
        None,
        vec![parent_id.clone()],
    );
    let child_id = wl.plan(child_item).await.expect("plan child");

    // Dispatch the parent (Ready → InProgress).
    wl.dispatch(parent_id.clone()).await.expect("dispatch");

    // Transition parent to Done; child should become Ready.
    wl.transition(parent_id.clone(), WorkStatus::Done)
        .await
        .expect("transition to Done");

    // Dispatch the child.
    wl.dispatch(child_id.clone()).await.expect("dispatch child");

    // Explicitly add a dependency to check WorkDependencyAdded is also emitted.
    let extra = wl.plan(fresh_item("extra")).await.expect("plan extra");
    let another = wl.plan(fresh_item("another")).await.expect("plan another");
    wl.depend(another.clone(), extra.clone())
        .await
        .expect("depend");

    let events = episodic.replay(run_id).await.expect("replay");
    let ops: Vec<OpsEvent> = events
        .into_iter()
        .filter_map(|e| match e {
            Episode::Ops(payload) => OpsEvent::from_episode_payload(&payload),
            _ => None,
        })
        .collect();

    assert!(
        ops.iter()
            .any(|e| matches!(e, OpsEvent::WorkPlanned { .. })),
        "expected WorkPlanned event"
    );
    assert!(
        ops.iter()
            .any(|e| matches!(e, OpsEvent::WorkDependencyAdded { .. })),
        "expected WorkDependencyAdded event"
    );
    assert!(
        ops.iter()
            .any(|e| matches!(e, OpsEvent::WorkDispatched { .. })),
        "expected WorkDispatched event"
    );
    assert!(
        ops.iter()
            .any(|e| matches!(e, OpsEvent::WorkTransition { .. })),
        "expected WorkTransition event"
    );
}
