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

using System.Text.Json.Serialization;
using ZB.MOM.NatsNet.Server.Auth;
using ZB.MOM.NatsNet.Server.Auth.Ocsp;
using ZB.MOM.NatsNet.Server.Internal;
using ZB.MOM.NatsNet.Server.Internal.DataStructures;
using ZB.MOM.NatsNet.Server.WebSocket;

namespace ZB.MOM.NatsNet.Server;

// ============================================================================
// Wire-protocol Info payload
// ============================================================================

/// <summary>
/// The INFO payload sent to clients, routes, gateways and leaf nodes.
/// Mirrors Go <c>Info</c> struct in server.go.
/// </summary>
public sealed class ServerInfo
{
    [JsonPropertyName("server_id")]           public string   Id                { get; set; } = string.Empty;
    [JsonPropertyName("server_name")]         public string   Name              { get; set; } = string.Empty;
    [JsonPropertyName("version")]             public string   Version           { get; set; } = string.Empty;
    [JsonPropertyName("proto")]               public int      Proto             { get; set; }
    [JsonPropertyName("git_commit")]          public string?  GitCommit         { get; set; }
    [JsonPropertyName("go")]                  public string   GoVersion         { get; set; } = string.Empty;
    [JsonPropertyName("host")]                public string   Host              { get; set; } = string.Empty;
    [JsonPropertyName("port")]                public int      Port              { get; set; }
    [JsonPropertyName("headers")]             public bool     Headers           { get; set; }
    [JsonPropertyName("auth_required")]       public bool     AuthRequired      { get; set; }
    [JsonPropertyName("tls_required")]        public bool     TlsRequired       { get; set; }
    [JsonPropertyName("tls_verify")]          public bool     TlsVerify         { get; set; }
    [JsonPropertyName("tls_available")]       public bool     TlsAvailable      { get; set; }
    [JsonPropertyName("max_payload")]         public int      MaxPayload        { get; set; }
    [JsonPropertyName("jetstream")]           public bool     JetStream         { get; set; }
    [JsonPropertyName("ip")]                  public string?  Ip                { get; set; }
    [JsonPropertyName("client_id")]           public ulong    Cid               { get; set; }
    [JsonPropertyName("client_ip")]           public string?  ClientIp          { get; set; }
    [JsonPropertyName("nonce")]               public string?  Nonce             { get; set; }
    [JsonPropertyName("cluster")]             public string?  Cluster           { get; set; }
    [JsonPropertyName("cluster_dynamic")]     public bool     Dynamic           { get; set; }
    [JsonPropertyName("domain")]              public string?  Domain            { get; set; }
    [JsonPropertyName("connect_urls")]        public string[]? ClientConnectUrls { get; set; }
    [JsonPropertyName("ws_connect_urls")]     public string[]? WsConnectUrls    { get; set; }
    [JsonPropertyName("ldm")]                 public bool     LameDuckMode      { get; set; }
    [JsonPropertyName("compression")]         public string?  Compression       { get; set; }
    [JsonPropertyName("connect_info")]        public bool     ConnectInfo       { get; set; }
    [JsonPropertyName("remote_account")]      public string?  RemoteAccount     { get; set; }
    [JsonPropertyName("acc_is_sys")]          public bool     IsSystemAccount   { get; set; }
    [JsonPropertyName("api_lvl")]             public int      JsApiLevel        { get; set; }
    [JsonPropertyName("xkey")]                public string?  XKey              { get; set; }

    // Route-specific
    [JsonPropertyName("import")]              public SubjectPermission? Import        { get; set; }
    [JsonPropertyName("export")]              public SubjectPermission? Export        { get; set; }
    [JsonPropertyName("lnoc")]               public bool     Lnoc              { get; set; }
    [JsonPropertyName("lnocu")]              public bool     Lnocu             { get; set; }
    [JsonPropertyName("info_on_connect")]     public bool     InfoOnConnect     { get; set; }
    [JsonPropertyName("route_pool_size")]     public int      RoutePoolSize     { get; set; }
    [JsonPropertyName("route_pool_idx")]      public int      RoutePoolIdx      { get; set; }
    [JsonPropertyName("route_account")]       public string?  RouteAccount      { get; set; }
    [JsonPropertyName("route_acc_add_reqid")] public string?  RouteAccReqId     { get; set; }
    [JsonPropertyName("gossip_mode")]         public byte     GossipMode        { get; set; }

