// 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/events.go in the NATS server Go source.

using System.Text.Json.Serialization;
using ZB.MOM.NatsNet.Server.Auth.CertificateIdentityProvider;
using ZB.MOM.NatsNet.Server.Internal;

namespace ZB.MOM.NatsNet.Server;

// ============================================================================
// System subject constants
// Mirrors the const block at the top of server/events.go.
// ============================================================================

/// <summary>
/// System-account subject templates and constants used for internal NATS server
/// event routing. All format-string fields use <see cref="string.Format"/> with
/// the appropriate server/account ID substituted at call time.
/// Mirrors the const block in server/events.go.
/// </summary>
public static class SystemSubjects
{
    // Account lookup / claims
    public const string AccLookupReqSubj   = "$SYS.REQ.ACCOUNT.{0}.CLAIMS.LOOKUP";
    public const string AccPackReqSubj     = "$SYS.REQ.CLAIMS.PACK";
    public const string AccListReqSubj     = "$SYS.REQ.CLAIMS.LIST";
    public const string AccClaimsReqSubj   = "$SYS.REQ.CLAIMS.UPDATE";
    public const string AccDeleteReqSubj   = "$SYS.REQ.CLAIMS.DELETE";

    // Connection events
    public const string ConnectEventSubj    = "$SYS.ACCOUNT.{0}.CONNECT";
    public const string DisconnectEventSubj = "$SYS.ACCOUNT.{0}.DISCONNECT";

    // Direct request routing
    public const string AccDirectReqSubj    = "$SYS.REQ.ACCOUNT.{0}.{1}";
    public const string AccPingReqSubj      = "$SYS.REQ.ACCOUNT.PING.{0}";

    // Account update events (both old and new forms kept for backward compatibility)
    public const string AccUpdateEventSubjOld = "$SYS.ACCOUNT.{0}.CLAIMS.UPDATE";
    public const string AccUpdateEventSubjNew = "$SYS.REQ.ACCOUNT.{0}.CLAIMS.UPDATE";

    public const string ConnsRespSubj        = "$SYS._INBOX_.{0}";
    public const string AccConnsEventSubjNew = "$SYS.ACCOUNT.{0}.SERVER.CONNS";
    public const string AccConnsEventSubjOld = "$SYS.SERVER.ACCOUNT.{0}.CONNS"; // backward compat

    // Server lifecycle events
    public const string LameDuckEventSubj  = "$SYS.SERVER.{0}.LAMEDUCK";
    public const string ShutdownEventSubj  = "$SYS.SERVER.{0}.SHUTDOWN";

    // Client control
    public const string ClientKickReqSubj = "$SYS.REQ.SERVER.{0}.KICK";
    public const string ClientLdmReqSubj  = "$SYS.REQ.SERVER.{0}.LDM";

    // Auth error events
    public const string AuthErrorEventSubj        = "$SYS.SERVER.{0}.CLIENT.AUTH.ERR";
    public const string AuthErrorAccountEventSubj = "$SYS.ACCOUNT.CLIENT.AUTH.ERR";

    // Stats
    public const string ServerStatsSubj         = "$SYS.SERVER.{0}.STATSZ";
    public const string ServerDirectReqSubj     = "$SYS.REQ.SERVER.{0}.{1}";
    public const string ServerPingReqSubj       = "$SYS.REQ.SERVER.PING.{0}";
    public const string ServerStatsPingReqSubj  = "$SYS.REQ.SERVER.PING"; // deprecated; use STATSZ variant
    public const string ServerReloadReqSubj     = "$SYS.REQ.SERVER.{0}.RELOAD";

    // Leaf node
    public const string LeafNodeConnectEventSubj = "$SYS.ACCOUNT.{0}.LEAFNODE.CONNECT"; // internal only

    // Latency
    public const string RemoteLatencyEventSubj = "$SYS.LATENCY.M2.{0}";
    public const string InboxRespSubj          = "$SYS._INBOX.{0}.{1}";

    // User info
    public const string UserDirectInfoSubj = "$SYS.REQ.USER.INFO";
    public const string UserDirectReqSubj  = "$SYS.REQ.USER.{0}.INFO";

    // Subscription count
    public const string AccNumSubsReqSubj = "$SYS.REQ.ACCOUNT.NSUBS";

    // Debug
    public const string AccSubsSubj = "$SYS.DEBUG.SUBSCRIBERS";

