//! Surface-tier propagation test for `download_stall_timeout` (peat-mesh#227).
//!
//! #227 adds `AutomergeBackendConfig::download_stall_timeout: Option<Duration>`
//! so a consumer (e.g. peat-node) can shrink the per-attempt blob-fetch stall
//! threshold for redundant-peer / dual-C2-failover deployments. The stall
//! *watchdog behaviour* itself is already covered deterministically by the
//! `drive_attempt_with_stall_watchdog` unit tests (`stalls_when_no_events`,
//! `live_transfer_never_stalls`, `chatty_but_byte_stuck_stream_stalls`).
//!
//! What those don't cover — and what the #227 QA review flagged as the last
//! gap — is the *wiring*: that a caller-supplied value actually reaches the
//! field `fetch_blob` reads, through the public surface a consumer hits
//! (`AutomergeBackend::with_iroh`), rather than being silently re-defaulted at
//! the wrong level. This test exercises both arms of the
//! `download_stall_timeout.unwrap_or(iroh_defaults.download_stall_timeout)`
//! wiring in `with_iroh` and asserts the blob store's effective threshold.
//! Combined with the watchdog unit tests (effective threshold → rotation
//! timing), this transitively pins config → behaviour without a network-timed
//! (flaky) assertion.

use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;
use std::time::Duration;

use peat_mesh::config::IrohConfig;
use peat_mesh::security::FormationKey;
use peat_mesh::sync::{AutomergeBackend, AutomergeBackendConfig};
use tempfile::TempDir;

/// Build a backend via the real `with_iroh` surface with the given
/// `download_stall_timeout`, bound to an OS-chosen IPv4 localhost port.
async fn build_backend(
    download_stall_timeout: Option<Duration>,
) -> (Arc<AutomergeBackend>, TempDir) {
    let dir = TempDir::new().expect("tempdir");
    let shared_key = FormationKey::generate_secret();
    let mut cfg = AutomergeBackendConfig::default();
    cfg.data_dir = dir.path().to_path_buf();
    cfg.formation_id = "stall-timeout-propagation".to_string();
    cfg.base64_shared_key = shared_key;
    cfg.iroh_bind_addr = Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0));
    cfg.download_stall_timeout = download_stall_timeout;
    let backend = AutomergeBackend::with_iroh(cfg).await.expect("with_iroh");
    (backend, dir)
}

/// `Some(d)` branch: a caller-supplied stall timeout reaches the blob store's
/// effective threshold (the field `fetch_blob`'s watchdog reads). This is the
/// branch #227 exists to ship and the one the QA review noted was never
/// exercised through the public surface.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn with_iroh_propagates_explicit_stall_timeout_to_blob_store() {
    let explicit = Duration::from_secs(1);
    let (backend, _dir) = build_backend(Some(explicit)).await;
    assert_eq!(
        backend.blob_store().download_stall_timeout(),
        explicit,
        "explicit download_stall_timeout must reach the blob store's fetch watchdog"
    );
}

/// `None` branch: omitting the override preserves the `IrohConfig` default
/// (30s), so existing callers are unaffected and the default isn't accidentally
/// dropped on the floor.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn with_iroh_defaults_stall_timeout_when_unset() {
    let (backend, _dir) = build_backend(None).await;
    assert_eq!(
        backend.blob_store().download_stall_timeout(),
        IrohConfig::default().download_stall_timeout,
        "None must fall back to the IrohConfig default stall timeout"
    );
}
