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

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

namespace ZB.MOM.NatsNet.Server;

// ============================================================================
// Session 14: Routes
// ============================================================================

/// <summary>
/// Designates whether a route was explicitly configured or discovered via gossip.
/// Mirrors Go <c>RouteType</c> iota in route.go.
/// Note: Go defines Implicit=0, Explicit=1 — we keep TombStone=2 for future use.
/// </summary>
public enum RouteType : int
{
    /// <summary>This route was learned from speaking to other routes.</summary>
    Implicit  = 0,
    /// <summary>This route was explicitly configured.</summary>
    Explicit  = 1,
    /// <summary>Reserved tombstone marker for removed routes.</summary>
    TombStone = 2,
}

/// <summary>
/// Gossip mode constants exchanged between route servers.
/// Mirrors the const block immediately after <c>routeInfo</c> in route.go.
/// Do not change values — they are part of the wire protocol.
/// </summary>
internal static class GossipMode
{
    public const byte Default  = 0;
    public const byte Disabled = 1;
    public const byte Override = 2;
}

/// <summary>
/// Per-connection route state embedded in <see cref="ClientConnection"/> when the
/// connection kind is <c>Router</c>.
/// Mirrors Go <c>route</c> struct in route.go.
/// </summary>
internal sealed class Route
{
    /// <summary>Remote server ID string.</summary>
    public string RemoteId     { get; set; } = string.Empty;

    /// <summary>Remote server name.</summary>
    public string RemoteName   { get; set; } = string.Empty;

    /// <summary>True if this server solicited the outbound connection.</summary>
    public bool DidSolicit     { get; set; }

    /// <summary>True if the connection should be retried on failure.</summary>
    public bool Retry          { get; set; }

    /// <summary>Leaf-node origin cluster flag (lnoc).</summary>
    public bool Lnoc           { get; set; }

    /// <summary>Leaf-node origin cluster with unsub support (lnocu).</summary>
    public bool Lnocu          { get; set; }

    /// <summary>Whether this is an explicit or implicit route.</summary>
    public RouteType RouteType { get; set; }

    /// <summary>Remote URL used to establish the connection.</summary>
    public Uri?      Url       { get; set; }

    /// <summary>True if the remote requires authentication.</summary>
    public bool AuthRequired   { get; set; }

    /// <summary>True if the remote requires TLS.</summary>
    public bool TlsRequired    { get; set; }

    /// <summary>True if JetStream is enabled on the remote.</summary>
    public bool JetStream      { get; set; }

    /// <summary>List of client connect URLs advertised by the remote.</summary>
    public List<string> ConnectUrls   { get; set; } = [];

    /// <summary>List of WebSocket connect URLs advertised by the remote.</summary>
    public List<string> WsConnUrls    { get; set; } = [];

    /// <summary>Gateway URL advertised by the remote.</summary>
    public string GatewayUrl          { get; set; } = string.Empty;

    /// <summary>Leaf-node URL advertised by the remote.</summary>
    public string LeafnodeUrl         { get; set; } = string.Empty;

    /// <summary>Cluster hash used for routing.</summary>
    public string Hash                { get; set; } = string.Empty;

    /// <summary>Server ID hash (6 bytes encoded).</summary>
    public string IdHash              { get; set; } = string.Empty;

    /// <summary>
    /// Index of this route in the <c>s.routes[remoteID]</c> slice.
    /// Initialized to -1 to indicate the route has not yet been registered.
    /// </summary>
    public int PoolIdx                { get; set; } = -1;

    /// <summary>
    /// When set, this route is pinned to a specific account and the account
    /// name will not be included in routed protocols.
    /// </summary>
    public byte[]? AccName            { get; set; }

    /// <summary>True if this is a connection to an old server or one with pooling disabled.</summary>
    public bool NoPool                { get; set; }

    /// <summary>
    /// Selected compression mode, which may differ from the server-configured mode.
    /// </summary>
    public string Compression         { get; set; } = string.Empty;

    /// <summary>
    /// Transient gossip mode byte sent when initiating an implicit route.
    /// </summary>
    public byte GossipMode            { get; set; }

    /// <summary>
    /// When set in a pooling scenario, signals that the route should trigger
    /// creation of the next pooled connection after receiving the first PONG.
    /// </summary>
    public RouteInfo? StartNewRoute   { get; set; }
}

/// <summary>
/// Minimal descriptor used to create a new route connection, including
/// the target URL, its type, and gossip mode.
/// Mirrors Go <c>routeInfo</c> struct (the small inner type) in route.go.
/// </summary>
internal sealed class RouteInfo
{
    public Uri?      Url        { get; set; }
    public RouteType RouteType  { get; set; }
    public byte      GossipMode { get; set; }
}

/// <summary>
/// CONNECT protocol payload exchanged between cluster servers.
/// Fields map 1-to-1 with the JSON tags in Go's <c>connectInfo</c>.
/// Mirrors Go <c>connectInfo</c> struct in route.go.
/// </summary>
internal sealed class ConnectInfo
{
    [JsonPropertyName("echo")]             public bool   Echo     { get; set; }
    [JsonPropertyName("verbose")]          public bool   Verbose  { get; set; }
    [JsonPropertyName("pedantic")]         public bool   Pedantic { get; set; }
    [JsonPropertyName("user")]             public string User     { get; set; } = string.Empty;
    [JsonPropertyName("pass")]             public string Pass     { get; set; } = string.Empty;
    [JsonPropertyName("tls_required")]     public bool   Tls      { get; set; }
    [JsonPropertyName("headers")]          public bool   Headers  { get; set; }
    [JsonPropertyName("name")]             public string Name     { get; set; } = string.Empty;
    [JsonPropertyName("cluster")]          public string Cluster  { get; set; } = string.Empty;
    [JsonPropertyName("cluster_dynamic")]  public bool   Dynamic  { get; set; }
    [JsonPropertyName("lnoc")]             public bool   Lnoc     { get; set; }
    [JsonPropertyName("lnocu")]            public bool   Lnocu    { get; set; }
    [JsonPropertyName("gateway")]          public string Gateway  { get; set; } = string.Empty;
}

/// <summary>
/// Holds a set of subscriptions for a single account, used when fanning out
/// route subscription interest.
/// Mirrors Go <c>asubs</c> struct in route.go.
/// </summary>
internal sealed class ASubs
{
    public Account?                          Account { get; set; }
    public List<Internal.Subscription>       Subs    { get; set; } = [];
}
