namespace ZB.MOM.WW.LocalDb.Hlc;

/// <summary>64-bit HLC: (UTC unix-ms &lt;&lt; 16) | 16-bit logical counter. Thread-safe.</summary>
public sealed class HybridLogicalClock
{
    private readonly Func<long> _utcNowMs;
    private readonly Lock _lock = new();
    private long _last;

    public HybridLogicalClock(long initialValue = 0, Func<long>? utcNowMs = null)
    {
        _utcNowMs = utcNowMs ?? (() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
        _last = initialValue;
    }

    /// <summary>Returns the next monotonically increasing HLC stamp: the current UTC physical time when it advances the clock, otherwise the last value with its logical counter incremented by one.</summary>
    public long Next()
    {
        lock (_lock)
        {
            var candidate = _utcNowMs() << 16;
            _last = candidate > _last ? candidate : _last + 1; // +1 rolls counter; overflow naturally carries into physical bits
            return _last;
        }
    }

    /// <summary>Merge a peer's HLC so our next stamp orders after everything we've seen.</summary>
    public void Observe(long remote) { lock (_lock) { if (remote > _last) _last = remote; } }

    /// <summary>The most recently issued/observed HLC value, without advancing the clock.</summary>
    public long Current { get { lock (_lock) return _last; } }

    /// <summary>Extracts the physical component (UTC Unix milliseconds) from a packed HLC value.</summary>
    public static long PhysicalMs(long hlc) => hlc >> 16;

    /// <summary>Extracts the 16-bit logical counter from a packed HLC value.</summary>
    public static int Counter(long hlc) => (int)(hlc & 0xFFFF);
}
