// Copyright 2018-2025 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Adapted from server/ring.go in the NATS server Go source.

namespace ZB.MOM.NatsNet.Server.Internal;

// -------------------------------------------------------------------------
// Placeholder types — will be populated in session 08 (Client) and
// session 12 (Monitor/Events).
// -------------------------------------------------------------------------

/// <summary>
/// Subset of client connection info exposed on the monitoring endpoint (/connz).
/// Full implementation is in session 12 (monitor.go).
/// </summary>
public class ConnInfo { }

/// <summary>
/// Subscription detail for the monitoring endpoint.
/// Full implementation is in session 12 (monitor.go).
/// </summary>
public class SubDetail { }

// -------------------------------------------------------------------------
// ClosedClient / ClosedRingBuffer (ring.go)
// -------------------------------------------------------------------------

/// <summary>
/// Wraps connection info with optional items for the /connz endpoint.
/// Mirrors <c>closedClient</c> in server/ring.go.
/// </summary>
public sealed class ClosedClient
{
    public ConnInfo Info { get; init; } = new();
    public IReadOnlyList<SubDetail> Subs { get; init; } = [];
    public string User { get; init; } = string.Empty;
    public string Account { get; init; } = string.Empty;
}

/// <summary>
/// Fixed-size ring buffer that retains the most recent closed connections,
/// evicting the oldest when full.
/// Mirrors <c>closedRingBuffer</c> in server/ring.go.
/// </summary>
public sealed class ClosedRingBuffer
{
    private ulong _total;
    private readonly ClosedClient?[] _conns;

    /// <summary>Creates a ring buffer that holds at most <paramref name="max"/> entries.</summary>
    public ClosedRingBuffer(int max)
    {
        _conns = new ClosedClient?[max];
    }

    /// <summary>
    /// Appends a closed connection, evicting the oldest if the buffer is full.
    /// Mirrors <c>closedRingBuffer.append</c>.
    /// </summary>
    public void Append(ClosedClient cc)
    {
        _conns[Next()] = cc;
        _total++;
    }

    // Index of the slot to write next — wraps around.
    private int Next() => (int)(_total % (ulong)_conns.Length);

    /// <summary>
    /// Returns the number of entries currently stored (≤ capacity).
    /// Mirrors <c>closedRingBuffer.len</c>.
    /// </summary>
    public int Len() =>
        _total > (ulong)_conns.Length ? _conns.Length : (int)_total;

    /// <summary>
    /// Returns the total number of connections ever appended (not capped).
    /// Mirrors <c>closedRingBuffer.totalConns</c>.
    /// </summary>
    public ulong TotalConns() => _total;

    /// <summary>
    /// Returns a chronologically ordered copy of the stored closed connections.
    /// The caller may freely modify the returned array.
    /// Mirrors <c>closedRingBuffer.closedClients</c>.
    /// </summary>
    public ClosedClient?[] ClosedClients()
    {
        var len = Len();
        var dup = new ClosedClient?[len];
        var head = Next();

        if (_total <= (ulong)_conns.Length || head == 0)
        {
            Array.Copy(_conns, dup, len);
        }
        else
        {
            var fp = _conns.AsSpan(head);   // oldest … end of array
            var sp = _conns.AsSpan(0, head); // wrap-around … newest
            fp.CopyTo(dup.AsSpan());
            sp.CopyTo(dup.AsSpan(fp.Length));
        }

        return dup;
    }
}