    // Gateway-specific
    [JsonPropertyName("gateway")]             public string?  Gateway           { get; set; }
    [JsonPropertyName("gateway_urls")]        public string[]? GatewayUrls      { get; set; }
    [JsonPropertyName("gateway_url")]         public string?  GatewayUrl        { get; set; }
    [JsonPropertyName("gateway_cmd")]         public byte     GatewayCmd        { get; set; }
    [JsonPropertyName("gateway_cmd_payload")] public byte[]?  GatewayCmdPayload { get; set; }
    [JsonPropertyName("gateway_nrp")]         public bool     GatewayNrp        { get; set; }
    [JsonPropertyName("gateway_iom")]         public bool     GatewayIom        { get; set; }

    // LeafNode-specific
    [JsonPropertyName("leafnode_urls")]       public string[]? LeafNodeUrls     { get; set; }

    // =========================================================================
    // Advisory / event-system fields
    // Mirrors Go server/events.go ServerInfo advisory struct fields.
    // These are populated by the internal send loop when publishing advisories.
    // =========================================================================

    /// <summary>
    /// Sequence number for this server's advisory messages.
    /// Mirrors Go <c>Seq uint64</c> in ServerInfo (events.go).
    /// </summary>
    [JsonPropertyName("seq")]
    public ulong Seq { get; set; }

    /// <summary>
    /// UTC timestamp of this advisory message.
    /// Mirrors Go <c>Time time.Time</c> in ServerInfo (events.go).
    /// </summary>
    [JsonPropertyName("time")]
    public DateTime Time { get; set; }

    /// <summary>
    /// Capability flags for this server (bitmask of <see cref="ServerCapability"/>).
    /// Mirrors Go <c>Flags ServerCapability</c> in ServerInfo (events.go).
    /// </summary>
    [JsonPropertyName("flags")]
    public ulong Capabilities { get; set; }

