namespace ZB.MOM.WW.Overview.Observability;

/// <summary>
/// When the poller last finished a full sweep. Written by the poller, read by
/// <see cref="PollCycleHealthCheck"/>.
/// </summary>
/// <remarks>
/// Deliberately not derived from the published snapshot's <c>GeneratedUtc</c>: the poller publishes
/// the registry once before it probes anything, so a snapshot exists from the moment the host
/// starts. A stuck poller would therefore look like a working one. This records only sweeps that
/// actually completed.
/// </remarks>
public sealed class PollCycleHeartbeat
{
    private long _lastCompletedTicks;

    /// <summary>Initializes the heartbeat and stamps the host start time.</summary>
    /// <param name="timeProvider">Clock.</param>
    public PollCycleHeartbeat(TimeProvider timeProvider)
    {
        ArgumentNullException.ThrowIfNull(timeProvider);
        StartedUtc = timeProvider.GetUtcNow();
    }

    /// <summary>When this heartbeat was constructed — effectively when the host started.</summary>
    /// <remarks>
    /// Lets the health check distinguish "started a moment ago and has not swept yet" from "has
    /// not swept in minutes", which are the same null reading but opposite conclusions.
    /// </remarks>
    public DateTimeOffset StartedUtc { get; }

    /// <summary>When the last full sweep completed, or null if none has.</summary>
    public DateTimeOffset? LastCompletedUtc
    {
        get
        {
            var ticks = Interlocked.Read(ref _lastCompletedTicks);
            return ticks == 0 ? null : new DateTimeOffset(ticks, TimeSpan.Zero);
        }
    }

    /// <summary>Records that a sweep completed.</summary>
    /// <param name="at">When it completed.</param>
    public void RecordCycleCompleted(DateTimeOffset at) =>
        Interlocked.Exchange(ref _lastCompletedTicks, at.UtcTicks);
}
