//! Smoke tests for KvWorkLog: plan / depend / dispatch / transition / dag.

use klieo_bus_memory::MemoryBus;
use klieo_ops::worklog::{KvWorkLog, WorkItem, WorkLog, WorkLogError, WorkStatus};

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

#[tokio::test]
async fn plan_without_deps_starts_ready() {
    let bus = MemoryBus::new();
    let wl = KvWorkLog::new(bus.kv.clone());
    let id = wl.plan(fresh_item("solo")).await.expect("plan");
    let item = wl.get(id).await.expect("get");
    assert_eq!(item.status, WorkStatus::Ready);
}

#[tokio::test]
async fn plan_with_dep_starts_planned_then_ready() {
    let bus = MemoryBus::new();
    let wl = KvWorkLog::new(bus.kv.clone());
    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");
    assert_eq!(
        wl.get(child_id.clone()).await.unwrap().status,
        WorkStatus::Planned
    );

    wl.transition(parent_id, WorkStatus::Done)
        .await
        .expect("done parent");
    assert_eq!(wl.get(child_id).await.unwrap().status, WorkStatus::Ready);
}

#[tokio::test]
async fn depend_rejects_cycle() {
    let bus = MemoryBus::new();
    let wl = KvWorkLog::new(bus.kv.clone());
    let a = wl.plan(fresh_item("a")).await.expect("plan a");
    let b = wl.plan(fresh_item("b")).await.expect("plan b");
    wl.depend(b.clone(), a.clone())
        .await
        .expect("b depends on a");
    let err = wl.depend(a, b).await.expect_err("must reject cycle");
    assert!(matches!(err, WorkLogError::CycleDetected { .. }));
}

#[tokio::test]
async fn dispatch_requires_ready_status() {
    let bus = MemoryBus::new();
    let wl = KvWorkLog::new(bus.kv.clone());
    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");
    let err = wl
        .dispatch(child_id)
        .await
        .expect_err("planned cannot dispatch");
    assert!(matches!(err, WorkLogError::Internal(_)));
}

#[tokio::test]
async fn cap_rejects_overplan() {
    let bus = MemoryBus::new();
    let wl = KvWorkLog::with_cap(bus.kv.clone(), 2);
    let _ = wl.plan(fresh_item("a")).await.expect("plan a");
    let _ = wl.plan(fresh_item("b")).await.expect("plan b");
    let err = wl.plan(fresh_item("c")).await.expect_err("cap exceeded");
    assert!(matches!(err, WorkLogError::CapExceeded { cap: 2 }));
}
