use std::collections::HashSet;
use std::time::Duration;
use nostr_sdk::prelude::*;
pub async fn reconcile_missing(
filter: Filter,
local_items: Vec<(EventId, Timestamp)>,
timeout: Duration,
) -> Result<HashSet<EventId>, String> {
use futures_util::stream::{FuturesUnordered, StreamExt};
let client = crate::state::nostr_client().ok_or("Nostr client not initialized")?;
let opts = SyncOptions::new()
.direction(SyncDirection::Down)
.initial_timeout(timeout)
.dry_run();
let relay_map = client.relays().await;
let trusted = crate::state::active_trusted_relays().await;
let relays: Vec<(String, Relay)> = trusted.iter().filter_map(|url| {
let normalized = url.trim_end_matches('/');
relay_map.iter()
.find(|(u, _)| u.as_str().trim_end_matches('/') == normalized)
.map(|(_, r)| (url.to_string(), r.clone()))
}).collect();
drop(relay_map);
if relays.is_empty() {
crate::log_warn!("[Negentropy] No trusted relays available for reconciliation");
return Ok(HashSet::new());
}
let mut futs = FuturesUnordered::new();
for (url, relay) in &relays {
let url = url.clone();
let relay = relay.clone();
let f = filter.clone();
let items = local_items.clone();
let o = opts.clone();
futs.push(async move {
let r = tokio::time::timeout(timeout, relay.sync_with_items(f, items, &o)).await;
(url, r)
});
}
let mut missing: HashSet<EventId> = HashSet::new();
while let Some((url, result)) = futs.next().await {
match result {
Ok(Ok(recon)) => {
let n = recon.remote.len();
missing.extend(recon.remote);
crate::log_debug!("[Negentropy] {} reconciled: {} missing", url, n);
}
Ok(Err(e)) => crate::log_warn!("[Negentropy] {} failed: {}", url, e),
Err(_) => crate::log_warn!("[Negentropy] {} timed out", url),
}
}
Ok(missing)
}