//! Reusable sub-trait contract tests.
//!
//! Pass any `&dyn ArchiveBackend` to verify it honors the behavioral
//! contract of each sub-trait it exposes. Phase 2 runs this against
//! `JsonlBackend`; Phase 3+ reuses it for every new backend.

#![cfg(any(test, feature = "test-utils"))]

use uuid::Uuid;

use crate::backend::ArchiveBackend;

/// Exercises `ConnectorRegistryBackend` through `as_connector_registry()`.
/// Skips silently if the backend does not expose the sub-trait.
pub async fn verify_connector_registry_contract(backend: &dyn ArchiveBackend) {
    let Some(registry) = backend.as_connector_registry() else {
        return;
    };

    // Empty state — listing returns Vec::new(), not an error.
    let list = registry.list_connectors().await.expect("list_connectors");
    assert!(list.is_empty(), "fresh backend should have no connectors");

    // get_connector on missing UID returns Ok(None).
    let missing = registry
        .get_connector(Uuid::new_v4())
        .await
        .expect("get_connector");
    assert!(missing.is_none());

    // resolve_connector_uid on unknown id returns Ok(None).
    let unresolved = registry
        .resolve_connector_uid("nonexistent@host")
        .await
        .expect("resolve_connector_uid");
    assert!(unresolved.is_none());
}

/// Exercises `SessionMappingBackend`.
pub async fn verify_session_mapping_contract(backend: &dyn ArchiveBackend) {
    let Some(mapping) = backend.as_session_mapping() else {
        return;
    };

    let missing = mapping
        .get_mapping(Uuid::new_v4(), "absent")
        .await
        .expect("get_mapping");
    assert!(missing.is_none());

    let owner = mapping
        .find_owner("absent")
        .await
        .expect("find_owner");
    assert!(owner.is_none());
}

/// Exercises `DagBackend`.
pub async fn verify_dag_contract(backend: &dyn ArchiveBackend) {
    let Some(dag) = backend.as_dag() else {
        return;
    };

    let children = dag
        .get_children(Uuid::new_v4())
        .await
        .expect("get_children");
    assert!(children.is_empty());

    let edges = dag
        .get_dag_edges(Uuid::new_v4())
        .await
        .expect("get_dag_edges");
    assert!(edges.is_empty());
}

/// Exercises `MetaEventsBackend`.
pub async fn verify_meta_events_contract(backend: &dyn ArchiveBackend) {
    let Some(meta) = backend.as_meta_events() else {
        return;
    };

    let events = meta
        .get_meta_events(Uuid::new_v4())
        .await
        .expect("get_meta_events");
    assert!(events.is_empty());

    let by_client = meta
        .find_meta_session_by_client("absent")
        .await
        .expect("find_meta_session_by_client");
    assert!(by_client.is_none());

    let all = meta
        .list_meta_sessions()
        .await
        .expect("list_meta_sessions");
    assert!(all.is_empty());
}

/// One-shot helper: runs every sub-trait contract whose capability is present.
pub async fn verify_all_contracts(backend: &dyn ArchiveBackend) {
    verify_connector_registry_contract(backend).await;
    verify_session_mapping_contract(backend).await;
    verify_dag_contract(backend).await;
    verify_meta_events_contract(backend).await;
}
