//! Substantive happy-path test for `cellos-host-cellos`.
//!
//! Scope: end-to-end residue check exercising both adapters in this crate the
//! way the supervisor wires them — `ProprietaryCellBackend` + `MemorySecretBroker`.
//! For a single cell run we:
//!
//! 1. Create the cell via the host backend (asserts host-side tracking).
//! 2. Resolve N secrets for the cell via the broker (asserts in-memory rows
//!    are accumulated, scoped to that cell id).
//! 3. Destroy the cell via the host backend.
//! 4. Revoke broker rows for the cell.
//! 5. Assert post-teardown:
//!    - `tracked_cell_count() == 0` (no host-side residue)
//!    - `total_materialized_rows() == 0` (no broker-side residue)
//!
//! This is the residue contract that the supervisor + downstream auditors
//! depend on; the inline tests cover each adapter in isolation, this one
//! verifies they compose correctly through the public trait surface.

use cellos_core::ports::{CellBackend, SecretBroker};
use cellos_core::ExecutionCellDocument;
use cellos_host_cellos::{MemorySecretBroker, ProprietaryCellBackend};

fn happy_doc(id: &str) -> ExecutionCellDocument {
    serde_json::from_value(serde_json::json!({
        "apiVersion": "cellos.io/v1",
        "kind": "ExecutionCell",
        "spec": {
            "id": id,
            "authority": { "secretRefs": [] },
            "lifetime": { "ttlSeconds": 60 }
        }
    }))
    .expect("happy: spec must parse")
}

#[tokio::test]
async fn happy_path_create_resolve_destroy_revoke_leaves_no_residue() {
    let host = ProprietaryCellBackend::new();
    let broker = MemorySecretBroker::new();
    let cell_id = "hp-cell-cellos";

    // Pre-conditions: clean slate.
    assert_eq!(host.tracked_cell_count().await, 0);
    assert_eq!(broker.total_materialized_rows().await, 0);

    // (1) Create the cell.
    let doc = happy_doc(cell_id);
    let handle = host.create(&doc).await.expect("happy: host create");
    assert_eq!(handle.cell_id, cell_id);
    assert_eq!(host.tracked_cell_count().await, 1);
    assert!(host.has_tracked_state(cell_id).await);

    // (2) Resolve N secrets through the broker, all scoped to the same cell.
    let keys = ["DB_PASSWORD", "API_TOKEN", "SIGNING_KEY"];
    for key in &keys {
        let view = broker
            .resolve(key, cell_id, 60)
            .await
            .unwrap_or_else(|e| panic!("happy: broker resolve {key} must succeed: {e}"));
        assert_eq!(view.key, *key);
        // Simulated value format documented by MemorySecretBroker.
        assert!(
            view.value.as_str().contains(cell_id),
            "simulated value should reference the cell id, got {:?}",
            view.value.as_str()
        );
    }
    assert_eq!(
        broker.materialized_keys_for_cell(cell_id).await.len(),
        keys.len(),
        "all N broker rows must be tracked under the cell id"
    );
    assert_eq!(broker.total_materialized_rows().await, keys.len());

    // (3) Destroy the cell — host-side state must be removed.
    let report = host.destroy(&handle).await.expect("happy: host destroy");
    assert!(report.destroyed);
    assert_eq!(report.cell_id, cell_id);
    assert_eq!(report.peers_tracked_after, 0);

    // (4) Revoke broker rows for the cell.
    broker
        .revoke_for_cell(cell_id)
        .await
        .expect("happy: broker revoke");

    // (5) Final residue assertions — both surfaces must be empty.
    assert_eq!(
        host.tracked_cell_count().await,
        0,
        "no host-side residue after destroy"
    );
    assert!(
        !host.has_tracked_state(cell_id).await,
        "host must not retain state for the destroyed cell"
    );
    assert!(
        broker.materialized_keys_for_cell(cell_id).await.is_empty(),
        "no broker rows for the cell after revoke"
    );
    assert_eq!(
        broker.total_materialized_rows().await,
        0,
        "no broker-side residue across all cells after revoke"
    );
}
