namespace ZB.MOM.WW.Overview.Polling;

/// <summary>
/// Holds the current <see cref="OverviewSnapshot"/> and notifies subscribers when it is replaced.
/// </summary>
/// <remarks>
/// This is the cache-first requirement made concrete: the poller is the only writer, and every
/// render reads whatever is already here. A page load therefore paints the last known state
/// immediately and never waits on — or triggers — a probe of its own.
/// </remarks>
public sealed class OverviewSnapshotStore
{
    private OverviewSnapshot _current = OverviewSnapshot.Empty;

    /// <summary>Raised after a new snapshot has been published.</summary>
    /// <remarks>
    /// Handlers run on the poller's thread, so a Blazor subscriber must marshal onto its own
    /// synchronization context (<c>InvokeAsync(StateHasChanged)</c>) and must unsubscribe when
    /// disposed.
    /// </remarks>
    public event Action<OverviewSnapshot>? Changed;

    /// <summary>The current snapshot; never null.</summary>
    public OverviewSnapshot Current => Volatile.Read(ref _current);

    /// <summary>Replaces the current snapshot and notifies subscribers.</summary>
    /// <param name="snapshot">The new snapshot.</param>
    public void Publish(OverviewSnapshot snapshot)
    {
        ArgumentNullException.ThrowIfNull(snapshot);

        // A single reference swap of an already-complete graph. Readers hold their own reference
        // for the duration of a render, so a sweep landing mid-render cannot tear the view.
        Volatile.Write(ref _current, snapshot);

        Changed?.Invoke(snapshot);
    }
}
