using System.Text.Json.Serialization;

namespace NATS.Server.Events;

/// <summary>
/// Server capability flags.
/// Go reference: events.go ServerCapability constants.
/// </summary>
[Flags]
public enum ServerCapability : ulong
{
    None = 0,
    JetStreamEnabled = 1UL << 0,
    BinaryStreamSnapshot = 1UL << 1,
    AccountNRG = 1UL << 2,
}

/// <summary>
/// Server identity response model used by IDZ requests.
/// Go reference: events.go ServerID.
/// </summary>
public sealed class ServerID
{
    [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;
}

/// <summary>
/// Server identity block embedded in all system events.
/// Go reference: events.go:249-265 ServerInfo struct.
/// </summary>
public sealed class EventServerInfo
{
    [JsonPropertyName("name")]
    public string Name { get; set; } = string.Empty;

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

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

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

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

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

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

    [JsonPropertyName("metadata")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public Dictionary<string, string>? Metadata { get; set; }

    [JsonPropertyName("jetstream")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public bool JetStream { get; set; }

    [JsonPropertyName("flags")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public ulong Flags { get; set; }

    [JsonPropertyName("seq")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public ulong Seq { get; set; }

    [JsonPropertyName("time")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public DateTime Time { get; set; }

    public void SetJetStreamEnabled()
    {
        JetStream = true;
        Flags |= (ulong)ServerCapability.JetStreamEnabled;
    }

    public bool JetStreamEnabled() =>
        (Flags & (ulong)ServerCapability.JetStreamEnabled) != 0;

    public void SetBinaryStreamSnapshot() =>
        Flags |= (ulong)ServerCapability.BinaryStreamSnapshot;

    public bool BinaryStreamSnapshot() =>
        (Flags & (ulong)ServerCapability.BinaryStreamSnapshot) != 0;

    public void SetAccountNRG() =>
        Flags |= (ulong)ServerCapability.AccountNRG;

    public bool AccountNRG() =>
        (Flags & (ulong)ServerCapability.AccountNRG) != 0;
}

/// <summary>
/// Client identity block for connect/disconnect events.
/// Go reference: events.go:308-331 ClientInfo struct.
/// </summary>
public sealed class EventClientInfo
{
    [JsonPropertyName("start")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public DateTime Start { get; set; }

    [JsonPropertyName("stop")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public DateTime Stop { get; set; }

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

    [JsonPropertyName("id")]
    public ulong Id { get; set; }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// <summary>
/// Message and byte count stats. Applicable for both sent and received.
/// Go reference: events.go:407-410 MsgBytes, events.go:412-418 DataStats.
/// </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 MsgBytesStats? Gateways { get; set; }

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

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

/// <summary>
/// Sub-stats for gateway/route/leaf message flow.
/// Go reference: events.go:407-410 MsgBytes.
/// </summary>
public sealed class MsgBytesStats
{
    [JsonPropertyName("msgs")]
    public long Msgs { get; set; }

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

/// <summary>Client connect advisory. Go events.go:155-160.</summary>
public sealed class ConnectEventMsg
{
    public const string EventType = "io.nats.server.advisory.v1.client_connect";

    [JsonPropertyName("type")]
    public string Type { get; set; } = EventType;

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

    [JsonPropertyName("timestamp")]
    public DateTime Time { get; set; }

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

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

/// <summary>Client disconnect advisory. Go events.go:167-174.</summary>
public sealed class DisconnectEventMsg
{
    public const string EventType = "io.nats.server.advisory.v1.client_disconnect";

    [JsonPropertyName("type")]
    public string Type { get; set; } = EventType;

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

    [JsonPropertyName("timestamp")]
    public DateTime Time { get; set; }

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

    [JsonPropertyName("client")]
    public EventClientInfo 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;
}

/// <summary>
/// Account connection count heartbeat. Go events.go:210-214, 217-227.
/// Includes the full AccountStat fields from Go.
/// </summary>
public sealed class AccountNumConns
{
    public const string EventType = "io.nats.server.advisory.v1.account_connections";

    [JsonPropertyName("type")]
    public string Type { get; set; } = EventType;

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

    [JsonPropertyName("timestamp")]
    public DateTime Time { get; set; }

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

    /// <summary>Account identifier. Go AccountStat.Account.</summary>
    [JsonPropertyName("acc")]
    public string AccountName { get; set; } = string.Empty;

    /// <summary>Account display name. Go AccountStat.Name.</summary>
    [JsonPropertyName("name")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Name { get; set; }

    /// <summary>Current active connections. Go AccountStat.Conns.</summary>
    [JsonPropertyName("conns")]
    public int Connections { get; set; }

    /// <summary>Active leaf node connections. Go AccountStat.LeafNodes.</summary>
    [JsonPropertyName("leafnodes")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public int LeafNodes { get; set; }

    /// <summary>Total connections over time. Go AccountStat.TotalConns.</summary>
    [JsonPropertyName("total_conns")]
    public int TotalConnections { get; set; }

    /// <summary>Active subscription count. Go AccountStat.NumSubs.</summary>
    [JsonPropertyName("num_subscriptions")]
    public uint NumSubscriptions { get; set; }

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

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

    /// <summary>Slow consumer count. Go AccountStat.SlowConsumers.</summary>
    [JsonPropertyName("slow_consumers")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public long SlowConsumers { get; set; }
}

/// <summary>
/// Route statistics for server stats broadcast.
/// Go reference: events.go:390-396 RouteStat.
/// </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>
/// Gateway statistics for server stats broadcast.
/// Go reference: events.go:399-405 GatewayStat.
/// </summary>
public sealed class GatewayStat
{
    [JsonPropertyName("gwid")]
    public ulong Id { get; set; }

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

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

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

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

/// <summary>
/// Slow consumer breakdown statistics.
/// Go reference: events.go:377 SlowConsumersStats.
/// </summary>
public sealed class SlowConsumersStats
{
    [JsonPropertyName("clients")]
    public long Clients { get; set; }

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

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

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

/// <summary>
/// Stale connection breakdown statistics.
/// Go reference: events.go:379 StaleConnectionStats.
/// </summary>
public sealed class StaleConnectionStats
{
    [JsonPropertyName("clients")]
    public long Clients { get; set; }

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

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

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

/// <summary>Server stats broadcast. Go events.go:150-153.</summary>
public sealed class ServerStatsMsg
{
    [JsonPropertyName("server")]
    public EventServerInfo Server { get; set; } = new();

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

/// <summary>
/// Server stats data. Full parity with Go events.go:365-387 ServerStats.
/// </summary>
public sealed class ServerStatsData
{
    [JsonPropertyName("start")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public DateTime Start { get; set; }

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

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

    [JsonPropertyName("cpu")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public double Cpu { get; set; }

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

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

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

    [JsonPropertyName("subscriptions")]
    public long Subscriptions { get; set; }

    /// <summary>Sent stats (msgs + bytes). Go ServerStats.Sent.</summary>
    [JsonPropertyName("sent")]
    public DataStats Sent { get; set; } = new();

    /// <summary>Received stats (msgs + bytes). Go ServerStats.Received.</summary>
    [JsonPropertyName("received")]
    public DataStats Received { get; set; } = new();

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

    [JsonPropertyName("slow_consumer_stats")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public SlowConsumersStats? SlowConsumerStats { 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("routes")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public RouteStat[]? Routes { get; set; }

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

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

    // Kept for backward compat — flat counters that mirror Sent/Received.
    [JsonPropertyName("in_msgs")]
    public long InMsgs { get; set; }

    [JsonPropertyName("out_msgs")]
    public long OutMsgs { get; set; }

    [JsonPropertyName("in_bytes")]
    public long InBytes { get; set; }

    [JsonPropertyName("out_bytes")]
    public long OutBytes { get; set; }
}

/// <summary>Server shutdown notification.</summary>
public sealed class ShutdownEventMsg
{
    [JsonPropertyName("server")]
    public EventServerInfo Server { get; set; } = new();

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

/// <summary>Lame duck mode notification.</summary>
public sealed class LameDuckEventMsg
{
    [JsonPropertyName("server")]
    public EventServerInfo Server { get; set; } = new();
}

/// <summary>Auth error advisory.</summary>
public sealed class AuthErrorEventMsg
{
    public const string EventType = "io.nats.server.advisory.v1.client_auth";

    [JsonPropertyName("type")]
    public string Type { get; set; } = EventType;

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

    [JsonPropertyName("timestamp")]
    public DateTime Time { get; set; }

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

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

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

/// <summary>
/// OCSP peer rejection advisory.
/// Go reference: events.go:182-188 OCSPPeerRejectEventMsg.
/// </summary>
public sealed class OcspPeerRejectEventMsg
{
    public const string EventType = "io.nats.server.advisory.v1.ocsp_peer_reject";

    [JsonPropertyName("type")]
    public string Type { get; set; } = EventType;

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

    [JsonPropertyName("timestamp")]
    public DateTime Time { get; set; }

    [JsonPropertyName("kind")]
    public string Kind { get; set; } = "";

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

    [JsonPropertyName("peer")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public EventCertInfo? Peer { get; set; }

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

/// <summary>
/// Certificate identity block used by OCSP peer advisories.
/// Go reference: certidp.CertInfo payload embedded in events.go OCSP messages.
/// </summary>
public sealed class EventCertInfo
{
    [JsonPropertyName("subject")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Subject { get; set; }

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

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

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

/// <summary>
/// OCSP chain-link invalid advisory.
/// Go reference: events.go OCSPPeerChainlinkInvalidEventMsg.
/// </summary>
public sealed class OcspPeerChainlinkInvalidEventMsg
{
    public const string EventType = "io.nats.server.advisory.v1.ocsp_peer_link_invalid";

    [JsonPropertyName("type")]
    public string Type { get; set; } = EventType;

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

    [JsonPropertyName("timestamp")]
    public DateTime Time { get; set; }

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

    [JsonPropertyName("link")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public EventCertInfo? Link { get; set; }

    [JsonPropertyName("peer")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public EventCertInfo? Peer { get; set; }
}

/// <summary>
/// OCSP chain validation advisory, published when a certificate's OCSP status
/// is checked during TLS handshake.
/// Go reference: ocsp.go — OCSP peer verification advisory.
/// </summary>
public sealed class OcspChainValidationEvent
{
    public const string EventType = "io.nats.server.advisory.v1.ocsp_chain_validation";

    [JsonPropertyName("type")]
    public string Type { get; init; } = EventType;

    [JsonPropertyName("id")]
    public string Id { get; init; } = Guid.NewGuid().ToString("N");

    [JsonPropertyName("time")]
    public string Time { get; init; } = DateTime.UtcNow.ToString("O");

    [JsonPropertyName("server")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public EventServerInfo? Server { get; set; }

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

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

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

    /// <summary>OCSP status string: "good", "revoked", or "unknown".</summary>
    [JsonPropertyName("ocsp_status")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? OcspStatus { get; set; }

    [JsonPropertyName("checked_at")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public DateTime? CheckedAt { get; set; }

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

/// <summary>
/// OCSP status values matching the Go OCSP response status strings.
/// Go reference: ocsp.go — ocspStatusGood, ocspStatusRevoked, ocspStatusUnknown.
/// </summary>
public enum OcspStatus
{
    Unknown,
    Good,
    Revoked,
}

/// <summary>
/// Factory helpers for constructing OCSP advisory event messages.
/// Go reference: ocsp.go — OCSP peer reject and chain validation advisory publishing.
/// </summary>
public static class OcspEventBuilder
{
    /// <summary>
    /// Build an OcspPeerRejectEventMsg for a rejected peer certificate.
    /// Go reference: ocsp.go — postOCSPPeerRejectEvent.
    /// </summary>
    public static OcspPeerRejectEventMsg BuildPeerReject(
        string serverId, string serverName,
        string kind, string reason) =>
        new()
        {
            Id = EventBuilder.GenerateEventId(),
            Time = DateTime.UtcNow,
            Server = new EventServerInfo { Id = serverId, Name = serverName },
            Kind = kind,
            Reason = reason,
        };

    /// <summary>
    /// Build an OcspChainValidationEvent for a certificate OCSP check.
    /// Go reference: ocsp.go — OCSP chain validation advisory.
    /// </summary>
    public static OcspChainValidationEvent BuildChainValidation(
        string serverId, string serverName,
        string certSubject, string certIssuer, string serialNumber,
        string ocspStatus, string? error = null) =>
        new()
        {
            Server = new EventServerInfo { Id = serverId, Name = serverName },
            CertSubject = certSubject,
            CertIssuer = certIssuer,
            SerialNumber = serialNumber,
            OcspStatus = ocspStatus,
            CheckedAt = DateTime.UtcNow,
            Error = error,
        };

    /// <summary>
    /// Parse an OCSP status string into the <see cref="OcspStatus"/> enum.
    /// Go reference: ocsp.go — ocspStatusGood / ocspStatusRevoked string constants.
    /// </summary>
    public static OcspStatus ParseStatus(string? status) =>
        status?.ToLowerInvariant() switch
        {
            "good" => OcspStatus.Good,
            "revoked" => OcspStatus.Revoked,
            _ => OcspStatus.Unknown,
        };
}

/// <summary>
/// Remote server shutdown advisory.
/// Go reference: events.go — remote server lifecycle.
/// </summary>
public sealed class RemoteServerShutdownEvent
{
    public const string EventType = "io.nats.server.advisory.v1.remote_shutdown";

    [JsonPropertyName("type")]
    public string Type { get; init; } = EventType;

    [JsonPropertyName("id")]
    public string Id { get; init; } = Guid.NewGuid().ToString("N");

    [JsonPropertyName("time")]
    public string Time { get; init; } = DateTime.UtcNow.ToString("O");

    [JsonPropertyName("server")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public EventServerInfo? Server { get; set; }

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

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

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

/// <summary>
/// Remote server update advisory.
/// Go reference: events.go — remote server lifecycle.
/// </summary>
public sealed class RemoteServerUpdateEvent
{
    public const string EventType = "io.nats.server.advisory.v1.remote_update";

    [JsonPropertyName("type")]
    public string Type { get; init; } = EventType;

    [JsonPropertyName("id")]
    public string Id { get; init; } = Guid.NewGuid().ToString("N");

    [JsonPropertyName("time")]
    public string Time { get; init; } = DateTime.UtcNow.ToString("O");

    [JsonPropertyName("server")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public EventServerInfo? Server { get; set; }

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

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

    /// <summary>Update category, e.g. "routes_changed", "config_updated".</summary>
    [JsonPropertyName("update_type")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? UpdateType { get; set; }
}

/// <summary>
/// Leaf node connect advisory.
/// Go reference: events.go — leaf node lifecycle.
/// </summary>
public sealed class LeafNodeConnectEvent
{
    public const string EventType = "io.nats.server.advisory.v1.leafnode_connect";

    [JsonPropertyName("type")]
    public string Type { get; init; } = EventType;

    [JsonPropertyName("id")]
    public string Id { get; init; } = Guid.NewGuid().ToString("N");

    [JsonPropertyName("time")]
    public string Time { get; init; } = DateTime.UtcNow.ToString("O");

    [JsonPropertyName("server")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public EventServerInfo? Server { get; set; }

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

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

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

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

/// <summary>
/// Leaf node disconnect advisory.
/// Go reference: events.go — leaf node lifecycle.
/// </summary>
public sealed class LeafNodeDisconnectEvent
{
    public const string EventType = "io.nats.server.advisory.v1.leafnode_disconnect";

    [JsonPropertyName("type")]
    public string Type { get; init; } = EventType;

    [JsonPropertyName("id")]
    public string Id { get; init; } = Guid.NewGuid().ToString("N");

    [JsonPropertyName("time")]
    public string Time { get; init; } = DateTime.UtcNow.ToString("O");

    [JsonPropertyName("server")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public EventServerInfo? Server { get; set; }

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

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

/// <summary>
/// Account numeric connections request.
/// Go reference: events.go:233-236 accNumConnsReq.
/// </summary>
public sealed class AccNumConnsReq
{
    [JsonPropertyName("server")]
    public EventServerInfo Server { get; set; } = new();

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

/// <summary>
/// Account subscription-count request.
/// Go reference: events.go accNumSubsReq.
/// </summary>
public sealed class AccNumSubsReq
{
    [JsonPropertyName("server")]
    public EventServerInfo Server { get; set; } = new();

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

/// <summary>
/// Shared request filter options for system request subjects.
/// Go reference: events.go EventFilterOptions.
/// </summary>
public class EventFilterOptions
{
    [JsonPropertyName("name")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Name { get; set; }

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

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

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

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

public sealed class StatszEventOptions : EventFilterOptions;
public sealed class AccInfoEventOptions : EventFilterOptions;
public sealed class ConnzEventOptions : EventFilterOptions;
public sealed class RoutezEventOptions : EventFilterOptions;
public sealed class SubszEventOptions : EventFilterOptions;
public sealed class VarzEventOptions : EventFilterOptions;
public sealed class GatewayzEventOptions : EventFilterOptions;
public sealed class LeafzEventOptions : EventFilterOptions;
public sealed class AccountzEventOptions : EventFilterOptions;
public sealed class AccountStatzEventOptions : EventFilterOptions;
public sealed class JszEventOptions : EventFilterOptions;
public sealed class HealthzEventOptions : EventFilterOptions;
public sealed class ProfilezEventOptions : EventFilterOptions;
public sealed class ExpvarzEventOptions : EventFilterOptions;
public sealed class IpqueueszEventOptions : EventFilterOptions;
public sealed class RaftzEventOptions : EventFilterOptions;

/// <summary>
/// Generic server API error payload.
/// Go reference: events.go server API response errors.
/// </summary>
public sealed class ServerAPIError
{
    [JsonPropertyName("code")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
    public int Code { get; set; }

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

/// <summary>
/// Generic server request/response envelope for $SYS.REQ services.
/// Go reference: events.go ServerAPIResponse.
/// </summary>
public class ServerAPIResponse
{
    [JsonPropertyName("server")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public EventServerInfo? Server { get; set; }

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

    [JsonPropertyName("error")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public ServerAPIError? Error { get; set; }
}

public sealed class ServerAPIConnzResponse : ServerAPIResponse;
public sealed class ServerAPIRoutezResponse : ServerAPIResponse;
public sealed class ServerAPIGatewayzResponse : ServerAPIResponse;
public sealed class ServerAPIJszResponse : ServerAPIResponse;
public sealed class ServerAPIHealthzResponse : ServerAPIResponse;
public sealed class ServerAPIVarzResponse : ServerAPIResponse;
public sealed class ServerAPISubszResponse : ServerAPIResponse;
public sealed class ServerAPILeafzResponse : ServerAPIResponse;
public sealed class ServerAPIAccountzResponse : ServerAPIResponse;
public sealed class ServerAPIExpvarzResponse : ServerAPIResponse;
public sealed class ServerAPIpqueueszResponse : ServerAPIResponse;
public sealed class ServerAPIRaftzResponse : ServerAPIResponse;

/// <summary>
/// Kick client request payload.
/// Go reference: events.go KickClientReq.
/// </summary>
public sealed class KickClientReq
{
    [JsonPropertyName("cid")]
    public ulong ClientId { get; set; }
}

/// <summary>
/// Lame duck mode client request payload.
/// Go reference: events.go LDMClientReq.
/// </summary>
public sealed class LDMClientReq
{
    [JsonPropertyName("cid")]
    public ulong ClientId { get; set; }
}

/// <summary>
/// User info payload for direct user info requests.
/// Go reference: events.go UserInfo.
/// </summary>
public sealed class UserInfo
{
    [JsonPropertyName("user")]
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? User { get; set; }

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

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

/// <summary>
/// Factory helpers that construct fully-populated system event messages,
/// mirroring Go's inline struct initialization patterns in events.go.
/// Go reference: events.go sendConnectEventMsg, sendDisconnectEventMsg,
/// sendAccountNumConns, sendServerStats.
/// </summary>
public static class EventBuilder
{
    /// <summary>
    /// Build a ConnectEventMsg with all required fields.
    /// Go reference: events.go sendConnectEventMsg (around line 636).
    /// </summary>
    public static ConnectEventMsg BuildConnectEvent(
        string serverId, string serverName, string? cluster,
        ulong clientId, string host, string? account, string? user, string? name,
        string? lang, string? version) =>
        new()
        {
            Id = GenerateEventId(),
            Time = DateTime.UtcNow,
            Server = new EventServerInfo
            {
                Id = serverId,
                Name = serverName,
                Cluster = cluster,
            },
            Client = new EventClientInfo
            {
                Id = clientId,
                Host = host,
                Account = account,
                User = user,
                Name = name,
                Lang = lang,
                Version = version,
                Start = DateTime.UtcNow,
            },
        };

    /// <summary>
    /// Build a DisconnectEventMsg with all required fields.
    /// Go reference: events.go sendDisconnectEventMsg (around line 668).
    /// </summary>
    public static DisconnectEventMsg BuildDisconnectEvent(
        string serverId, string serverName, string? cluster,
        ulong clientId, string host, string? account, string? user,
        string reason, DataStats sent, DataStats received) =>
        new()
        {
            Id = GenerateEventId(),
            Time = DateTime.UtcNow,
            Server = new EventServerInfo
            {
                Id = serverId,
                Name = serverName,
                Cluster = cluster,
            },
            Client = new EventClientInfo
            {
                Id = clientId,
                Host = host,
                Account = account,
                User = user,
                Stop = DateTime.UtcNow,
            },
            Reason = reason,
            Sent = sent,
            Received = received,
        };

    /// <summary>
    /// Build an AccountNumConns event.
    /// Go reference: events.go sendAccountNumConns (around line 714).
    /// </summary>
    public static AccountNumConns BuildAccountConnsEvent(
        string serverId, string serverName,
        string accountName, int connections, int leafNodes,
        int totalConnections, int numSubscriptions,
        DataStats sent, DataStats received, long slowConsumers) =>
        new()
        {
            Id = GenerateEventId(),
            Time = DateTime.UtcNow,
            Server = new EventServerInfo
            {
                Id = serverId,
                Name = serverName,
            },
            AccountName = accountName,
            Connections = connections,
            LeafNodes = leafNodes,
            TotalConnections = totalConnections,
            NumSubscriptions = (uint)numSubscriptions,
            Sent = sent,
            Received = received,
            SlowConsumers = slowConsumers,
        };

    /// <summary>
    /// Build a ServerStatsMsg.
    /// Go reference: events.go sendStatsz (around line 742).
    /// </summary>
    public static ServerStatsMsg BuildServerStats(
        string serverId, string serverName,
        long mem, int cores, double cpu,
        int connections, int totalConnections, int activeAccounts,
        int subscriptions, DataStats sent, DataStats received) =>
        new()
        {
            Server = new EventServerInfo
            {
                Id = serverId,
                Name = serverName,
            },
            Stats = new ServerStatsData
            {
                Start = DateTime.UtcNow,
                Mem = mem,
                Cores = cores,
                Cpu = cpu,
                Connections = connections,
                TotalConnections = totalConnections,
                ActiveAccounts = activeAccounts,
                Subscriptions = subscriptions,
                Sent = sent,
                Received = received,
                InMsgs = received.Msgs,
                OutMsgs = sent.Msgs,
                InBytes = received.Bytes,
                OutBytes = sent.Bytes,
            },
        };

    /// <summary>
    /// Generate a unique event ID. Go uses nuid for this.
    /// Go reference: events.go — nuid.Next() for event IDs.
    /// </summary>
    public static string GenerateEventId() => Guid.NewGuid().ToString("N");

    /// <summary>
    /// Get an ISO 8601 timestamp string for embedding in events.
    /// </summary>
    public static string GetTimestamp() => DateTime.UtcNow.ToString("O");
}
