//! SC-4b — A SESSION-OWNED fire-and-return approval is never prompted and never
//! falsely resolved by the in-process transport.
//!
//! `host.request_approval` defaults `system_level: false`, producing a row with
//! `client_id: Some(...)` — owned by the requesting WS session
//! (`handler.rs:2008-2012`). The action carries NO `ws.method:` prefix, so the
//! pre-fix eligibility predicate (which only excluded `ws.method:*`) would have
//! made it eligible: the orchestrator would PROMPT for it, the user would reply,
//! and `HostState::resolve_approval("imessage-transport", ...)` would hit the
//! cross-session fan-out branch (`host.rs:334-366`) — returning `Ok(still-
//! Pending)` WITHOUT resolving — while the orchestrator treated the `Ok` as
//! success and dropped the code. The fix narrows eligibility to
//! `client_id.is_none()` (system-level rows only — the only ones an in-process,
//! session-less caller can actually resolve).
//!
//! This test pins BOTH halves of the fix:
//! 1. A session-owned fire-and-return row produces ZERO outbound prompts
//!    (it is not eligible, so no code is ever minted for it).
//! 2. If such a row is somehow fed inbound (bare approve), no code is falsely
//!    consumed, no resolve lands, and the row stays Pending.

mod messaging_harness;
use messaging_harness::{inbound, Harness, PAIRED};

#[tokio::test]
async fn session_owned_row_is_never_prompted() {
    let h = Harness::enabled_paired();
    // A session-owned (client_id: Some) fire-and-return row — the default of
    // host.request_approval. Action has NO ws.method: prefix.
    let owned_id = h
        .seed_session_owned_fire_and_return("send the quarterly report email")
        .await;

    // Outbound observe: a session-owned row is NOT eligible, so ZERO prompts.
    h.orch.observe_and_notify().await;
    assert_eq!(
        h.spy.count(),
        0,
        "a session-owned fire-and-return row must NOT be prompted over iMessage"
    );

    // The row is still Pending (the orchestrator did nothing to it).
    assert!(
        !h.is_resolved(&owned_id).await,
        "the session-owned row stays Pending — the transport never touched it"
    );
}

#[tokio::test]
async fn session_owned_row_fed_inbound_is_not_falsely_consumed() {
    let h = Harness::enabled_paired();
    let owned_id = h
        .seed_session_owned_fire_and_return("transfer funds to vendor")
        .await;

    // It was never prompted, so no code maps to it. Now an inbound bare
    // "approve" arrives from the paired handle. Because the only pending row is
    // NOT eligible (session-owned), the "sole pending" set is empty: nothing
    // resolves, no code is consumed, no resolved event fires.
    h.orch.handle_inbound(&inbound(PAIRED, "approve", 1)).await;

    assert!(
        !h.is_resolved(&owned_id).await,
        "a session-owned row must NOT be resolved by an inbound approve"
    );
    assert_eq!(
        h.approval_resolved_count().await,
        0,
        "no approval.resolved fired — the only row was ineligible"
    );

    // A coded approve naming a fabricated code also resolves nothing (no code
    // was ever minted for an ineligible row).
    h.orch
        .handle_inbound(&inbound(PAIRED, "A0 approve", 2))
        .await;
    assert!(
        !h.is_resolved(&owned_id).await,
        "a fabricated code cannot resolve the session-owned row"
    );
    assert_eq!(
        h.approval_resolved_count().await,
        0,
        "still nothing resolved"
    );
}

#[tokio::test]
async fn system_level_row_alongside_session_owned_resolves_only_the_system_one() {
    // Mixed queue: one system-level (eligible) row and one session-owned
    // (ineligible) row. Exactly one prompt goes out (for the system-level row);
    // a coded approve resolves only that one and leaves the session-owned row
    // Pending.
    let h = Harness::enabled_paired();
    let system_id = h.seed_fire_and_return("delete the staging bucket").await;
    let owned_id = h
        .seed_session_owned_fire_and_return("email the board deck")
        .await;

    h.orch.observe_and_notify().await;
    assert_eq!(
        h.spy.count(),
        1,
        "only the system-level row is prompted; the session-owned row is excluded"
    );
    let code = messaging_harness::extract_code(&h.spy.calls()[0].1).expect("code in prompt");

    h.orch
        .handle_inbound(&inbound(PAIRED, &format!("{code} approve"), 1))
        .await;

    assert!(
        h.is_resolved(&system_id).await,
        "the system-level row resolves"
    );
    assert!(
        !h.is_resolved(&owned_id).await,
        "the session-owned row stays Pending"
    );
    assert_eq!(h.approval_resolved_count().await, 1, "exactly one resolve");
}