    /// <summary>
    /// Server tags from options.
    /// Mirrors Go <c>Tags []string</c> in ServerInfo (events.go).
    /// </summary>
    [JsonPropertyName("tags")]
    [JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public List<string>? Tags { get; set; }

    /// <summary>
    /// Server metadata from options.
    /// Mirrors Go <c>Metadata map[string]string</c> in ServerInfo (events.go).
    /// </summary>
    [JsonPropertyName("metadata")]
    [JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
    public Dictionary<string, string>? Metadata { get; set; }

    // =========================================================================
    // Capability helpers (Group B)
    // Mirrors Go ServerInfo capability methods in server/events.go.
    // =========================================================================

    /// <summary>
    /// Sets the JetStream capability bit and the legacy JetStream bool field.
    /// Mirrors Go <c>(si *ServerInfo) SetJetStreamEnabled()</c> in server/events.go.
    /// </summary>
    public void SetJetStreamEnabled()
    {
        Capabilities |= (ulong)ServerCapability.JetStreamEnabled;
        JetStream      = true;
    }

    /// <summary>
    /// Returns whether this server has JetStream enabled, taking into account
    /// both the capability flag and the legacy bool field.
    /// Mirrors Go <c>(si *ServerInfo) JetStreamEnabled() bool</c> in server/events.go.
    /// </summary>
    public bool IsJetStreamEnabled() =>
        (Capabilities & (ulong)ServerCapability.JetStreamEnabled) != 0 || JetStream;

    /// <summary>
    /// Sets the binary-stream-snapshot capability bit.
    /// Mirrors Go <c>(si *ServerInfo) SetBinaryStreamSnapshot()</c> in server/events.go.
    /// </summary>
    public void SetBinaryStreamSnapshot() =>
        Capabilities |= (ulong)ServerCapability.BinaryStreamSnapshot;

    /// <summary>
    /// Returns whether this server supports binary stream snapshots.
    /// Mirrors Go <c>(si *ServerInfo) BinaryStreamSnapshot() bool</c> in server/events.go.
    /// </summary>
    public bool IsBinaryStreamSnapshot() =>
        (Capabilities & (ulong)ServerCapability.BinaryStreamSnapshot) != 0;

    /// <summary>
    /// Sets the account-NRG capability bit.
    /// Mirrors Go <c>(si *ServerInfo) SetAccountNRG()</c> in server/events.go.
    /// </summary>
    public void SetAccountNrg() =>
        Capabilities |= (ulong)ServerCapability.AccountNrg;

    /// <summary>
    /// Returns whether this server supports moving NRG traffic into the asset account.
    /// Mirrors Go <c>(si *ServerInfo) AccountNRG() bool</c> in server/events.go.
    /// </summary>
    public bool IsAccountNrg() =>
        (Capabilities & (ulong)ServerCapability.AccountNrg) != 0;

    /// <summary>Returns a shallow clone of this <see cref="ServerInfo"/>.</summary>
    internal ServerInfo ShallowClone() => (ServerInfo)MemberwiseClone();
}

// ============================================================================
// Server stats structures
// ============================================================================

/// <summary>
/// Aggregate message/byte counters for the server.
/// Mirrors Go embedded <c>stats</c> struct in server.go.
/// </summary>
internal sealed class ServerStats
{
    public long InMsgs;
    public long OutMsgs;
    public long InBytes;
    public long OutBytes;
    public long SlowConsumers;
    public long StaleConnections;
    public long Stalls;
}

/// <summary>
/// Per-kind slow-consumer counters (atomic).
/// Mirrors Go embedded <c>scStats</c> in server.go.
/// </summary>
internal sealed class SlowConsumerStats
{
    public long Clients;
    public long Routes;
    public long Leafs;
    public long Gateways;
}

/// <summary>
/// Per-kind stale-connection counters (atomic, internal use only).
/// Mirrors Go embedded <c>staleStats</c> in server.go.
/// NOTE: The public JSON-serialisable monitoring equivalent is <c>StaleConnectionStats</c>
/// in Events/EventTypes.cs.
/// </summary>
internal sealed class InternalStaleStats
{
    public long Clients;
    public long Routes;
    public long Leafs;
    public long Gateways;
}

// ============================================================================
// nodeInfo — JetStream node metadata
// ============================================================================

/// <summary>
/// Per-node JetStream metadata stored in the server's node-info map.
/// Mirrors Go <c>nodeInfo</c> struct in server.go.
/// </summary>
internal sealed class NodeInfo
{
    public string   Name            { get; set; } = string.Empty;
    public string   Version         { get; set; } = string.Empty;
    public string   Cluster         { get; set; } = string.Empty;
    public string   Domain          { get; set; } = string.Empty;
    public string   Id              { get; set; } = string.Empty;
    public string[] Tags            { get; set; } = [];
    public object?  Cfg             { get; set; } // JetStreamConfig — session 19
    public object?  Stats           { get; set; } // JetStreamStats  — session 19
    public bool     Offline         { get; set; }
    public bool     Js              { get; set; }
    public bool     BinarySnapshots { get; set; }
    public bool     AccountNrg      { get; set; }
}

// ============================================================================
// Server protocol version constants
// ============================================================================

/// <summary>
/// Server-to-server (route/leaf/gateway) protocol versions.
/// Mirrors the iota block at the top of server.go.
/// </summary>
public static class ServerProtocol
{
    /// <summary>Original route protocol (2009).</summary>
    public const int RouteProtoZero = 0;
    /// <summary>Route protocol that supports INFO updates.</summary>
    public const int RouteProtoInfo = 1;
    /// <summary>Route/cluster protocol with account support.</summary>
    public const int RouteProtoV2   = 2;
    /// <summary>Protocol with distributed message tracing.</summary>
    public const int MsgTraceProto  = 3;
}

// ============================================================================
// Compression mode constants
// ============================================================================

/// <summary>
/// Compression mode string constants.
/// Mirrors the <c>const</c> block in server.go.
/// </summary>
public static class CompressionMode
{
    public const string NotSupported  = "not supported";
    public const string Off           = "off";
    public const string Accept        = "accept";
    public const string S2Auto        = "s2_auto";
    public const string S2Uncompressed = "s2_uncompressed";
    public const string S2Fast        = "s2_fast";
    public const string S2Better      = "s2_better";
    public const string S2Best        = "s2_best";
}

// ============================================================================
// Stub types for cross-session dependencies
// ============================================================================

// These stubs will be replaced with full implementations in later sessions.
// They are declared here to allow the NatsServer class to compile.

// InternalState is now fully defined in Events/EventTypes.cs (session 12).

// JetStreamState — replaced by JsAccount in JetStream/JetStreamTypes.cs (session 19).
// JetStreamConfig — replaced by full implementation in JetStream/JetStreamTypes.cs (session 19).

// SrvGateway — full class is in Gateway/GatewayTypes.cs (session 16).

// SrvWebsocket — now fully defined in WebSocket/WebSocketTypes.cs (session 23).
// OcspMonitor    — now fully defined in Auth/Ocsp/OcspTypes.cs (session 23).
// IOcspResponseCache — now fully defined in Auth/Ocsp/OcspTypes.cs (session 23).

/// <summary>Stub for server MQTT state (session 22).</summary>
internal sealed class SrvMqtt { }

/// <summary>Stub for IP queue (session 02 — already ported as IpQueue).</summary>
// IpQueue is already in session 02 internals — used here via object.

// LeafNodeCfg — full class is in LeafNode/LeafNodeTypes.cs (session 15).

/// <summary>
/// Network resolver used by <see cref="NatsServer.GetRandomIP"/>.
/// Mirrors Go <c>netResolver</c> interface in server.go.
/// </summary>
internal interface INetResolver
{
    Task<string[]> LookupHostAsync(string host, CancellationToken ct = default);
}

/// <summary>Factory for rate counters.</summary>
internal static class RateCounterFactory
{
    public static ZB.MOM.NatsNet.Server.Internal.RateCounter Create(long rateLimit)
        => new(rateLimit);
}

// IRaftNode is now fully defined in JetStream/RaftTypes.cs (session 20).

// ============================================================================
// Session 10: Ports, TlsMixConn, CaptureHttpServerLog
// ============================================================================

/// <summary>
/// Describes the URLs at which this server can be contacted.
/// Mirrors Go <c>Ports</c> struct in server.go.
/// </summary>
public sealed class Ports
{
    public string[]? Nats       { get; set; }
    public string[]? Monitoring { get; set; }
    public string[]? Cluster    { get; set; }
    public string[]? Profile    { get; set; }
    public string[]? WebSocket  { get; set; }
}

/// <summary>
/// Wraps a <see cref="Stream"/> with an optional "pre-read" buffer that is
/// drained first, then falls through to the underlying stream.
/// Used when we peek at the first bytes of a connection to detect TLS.
/// Mirrors Go <c>tlsMixConn</c>.
/// </summary>
internal sealed class TlsMixConn : Stream
{
    private readonly Stream         _inner;
    private          System.IO.MemoryStream? _pre;

    public TlsMixConn(Stream inner, byte[] preRead)
    {
        _inner = inner;
        if (preRead.Length > 0)
            _pre = new System.IO.MemoryStream(preRead, writable: false);
    }

    public override bool CanRead  => true;
    public override bool CanSeek  => false;
    public override bool CanWrite => _inner.CanWrite;
    public override long Length   => throw new NotSupportedException();
    public override long Position
    {
        get => throw new NotSupportedException();
        set => throw new NotSupportedException();
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (_pre is { } pre)
        {
            var n = pre.Read(buffer, offset, count);
            if (pre.Position >= pre.Length)
                _pre = null;
            return n;
        }
        return _inner.Read(buffer, offset, count);
    }

    public override void Write(byte[] buffer, int offset, int count) =>
        _inner.Write(buffer, offset, count);

    public override void Flush() => _inner.Flush();

    public override long Seek(long offset, SeekOrigin origin) =>
        throw new NotSupportedException();

    public override void SetLength(long value) =>
        throw new NotSupportedException();

    protected override void Dispose(bool disposing)
    {
        if (disposing) { _pre?.Dispose(); _inner.Dispose(); }
        base.Dispose(disposing);
    }
}

/// <summary>
/// Captures HTTP server log lines and routes them through the server's
/// error logger.
/// Mirrors Go <c>captureHTTPServerLog</c> in server.go.
/// </summary>
internal sealed class CaptureHttpServerLog : System.IO.TextWriter
{
    private readonly NatsServer _server;
    private readonly string     _prefix;

    public CaptureHttpServerLog(NatsServer server, string prefix)
    {
        _server = server;
        _prefix = prefix;
    }

    public override System.Text.Encoding Encoding => System.Text.Encoding.UTF8;

    public override void Write(string? value)
    {
        if (value is null) return;
        // Strip leading "http:" prefix that .NET's HttpListener sometimes emits.
        var msg = value.StartsWith("http:", StringComparison.Ordinal) ? value[6..] : value;
        _server.Errorf("{0}{1}", _prefix, msg.TrimEnd('\r', '\n'));
    }
}

/// <summary>
/// Stub for JWT account claims (session 06/11).
/// Mirrors Go <c>jwt.AccountClaims</c> from nats.io/jwt/v2.
/// Full implementation will decode a signed JWT and expose limits/imports/exports.
/// </summary>
public sealed class AccountClaims
{
    /// <summary>Account public NKey (subject of the JWT).</summary>
    public string Subject { get; set; } = string.Empty;

    /// <summary>Operator or signing-key that issued this JWT.</summary>
    public string Issuer  { get; set; } = string.Empty;

    /// <summary>
    /// Human-readable name tag for the account (JWT claim field <c>name</c>).
    /// Mirrors Go <c>jwt.AccountClaims.Name</c>.
    /// </summary>
    public string? Name { get; set; }

    /// <summary>
    /// Minimal stub decoder — returns null until session 11 provides full JWT parsing.
    /// In Go: <c>jwt.DecodeAccountClaims(claimJWT)</c>.
    /// </summary>
    public static AccountClaims? TryDecode(string claimJwt)
    {
        if (string.IsNullOrEmpty(claimJwt)) return null;
        // TODO: implement proper JWT decoding in session 11.
        return null;
    }
}