    // OCSP peer events
    public const string OcspPeerRejectEventSubj           = "$SYS.SERVER.{0}.OCSP.PEER.CONN.REJECT";
    public const string OcspPeerChainlinkInvalidEventSubj = "$SYS.SERVER.{0}.OCSP.PEER.LINK.INVALID";

    // Parsing constants (token indexes / counts)
    public const int AccLookupReqTokens  = 6;
    public const int ShutdownEventTokens = 4;
    public const int ServerSubjectIndex  = 2;
    public const int AccUpdateTokensNew  = 6;
    public const int AccUpdateTokensOld  = 5;
    public const int AccUpdateAccIdxOld  = 2;
    public const int AccReqTokens        = 5;
    public const int AccReqAccIndex      = 3;
}

// ============================================================================
// Advisory message type schema URI constants
// Mirrors the const string variables near each struct in server/events.go.
// ============================================================================

public static class EventMsgTypes
{
    public const string ConnectEventMsgType                    = "io.nats.server.advisory.v1.client_connect";
    public const string DisconnectEventMsgType                 = "io.nats.server.advisory.v1.client_disconnect";
    public const string OcspPeerRejectEventMsgType             = "io.nats.server.advisory.v1.ocsp_peer_reject";
    public const string OcspPeerChainlinkInvalidEventMsgType   = "io.nats.server.advisory.v1.ocsp_peer_link_invalid";
    public const string AccountNumConnsMsgType                 = "io.nats.server.advisory.v1.account_connections";
}

// ============================================================================
// Heartbeat / rate-limit intervals (mirrors package-level vars in events.go)
// ============================================================================

/// <summary>
/// Default timing constants for server event heartbeats and rate limiting.
/// Mirrors Go package-level <c>var</c> declarations in events.go.
/// </summary>
public static class EventIntervals
{
    /// <summary>Default HB interval for events. Mirrors Go <c>eventsHBInterval = 30s</c>.</summary>
    public static readonly TimeSpan EventsHbInterval   = TimeSpan.FromSeconds(30);

    /// <summary>Default HB interval for stats. Mirrors Go <c>statsHBInterval = 10s</c>.</summary>
    public static readonly TimeSpan StatsHbInterval    = TimeSpan.FromSeconds(10);

    /// <summary>Minimum interval between statsz publishes. Mirrors Go <c>defaultStatszRateLimit = 1s</c>.</summary>
    public static readonly TimeSpan DefaultStatszRateLimit = TimeSpan.FromSeconds(1);
}

// ============================================================================
// SysMsgHandler — delegate for internal system message dispatch
// Mirrors Go <c>sysMsgHandler</c> func type in events.go.
// ============================================================================

/// <summary>
/// Callback invoked when an internal system message is dispatched.
/// Mirrors Go <c>sysMsgHandler</c> in server/events.go.
/// </summary>
public delegate void SysMsgHandler(
    Subscription sub,
    ClientConnection client,
    Account acc,
    string subject,
    string reply,
    byte[] hdr,
    byte[] msg);

// ============================================================================
// MsgHandler — subscription message callback delegate
// Mirrors Go <c>msgHandler</c> func type in server/events.go.
// ============================================================================

/// <summary>
/// Callback for a subscription message. Identical signature to
/// <see cref="SysMsgHandler"/>; the distinction exists in Go but is
/// collapsed here since both carry the same parameters.
/// Mirrors Go <c>msgHandler</c> in server/events.go.
/// </summary>
public delegate void MsgHandler(
    Subscription sub,
    ClientConnection client,
    Account acc,
    string subject,
    string reply,
    byte[] hdr,
    byte[] msg);

// ============================================================================
// ServerApiError — error payload for server API responses
// Mirrors Go <c>ApiError</c> used in server/events.go responses.
// ============================================================================

/// <summary>
/// Error payload returned in <see cref="ServerApiResponse"/> when a
/// monitoring z-endpoint request fails.
/// Mirrors Go <c>ApiError</c> struct used by server API responses.
/// </summary>
public sealed class ServerApiError
{
    [System.Text.Json.Serialization.JsonPropertyName("code")]
    public int Code { get; set; }

    [System.Text.Json.Serialization.JsonPropertyName("description")]
    public string Description { get; set; } = string.Empty;
}

// ============================================================================
// ServerApiResponse — wrapper for server API (z-endpoint) responses
// Mirrors Go <c>ServerAPIResponse</c> in server/events.go.
// ============================================================================

