using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming;
namespace ZB.MOM.WW.ScadaBridge.Communication;
///
/// Central, transient, in-memory per-site live alarm cache (plan #10, Task 4).
/// A DI singleton shared by every Alarm Summary Blazor circuit on the active central
/// node. For each site with at least one active viewer it runs ONE shared per-site
/// aggregator that seeds from the existing debug-snapshot fan-out and stays warm on a
/// single site-wide, alarm-only gRPC stream (SubscribeSite), so the page
/// reflects alarm transitions in near-real-time instead of re-polling every 15s.
///
/// Hard constraint (locked [PERM]): there is NO persisted central alarm
/// store. This cache is purely in-memory, per-node — no EF entity/table/migration backs
/// it. In routine operation only the active node (behind Traefik) hosts viewers, so only
/// it runs aggregators; browsing the standby node directly (diagnostic ports) starts an
/// independent read-only aggregator there — accepted (arch review 02 round 2, N8):
/// bounded (one stream/site, viewer-capped), read-only, and torn down by the viewer
/// linger. On a NodeA↔NodeB failover the new active node re-seeds from scratch.
///
///
/// Reference-counted lifecycle: the first for a site
/// starts its aggregator (open stream + snapshot seed); the last subscriber leaving
/// stops it after a short linger (see
/// ) to avoid re-seed thrash.
/// One gRPC stream per site, not per browser circuit.
///
///
public interface ISiteAlarmLiveCache
{
///
/// Registers a viewer for a site's live alarm feed and returns an
/// that unregisters it. The first subscriber for a site
/// starts the shared aggregator; the last subscriber's
/// schedules a linger-delayed stop. is raised (on the
/// aggregator's thread) after each applied change — the handler must not block and
/// should marshal any UI work onto its own dispatcher (mirrors
/// IDeploymentStatusNotifier). Disposing the returned handle is idempotent.
///
/// The numeric site id whose alarms the viewer wants.
/// Callback invoked whenever the site's cached alarm set changes.
/// A disposable that unregisters this viewer (idempotent).
IDisposable Subscribe(int siteId, Action onChanged);
///
/// Returns the current in-memory alarm snapshot for a site — exactly what the page
/// should render. Empty when the site has no aggregator or has not yet completed its
/// first seed. Safe to call from Blazor render threads: it returns an immutable
/// point-in-time list (the aggregator publishes fresh immutable lists; readers never
/// see a partially-mutated collection).
///
/// The numeric site id.
/// The current cached alarms (possibly empty), never null.
IReadOnlyList GetCurrentAlarms(int siteId);
///
/// Whether the site's aggregator has completed its initial seed and is serving live
/// state. The page can use this to decide whether to keep its 15s poll fallback
/// (Task 5): false means "not live yet — keep polling"; true means the
/// cache is authoritative.
///
/// The numeric site id.
/// true once the site's aggregator has seeded and published at least once.
bool IsLive(int siteId);
}