// Copyright 2018-2026 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/accounts.go (statz) and server/events.go in the NATS server Go source.

namespace ZB.MOM.NatsNet.Server;

// ============================================================================
// AccountTrafficStats — per-kind traffic counters for an account
// Mirrors the anonymous embedded `stats` struct in Go's Account.stats.
// ============================================================================

/// <summary>
/// Per-kind (total, gateway, route, leaf) message and byte traffic counters
/// for an account. Protected by the owning <see cref="AccountTrafficStatSet"/> lock.
/// Mirrors Go <c>stats</c> struct (the small one embedded in Account.stats).
/// </summary>
internal sealed class AccountTrafficStats
{
    public long InMsgs;
    public long OutMsgs;
    public long InBytes;
    public long OutBytes;
    public long SlowConsumers;
}

/// <summary>
/// Aggregated traffic statistics for an account, including breakdowns by
/// gateway, route, and leaf-node traffic.
/// Mirrors Go's embedded anonymous <c>stats struct</c> in <c>Account</c>.
/// </summary>
internal sealed class AccountTrafficStatSet
{
    private readonly object _mu = new();

    public AccountTrafficStats Total    { get; } = new();
    public AccountTrafficStats Gateways { get; } = new();
    public AccountTrafficStats Routes   { get; } = new();
    public AccountTrafficStats Leafs    { get; } = new();

    public void Lock()   => Monitor.Enter(_mu);
    public void Unlock() => Monitor.Exit(_mu);
}

// ============================================================================
// Account partial — events / statz
// ============================================================================

public sealed partial class Account
{
    // -------------------------------------------------------------------------
    // Traffic statistics (mirrors Go Account.stats embedded struct)
    // -------------------------------------------------------------------------

    /// <summary>
    /// Aggregated account traffic statistics.
    /// Mirrors Go's <c>stats struct { sync.Mutex; stats; gw stats; rt stats; ln stats }</c>
    /// embedded in <c>Account</c>.
    /// </summary>
    internal readonly AccountTrafficStatSet TrafficStats = new();

    // -------------------------------------------------------------------------
    // Group F: Account.statz
    // -------------------------------------------------------------------------

    /// <summary>
    /// Computes and returns a snapshot of the account's current statistics.
    /// Lock (account _mu write lock) should be held on entry.
    /// Mirrors Go <c>(a *Account) statz() *AccountStat</c> in server/events.go.
    /// </summary>
    internal AccountStat Statz()
    {
        var localConns = NumLocalConnectionsLocked();
        var leafConns  = NumLocalLeafNodes();

        TrafficStats.Lock();
        var received = new DataStats
        {
            Msgs  = TrafficStats.Total.InMsgs,
            Bytes = TrafficStats.Total.InBytes,
            Gateways = new MsgBytes
            {
                Msgs  = TrafficStats.Gateways.InMsgs,
                Bytes = TrafficStats.Gateways.InBytes,
            },
            Routes = new MsgBytes
            {
                Msgs  = TrafficStats.Routes.InMsgs,
                Bytes = TrafficStats.Routes.InBytes,
            },
            Leafs = new MsgBytes
            {
                Msgs  = TrafficStats.Leafs.InMsgs,
                Bytes = TrafficStats.Leafs.InBytes,
            },
        };
        var sent = new DataStats
        {
            Msgs  = TrafficStats.Total.OutMsgs,
            Bytes = TrafficStats.Total.OutBytes,
            Gateways = new MsgBytes
            {
                Msgs  = TrafficStats.Gateways.OutMsgs,
                Bytes = TrafficStats.Gateways.OutBytes,
            },
            Routes = new MsgBytes
            {
                Msgs  = TrafficStats.Routes.OutMsgs,
                Bytes = TrafficStats.Routes.OutBytes,
            },
            Leafs = new MsgBytes
            {
                Msgs  = TrafficStats.Leafs.OutMsgs,
                Bytes = TrafficStats.Leafs.OutBytes,
            },
        };
        var slowConsumers = TrafficStats.Total.SlowConsumers;
        TrafficStats.Unlock();

        return new AccountStat
        {
            Account       = Name,
            Name          = GetNameTagLocked(),
            Conns         = localConns,
            LeafNodes     = leafConns,
            TotalConns    = localConns + leafConns,
            NumSubs       = Sublist?.Count() ?? 0,
            Received      = received,
            Sent          = sent,
            SlowConsumers = slowConsumers,
        };
    }
}