/// <summary>
/// Standard envelope returned by server monitoring API (varz, connz, etc.)
/// published via the internal system bus.
/// Mirrors Go <c>ServerAPIResponse</c> in server/events.go.
/// </summary>
public sealed class ServerApiResponse
{
    [System.Text.Json.Serialization.JsonPropertyName("server")]
    public ServerInfo? Server { get; set; }

    [System.Text.Json.Serialization.JsonPropertyName("data")]
    [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public object? Data { get; set; }

    [System.Text.Json.Serialization.JsonPropertyName("error")]
    [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public ServerApiError? Error { get; set; }
}

// ============================================================================
// EventFilterOptions — server filter options for z-endpoint requests
// Mirrors Go <c>EventFilterOptions</c> in server/events.go.
// ============================================================================

/// <summary>
/// Filter parameters sent in monitoring z-endpoint request messages that
/// allow targeting a specific server, cluster, or set of tags.
/// Mirrors Go <c>EventFilterOptions</c> in server/events.go.
/// </summary>
public class EventFilterOptions
{
    [System.Text.Json.Serialization.JsonPropertyName("server_name")]
    [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public string? Name { get; set; }

    [System.Text.Json.Serialization.JsonPropertyName("cluster")]
    [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public string? Cluster { get; set; }

    [System.Text.Json.Serialization.JsonPropertyName("host")]
    [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public string? Host { get; set; }

    [System.Text.Json.Serialization.JsonPropertyName("domain")]
    [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public string? Domain { get; set; }

    [System.Text.Json.Serialization.JsonPropertyName("tags")]
    [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public List<string>? Tags { get; set; }

    /// <summary>
    /// When true, name/cluster/host must match exactly; when false, substring
    /// matching is used.
    /// Mirrors Go <c>EventFilterOptions.ExactMatch</c>.
    /// </summary>
    [System.Text.Json.Serialization.JsonPropertyName("exact")]
    public bool ExactMatch { get; set; }
}

// ============================================================================
// UserInfo — user info response payload for $SYS.REQ.USER.INFO
// Mirrors Go <c>UserInfo</c> struct in server/events.go.
// ============================================================================

/// <summary>
/// Response payload returned by the <c>$SYS.REQ.USER.INFO</c> endpoint.
/// Contains the authenticated user's identity, account, permissions, and
/// claim expiry.
/// Mirrors Go <c>UserInfo</c> struct in server/events.go.
/// </summary>
public sealed class UserInfo
{
    [System.Text.Json.Serialization.JsonPropertyName("userId")]
    public string UserId { get; set; } = string.Empty;

    [System.Text.Json.Serialization.JsonPropertyName("account")]
    public string Account { get; set; } = string.Empty;

    [System.Text.Json.Serialization.JsonPropertyName("permissions")]
    [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public object? Permissions { get; set; }

    [System.Text.Json.Serialization.JsonPropertyName("expires")]
    [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]
    public DateTime Expires { get; set; }
}

// ============================================================================
// KickClientReq / LdmClientReq — client control request payloads
// Mirrors Go anonymous structs used in server/events.go.
// ============================================================================

/// <summary>
/// Request payload for <c>$SYS.REQ.SERVER.{id}.KICK</c>, which asks this
/// server to forcefully disconnect the client with the given CID.
/// Mirrors the anonymous struct used in <c>Server.kickClient()</c>.
/// </summary>
internal sealed class KickClientReq
{
    [System.Text.Json.Serialization.JsonPropertyName("cid")]
    public ulong Cid { get; set; }
}

/// <summary>
/// Request payload for <c>$SYS.REQ.SERVER.{id}.LDM</c>, which asks this
/// server to put the client with the given CID into lame-duck mode.
/// Mirrors the anonymous struct used in <c>Server.ldmClient()</c>.
/// </summary>
internal sealed class LdmClientReq
{
    [System.Text.Json.Serialization.JsonPropertyName("cid")]
    public ulong Cid { get; set; }
}

// ============================================================================
// StatszEventOptions — options for statsz z-endpoint requests
// Mirrors Go <c>StatszEventOptions</c> in server/events.go.
// ============================================================================

/// <summary>
/// Options embedded in z-endpoint request messages that allow filtering and
/// configuring the statsz response.
/// Mirrors Go <c>StatszEventOptions</c> in server/events.go.
/// </summary>
public sealed class StatszEventOptions : EventFilterOptions
{
}

// ============================================================================
// AccNumSubsReq — request payload for subscription count queries
// Mirrors Go <c>accNumSubsReq</c> struct in server/events.go.
// ============================================================================

/// <summary>
/// Payload for <c>$SYS.REQ.ACCOUNT.NSUBS</c> requests, which ask a remote
/// server how many local subscriptions exist for a given account + subject.
/// Mirrors Go <c>accNumSubsReq</c> in server/events.go.
/// </summary>
internal sealed class AccNumSubsReq
{
    [System.Text.Json.Serialization.JsonPropertyName("server")]
    public ServerInfo Server { get; set; } = new();

    [System.Text.Json.Serialization.JsonPropertyName("acc")]
    public string Account { get; set; } = string.Empty;

    [System.Text.Json.Serialization.JsonPropertyName("subject")]
    public string Subject { get; set; } = string.Empty;

    [System.Text.Json.Serialization.JsonPropertyName("queue")]
    [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public string? Queue { get; set; }
}

// ============================================================================
// InSysMsg — queued internal system message
// Mirrors Go <c>inSysMsg</c> struct in server/events.go.
// ============================================================================

/// <summary>
/// Holds a system message queued for internal delivery, avoiding the
/// route/gateway path.
/// Mirrors Go <c>inSysMsg</c> struct in server/events.go.
/// </summary>
internal sealed class InSysMsg
{
    public Subscription?       Sub     { get; set; }
    public ClientConnection?   Client  { get; set; }
    public Account?            Acc     { get; set; }
    public string         Subject { get; set; } = string.Empty;
    public string         Reply   { get; set; } = string.Empty;
    public byte[]?        Hdr     { get; set; }
    public byte[]?        Msg     { get; set; }
    public SysMsgHandler? Cb      { get; set; }
}

// ============================================================================
// InternalState — server internal/sys state
// Mirrors Go <c>internal</c> struct in server/events.go.
// Uses Monitor lock (lock(this)) in place of Go's embedded sync.Mutex.
// ============================================================================

/// <summary>
/// Holds all internal state used by the server's system-account event
/// machinery: account reference, client, send/receive queues, timers,
/// reply handlers, and heartbeat configuration.
/// Mirrors Go <c>internal</c> struct in server/events.go.
/// </summary>
internal sealed class InternalState
{
    // ---- identity / sequencing ----
    public Account?            Account    { get; set; }
    public ClientConnection?   Client     { get; set; }
    public long                Seq;       // accessed via Interlocked.Increment
    public int                 Sid        { get; set; }

    // ---- remote server tracking ----
    /// <summary>Map of server ID → serverUpdate. Mirrors Go <c>servers map[string]*serverUpdate</c>.</summary>
    public Dictionary<string, ServerUpdate> Servers { get; set; } = new();

    // ---- timers ----
    /// <summary>Sweeper timer. Mirrors Go <c>sweeper *time.Timer</c>.</summary>
    public System.Threading.Timer? Sweeper { get; set; }

    /// <summary>Stats heartbeat timer. Mirrors Go <c>stmr *time.Timer</c>.</summary>
    public System.Threading.Timer? StatsMsgTimer { get; set; }

    // ---- reply handlers ----
    /// <summary>
    /// Pending reply subject → handler map.
    /// Mirrors Go <c>replies map[string]msgHandler</c>.
    /// </summary>
    public Dictionary<string, Action<Subscription, ClientConnection, Account, string, string, byte[], byte[]>> Replies { get; set; } = new();

    // ---- queues ----
    /// <summary>Outbound message send queue. Mirrors Go <c>sendq *ipQueue[*pubMsg]</c>.</summary>
    public IpQueue<PubMsg>?     SendQueue         { get; set; }

    /// <summary>Inbound receive queue. Mirrors Go <c>recvq *ipQueue[*inSysMsg]</c>.</summary>
    public IpQueue<InSysMsg>?   RecvQueue         { get; set; }

    /// <summary>Priority receive queue for STATSZ/Pings. Mirrors Go <c>recvqp *ipQueue[*inSysMsg]</c>.</summary>
    public IpQueue<InSysMsg>?   RecvQueuePriority { get; set; }

    /// <summary>Reset channel used to restart the send loop. Mirrors Go <c>resetCh chan struct{}</c>.</summary>
    public System.Threading.Channels.Channel<bool>? ResetChannel { get; set; }

    // ---- durations ----
    /// <summary>Maximum time before an orphaned server entry is removed. Mirrors Go <c>orphMax</c>.</summary>
    public TimeSpan OrphanMax  { get; set; }

    /// <summary>Interval at which orphan checks run. Mirrors Go <c>chkOrph</c>.</summary>
    public TimeSpan CheckOrphan { get; set; }

    /// <summary>Interval between statsz publishes. Mirrors Go <c>statsz</c>.</summary>
    public TimeSpan StatszInterval { get; set; }

    /// <summary>Client-facing statsz interval. Mirrors Go <c>cstatsz</c>.</summary>
    public TimeSpan ClientStatszInterval { get; set; }

    // ---- misc ----
    /// <summary>Short hash used for shared-inbox routing. Mirrors Go <c>shash string</c>.</summary>
    public string ShortHash  { get; set; } = string.Empty;

    /// <summary>Inbox prefix for this server's internal client. Mirrors Go <c>inboxPre string</c>.</summary>
    public string InboxPrefix { get; set; } = string.Empty;

    /// <summary>Subscription for remote stats. Mirrors Go <c>remoteStatsSub *subscription</c>.</summary>
    public Subscription? RemoteStatsSub { get; set; }

    /// <summary>Time of the last statsz publish. Mirrors Go <c>lastStatsz time.Time</c>.</summary>
    public DateTime LastStatsz { get; set; }
}

// ============================================================================
// ServerUpdate — remote server heartbeat tracking
// Mirrors Go <c>serverUpdate</c> struct in server/events.go.
// ============================================================================

/// <summary>
/// Tracks the sequence number and last-seen timestamp of a remote server's
/// system heartbeat. Used to detect orphaned servers.
/// Mirrors Go <c>serverUpdate</c> struct in server/events.go.
/// </summary>
internal sealed class ServerUpdate
{
    /// <summary>Last sequence number received from the remote server.</summary>
    public ulong    Seq   { get; set; }

    /// <summary>Wall-clock time of the last heartbeat.</summary>
    public DateTime LTime { get; set; }
}

// ============================================================================
// PubMsg — internally-queued outbound publish message
// Mirrors Go <c>pubMsg</c> struct in server/events.go.
// ============================================================================

/// <summary>
/// Holds an outbound message that the server wants to publish via the internal
/// send loop, avoiding direct route/gateway writes.
/// Mirrors Go <c>pubMsg</c> struct in server/events.go.
/// </summary>
internal sealed class PubMsg
{
    public ClientConnection?  Client  { get; set; }
    public string             Subject { get; set; } = string.Empty;
    public string       Reply   { get; set; } = string.Empty;
    public ServerInfo?  Si      { get; set; }
    public byte[]?      Hdr     { get; set; }
    public object?      Msg     { get; set; }

    /// <summary>Compression type. TODO: session 12 — wire up compressionType enum.</summary>
    public int          Oct     { get; set; }

    public bool         Echo    { get; set; }
    public bool         Last    { get; set; }

    internal void ReturnToPool()
    {
        Client  = null;
        Subject = string.Empty;
        Reply   = string.Empty;
        Si      = null;
        Hdr     = null;
        Msg     = null;
        Oct     = 0;
        Echo    = false;
        Last    = false;
    }
}

// ============================================================================
// DataStats — message/byte counter pair (sent or received)
// Mirrors Go <c>DataStats</c> struct in server/events.go.
// ============================================================================

/// <summary>
/// Reports how many messages and bytes were sent or received.
/// Optionally breaks out gateway, route, and leaf-node traffic.
/// Mirrors Go <c>DataStats</c> struct in server/events.go.
/// </summary>
public sealed class DataStats
{
    [JsonPropertyName("msgs")]
    public long Msgs { get; set; }

    [JsonPropertyName("bytes")]
    public long Bytes { get; set; }

    [JsonPropertyName("gateways")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public MsgBytes? Gateways { get; set; }

    [JsonPropertyName("routes")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public MsgBytes? Routes { get; set; }

    [JsonPropertyName("leafs")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public MsgBytes? Leafs { get; set; }
}

// ============================================================================
// MsgBytes — simple message+byte pair used inside DataStats
// Mirrors Go <c>MsgBytes</c> struct in server/events.go.
// ============================================================================

/// <summary>
/// A simple pair of message and byte counts, used as a nested breakdown
/// inside <see cref="DataStats"/>.
/// Mirrors Go <c>MsgBytes</c> struct in server/events.go.
/// </summary>
public sealed class MsgBytes
{
    [JsonPropertyName("msgs")]
    public long Msgs { get; set; }

    [JsonPropertyName("bytes")]
    public long Bytes { get; set; }
}

// ============================================================================
// RouteStat / GatewayStat — per-route and per-gateway stat snapshots
// Mirrors Go <c>RouteStat</c> and <c>GatewayStat</c> in server/events.go.
// ============================================================================

/// <summary>
/// Statistics snapshot for a single cluster route connection.
/// Mirrors Go <c>RouteStat</c> in server/events.go.
/// </summary>
public sealed class RouteStat
{
    [JsonPropertyName("rid")]
    public ulong Id { get; set; }

    [JsonPropertyName("name")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Name { get; set; }

    [JsonPropertyName("sent")]
    public DataStats Sent { get; set; } = new();

    [JsonPropertyName("received")]
    public DataStats Received { get; set; } = new();

    [JsonPropertyName("pending")]
    public int Pending { get; set; }
}

/// <summary>
/// Statistics snapshot for a gateway connection.
/// Mirrors Go <c>GatewayStat</c> in server/events.go.
/// </summary>
public sealed class GatewayStat
{
    [JsonPropertyName("gwid")]
    public ulong Id { get; set; }

    [JsonPropertyName("name")]
    public string Name { get; set; } = string.Empty;

    [JsonPropertyName("sent")]
    public DataStats Sent { get; set; } = new();

    [JsonPropertyName("received")]
    public DataStats Received { get; set; } = new();

    [JsonPropertyName("inbound_connections")]
    public int NumInbound { get; set; }
}

// ============================================================================
// ServerStatsMsg — periodic stats advisory published on $SYS.SERVER.{id}.STATSZ
// Mirrors Go <c>ServerStatsMsg</c> struct in server/events.go.
// ============================================================================

/// <summary>
/// Periodic advisory message containing the current server statistics.
/// Mirrors Go <c>ServerStatsMsg</c> struct in server/events.go.
/// </summary>
public sealed class ServerStatsMsg
{
    [JsonPropertyName("server")]
    public ServerInfo Server { get; set; } = new();

    [JsonPropertyName("statsz")]
    public ServerStatsAdvisory Stats { get; set; } = new();
}

// ============================================================================
// ServerStatsAdvisory — the statsz payload inside ServerStatsMsg
// Mirrors Go <c>ServerStats</c> struct (advisory form) in server/events.go.
// NOTE: distinct from the internal ServerStats in NatsServerTypes.cs.
// ============================================================================

/// <summary>
/// The JSON-serialisable statistics payload included inside <see cref="ServerStatsMsg"/>.
/// Mirrors Go <c>ServerStats</c> struct (advisory form) in server/events.go.
/// </summary>
public sealed class ServerStatsAdvisory
{
    [JsonPropertyName("start")]
    public DateTime Start { get; set; }

    [JsonPropertyName("mem")]
    public long Mem { get; set; }

    [JsonPropertyName("cores")]
    public int Cores { get; set; }

    [JsonPropertyName("cpu")]
    public double Cpu { get; set; }

    [JsonPropertyName("connections")]
    public int Connections { get; set; }

    [JsonPropertyName("total_connections")]
    public ulong TotalConnections { get; set; }

    [JsonPropertyName("active_accounts")]
    public int ActiveAccounts { get; set; }

    [JsonPropertyName("subscriptions")]
    public uint NumSubs { get; set; }

    [JsonPropertyName("sent")]
    public DataStats Sent { get; set; } = new();

    [JsonPropertyName("received")]
    public DataStats Received { get; set; } = new();

    [JsonPropertyName("slow_consumers")]
    public long SlowConsumers { get; set; }

    [JsonPropertyName("slow_consumer_stats")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public SlowConsumersStats? SlowConsumersStats { get; set; }

    [JsonPropertyName("stale_connections")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public long StaleConnections { get; set; }

    [JsonPropertyName("stale_connection_stats")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public StaleConnectionStats? StaleConnectionStats { get; set; }

    [JsonPropertyName("stalled_clients")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public long StalledClients { get; set; }

    [JsonPropertyName("routes")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public List<RouteStat>? Routes { get; set; }

    [JsonPropertyName("gateways")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public List<GatewayStat>? Gateways { get; set; }

    [JsonPropertyName("active_servers")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public int ActiveServers { get; set; }

    /// <summary>JetStream stats. TODO: session 19 — wire JetStreamVarz type.</summary>
    [JsonPropertyName("jetstream")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public object? JetStream { get; set; }

    [JsonPropertyName("gomemlimit")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public long MemLimit { get; set; }

    [JsonPropertyName("gomaxprocs")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public int MaxProcs { get; set; }
}

// ============================================================================
// SlowConsumersStats / StaleConnectionStats — advisory-layer per-kind counters
// These are the JSON-serialisable variants used in ServerStatsAdvisory.
// The internal atomic counters live in NatsServerTypes.cs (SlowConsumerStats /
// StaleConnectionStats with different casing).
// ============================================================================

/// <summary>
/// Per-kind slow-consumer counters included in stats advisories.
/// Mirrors Go <c>SlowConsumersStats</c> in server/monitor.go.
/// </summary>
public sealed class SlowConsumersStats
{
    [JsonPropertyName("clients")]
    public ulong Clients { get; set; }

    [JsonPropertyName("routes")]
    public ulong Routes { get; set; }

    [JsonPropertyName("gateways")]
    public ulong Gateways { get; set; }

    [JsonPropertyName("leafs")]
    public ulong Leafs { get; set; }
}

/// <summary>
/// Per-kind stale-connection counters included in stats advisories.
/// Mirrors Go <c>StaleConnectionStats</c> in server/monitor.go.
/// </summary>
public sealed class StaleConnectionStats
{
    [JsonPropertyName("clients")]
    public ulong Clients { get; set; }

    [JsonPropertyName("routes")]
    public ulong Routes { get; set; }

    [JsonPropertyName("gateways")]
    public ulong Gateways { get; set; }

    [JsonPropertyName("leafs")]
    public ulong Leafs { get; set; }
}

// ============================================================================
// ConnectEventMsg / DisconnectEventMsg — client lifecycle advisories
// Mirrors Go structs in server/events.go.
// ============================================================================

/// <summary>
/// Advisory published on <c>$SYS.ACCOUNT.{acc}.CONNECT</c> when a new
/// client connection is established within a tracked account.
/// Mirrors Go <c>ConnectEventMsg</c> in server/events.go.
/// </summary>
public sealed class ConnectEventMsg : TypedEvent
{
    [JsonPropertyName("server")]
    public ServerInfo Server { get; set; } = new();

    [JsonPropertyName("client")]
    public ClientInfo Client { get; set; } = new();
}

/// <summary>
/// Advisory published on <c>$SYS.ACCOUNT.{acc}.DISCONNECT</c> when a
/// previously-tracked client connection closes.
/// Mirrors Go <c>DisconnectEventMsg</c> in server/events.go.
/// </summary>
public sealed class DisconnectEventMsg : TypedEvent
{
    [JsonPropertyName("server")]
    public ServerInfo Server { get; set; } = new();

    [JsonPropertyName("client")]
    public ClientInfo Client { get; set; } = new();

    [JsonPropertyName("sent")]
    public DataStats Sent { get; set; } = new();

    [JsonPropertyName("received")]
    public DataStats Received { get; set; } = new();

    [JsonPropertyName("reason")]
    public string Reason { get; set; } = string.Empty;
}

// ============================================================================
// OCSPPeerRejectEventMsg / OCSPPeerChainlinkInvalidEventMsg
// Mirrors Go structs in server/events.go.
// ============================================================================

/// <summary>
/// Advisory published when a peer TLS handshake is rejected due to OCSP
/// invalidation of the peer's leaf certificate.
/// Mirrors Go <c>OCSPPeerRejectEventMsg</c> in server/events.go.
/// </summary>
public sealed class OcspPeerRejectEventMsg : TypedEvent
{
    [JsonPropertyName("kind")]
    public string Kind { get; set; } = string.Empty;

    [JsonPropertyName("peer")]
    public CertInfo Peer { get; set; } = new();

    [JsonPropertyName("server")]
    public ServerInfo Server { get; set; } = new();

    [JsonPropertyName("reason")]
    public string Reason { get; set; } = string.Empty;
}

/// <summary>
/// Advisory published when a certificate in a valid TLS chain is found to be
/// OCSP-invalid during a peer handshake. Both the invalid link and the
/// peer's leaf cert are included.
/// Mirrors Go <c>OCSPPeerChainlinkInvalidEventMsg</c> in server/events.go.
/// </summary>
public sealed class OcspPeerChainlinkInvalidEventMsg : TypedEvent
{
    [JsonPropertyName("link")]
    public CertInfo Link { get; set; } = new();

    [JsonPropertyName("peer")]
    public CertInfo Peer { get; set; } = new();

    [JsonPropertyName("server")]
    public ServerInfo Server { get; set; } = new();

    [JsonPropertyName("reason")]
    public string Reason { get; set; } = string.Empty;
}

// ============================================================================
// AccountNumConns / AccountStat — account connection count advisories
// Mirrors Go structs in server/events.go.
// ============================================================================

/// <summary>
/// Advisory heartbeat published when the connection count for a tracked
/// account changes, or on a periodic schedule.
/// Mirrors Go <c>AccountNumConns</c> struct in server/events.go.
/// </summary>
public sealed class AccountNumConns : TypedEvent
{
    [JsonPropertyName("server")]
    public ServerInfo Server { get; set; } = new();

    // Embedded AccountStat fields are inlined via composition.
    [JsonPropertyName("acc")]
    public string Account { get; set; } = string.Empty;

    [JsonPropertyName("name")]
    public string Name { get; set; } = string.Empty;

    [JsonPropertyName("conns")]
    public int Conns { get; set; }

    [JsonPropertyName("leafnodes")]
    public int LeafNodes { get; set; }

    [JsonPropertyName("total_conns")]
    public int TotalConns { get; set; }

    [JsonPropertyName("num_subscriptions")]
    public uint NumSubs { get; set; }

    [JsonPropertyName("sent")]
    public DataStats Sent { get; set; } = new();

    [JsonPropertyName("received")]
    public DataStats Received { get; set; } = new();

    [JsonPropertyName("slow_consumers")]
    public long SlowConsumers { get; set; }
}

/// <summary>
/// Statistic data common to <see cref="AccountNumConns"/> and account-level
/// monitoring responses.
/// Mirrors Go <c>AccountStat</c> struct in server/events.go.
/// </summary>
public sealed class AccountStat
{
    [JsonPropertyName("acc")]
    public string Account { get; set; } = string.Empty;

    [JsonPropertyName("name")]
    public string Name { get; set; } = string.Empty;

    [JsonPropertyName("conns")]
    public int Conns { get; set; }

    [JsonPropertyName("leafnodes")]
    public int LeafNodes { get; set; }

    [JsonPropertyName("total_conns")]
    public int TotalConns { get; set; }

    [JsonPropertyName("num_subscriptions")]
    public uint NumSubs { get; set; }

    [JsonPropertyName("sent")]
    public DataStats Sent { get; set; } = new();

    [JsonPropertyName("received")]
    public DataStats Received { get; set; } = new();

    [JsonPropertyName("slow_consumers")]
    public long SlowConsumers { get; set; }
}

/// <summary>
/// Internal request payload sent when this server first starts tracking an
/// account, asking peer servers for their local connection counts.
/// Mirrors Go <c>accNumConnsReq</c> struct in server/events.go.
/// </summary>
internal sealed class AccNumConnsReq
{
    [JsonPropertyName("server")]
    public ServerInfo Server { get; set; } = new();

    [JsonPropertyName("acc")]
    public string Account { get; set; } = string.Empty;
}

// ============================================================================
// ServerCapability / ServerID — server identity and capability flags
// Mirrors Go types in server/events.go.
// ============================================================================

/// <summary>
/// Bit-flag capability set for a remote server.
/// Mirrors Go <c>ServerCapability uint64</c> in server/events.go.
/// </summary>
[Flags]
public enum ServerCapability : ulong
{
    /// <summary>No capabilities.</summary>
    None = 0,

    /// <summary>Server has JetStream enabled. Mirrors Go <c>JetStreamEnabled</c>.</summary>
    JetStreamEnabled = 1UL << 0,

    /// <summary>New stream snapshot capability. Mirrors Go <c>BinaryStreamSnapshot</c>.</summary>
    BinaryStreamSnapshot = 1UL << 1,

    /// <summary>Move NRG traffic out of system account. Mirrors Go <c>AccountNRG</c>.</summary>
    AccountNrg = 1UL << 2,
}

/// <summary>
/// Minimal static identity for a remote server (name, host, ID).
/// Mirrors Go <c>ServerID</c> struct in server/events.go.
/// </summary>
public sealed class ServerIdentity
{
    [JsonPropertyName("name")]
    public string Name { get; set; } = string.Empty;

    [JsonPropertyName("host")]
    public string Host { get; set; } = string.Empty;

    [JsonPropertyName("id")]
    public string Id { get; set; } = string.Empty;
}
