namespace NATS.Server.Monitoring;

/// <summary>
/// Fixed-size ring buffer for tracking recently closed client connections.
/// When full, oldest entries are overwritten. Thread-safe via Lock.
/// Go reference: server.go closedClients / ClosedState tracking.
/// </summary>
public sealed class ClosedConnectionRingBuffer
{
    private readonly ClosedClient[] _buffer;
    private readonly Lock _lock = new();
    private int _head;        // Next write position
    private int _count;       // Current count (up to capacity)
    private long _totalClosed; // Running total of all closed connections ever

    public ClosedConnectionRingBuffer(int capacity = 1024)
    {
        if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be greater than zero.");
        _buffer = new ClosedClient[capacity];
    }

    public int Capacity => _buffer.Length;

    public int Count
    {
        get { lock (_lock) return _count; }
    }

    public long TotalClosed
    {
        get { lock (_lock) return _totalClosed; }
    }

    /// <summary>
    /// Adds a closed connection snapshot. If the buffer is full the oldest entry is overwritten.
    /// </summary>
    public void Add(ClosedClient info)
    {
        lock (_lock)
        {
            _buffer[_head] = info;
            _head = (_head + 1) % _buffer.Length;
            if (_count < _buffer.Length)
                _count++;
            _totalClosed++;
        }
    }

    /// <summary>
    /// Returns a snapshot of all entries ordered newest-first.
    /// </summary>
    public IReadOnlyList<ClosedClient> GetAll()
    {
        lock (_lock)
        {
            return BuildSnapshot(_count);
        }
    }

    /// <summary>
    /// Returns up to <paramref name="count"/> most recent entries, ordered newest-first.
    /// </summary>
    public IReadOnlyList<ClosedClient> GetRecent(int count)
    {
        lock (_lock)
        {
            var take = Math.Min(count, _count);
            return BuildSnapshot(take);
        }
    }

    /// <summary>
    /// Clears all entries and resets counters.
    /// </summary>
    public void Clear()
    {
        lock (_lock)
        {
            Array.Clear(_buffer, 0, _buffer.Length);
            _head = 0;
            _count = 0;
            // TotalClosed is intentionally not reset — it is a running lifetime total.
        }
    }

    // Must be called while _lock is held.
    // Returns the 'take' most recent entries, newest-first.
    private ClosedClient[] BuildSnapshot(int take)
    {
        if (take == 0) return [];

        var result = new ClosedClient[take];
        var capacity = _buffer.Length;

        // _head points to the next write slot, so (_head - 1) is the most recent entry.
        for (var i = 0; i < take; i++)
        {
            var idx = ((_head - 1 - i) % capacity + capacity) % capacity;
            result[i] = _buffer[idx];
        }

        return result;
    }
}
