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

using System.Text.Json.Serialization;

namespace ZB.MOM.NatsNet.Server;

// ============================================================================
// SortOpt — string wrapper type for connection-list sort options
// Mirrors Go <c>SortOpt string</c> in server/monitor_sort_opts.go.
// ============================================================================

/// <summary>
/// A strongly-typed sort option for <see cref="ConnzOptions.Sort"/>.
/// Wraps a raw string value corresponding to the JSON sort key.
/// Mirrors Go <c>SortOpt string</c> in server/monitor_sort_opts.go.
/// </summary>
public sealed class SortOpt
{
    private readonly string _value;

    private SortOpt(string value) => _value = value;

    /// <summary>Returns the raw sort-option string value.</summary>
    public override string ToString() => _value;

    /// <summary>Allows implicit conversion from a string literal.</summary>
    public static implicit operator SortOpt(string value) => new(value);

    /// <summary>Allows implicit conversion back to a plain string.</summary>
    public static implicit operator string(SortOpt opt) => opt._value;

    public override bool Equals(object? obj) =>
        obj is SortOpt other && _value == other._value;

    public override int GetHashCode() => _value.GetHashCode();

    // ---- Well-known sort-option constants ----
    // Mirrors Go const block in monitor_sort_opts.go.

    /// <summary>Sort by connection ID (ascending). Mirrors Go <c>ByCid = "cid"</c>.</summary>
    public static readonly SortOpt ByCid = new("cid");

    /// <summary>Sort by connection start time (same as ByCid). Mirrors Go <c>ByStart = "start"</c>.</summary>
    public static readonly SortOpt ByStart = new("start");

    /// <summary>Sort by number of subscriptions (descending). Mirrors Go <c>BySubs = "subs"</c>.</summary>
    public static readonly SortOpt BySubs = new("subs");

    /// <summary>Sort by pending bytes waiting to be sent (descending). Mirrors Go <c>ByPending = "pending"</c>.</summary>
    public static readonly SortOpt ByPending = new("pending");

    /// <summary>Sort by number of outbound messages (descending). Mirrors Go <c>ByOutMsgs = "msgs_to"</c>.</summary>
    public static readonly SortOpt ByOutMsgs = new("msgs_to");

    /// <summary>Sort by number of inbound messages (descending). Mirrors Go <c>ByInMsgs = "msgs_from"</c>.</summary>
    public static readonly SortOpt ByInMsgs = new("msgs_from");

    /// <summary>Sort by bytes sent (descending). Mirrors Go <c>ByOutBytes = "bytes_to"</c>.</summary>
    public static readonly SortOpt ByOutBytes = new("bytes_to");

    /// <summary>Sort by bytes received (descending). Mirrors Go <c>ByInBytes = "bytes_from"</c>.</summary>
    public static readonly SortOpt ByInBytes = new("bytes_from");

    /// <summary>Sort by last activity time (descending). Mirrors Go <c>ByLast = "last"</c>.</summary>
    public static readonly SortOpt ByLast = new("last");

    /// <summary>Sort by idle duration (descending). Mirrors Go <c>ByIdle = "idle"</c>.</summary>
    public static readonly SortOpt ByIdle = new("idle");

    /// <summary>Sort by uptime (descending). Mirrors Go <c>ByUptime = "uptime"</c>.</summary>
    public static readonly SortOpt ByUptime = new("uptime");

    /// <summary>Sort by stop time — only valid on closed connections. Mirrors Go <c>ByStop = "stop"</c>.</summary>
    public static readonly SortOpt ByStop = new("stop");

    /// <summary>Sort by close reason — only valid on closed connections. Mirrors Go <c>ByReason = "reason"</c>.</summary>
    public static readonly SortOpt ByReason = new("reason");

    /// <summary>Sort by round-trip time (descending). Mirrors Go <c>ByRTT = "rtt"</c>.</summary>
    public static readonly SortOpt ByRtt = new("rtt");

    private static readonly HashSet<string> ValidValues =
    [
        "", "cid", "start", "subs", "pending",
        "msgs_to", "msgs_from", "bytes_to", "bytes_from",
        "last", "idle", "uptime", "stop", "reason", "rtt"
    ];

    /// <summary>
    /// Returns true if this sort option is a recognised value.
    /// Mirrors Go <c>SortOpt.IsValid()</c> in monitor_sort_opts.go.
    /// </summary>
    public bool IsValid() => ValidValues.Contains(_value);
}

// ============================================================================
// ConnInfos — sortable list wrapper for ConnInfo pointers
// Mirrors Go <c>ConnInfos []*ConnInfo</c> in monitor_sort_opts.go.
// ============================================================================

/// <summary>
/// A list of <see cref="ConnInfo"/> objects that can be sorted using one of
/// the <c>SortBy*</c> comparers defined in this file.
/// Mirrors Go <c>ConnInfos []*ConnInfo</c> in server/monitor_sort_opts.go.
/// </summary>
public sealed class ConnInfos : List<ConnInfo>
{
    public ConnInfos() { }
    public ConnInfos(IEnumerable<ConnInfo> items) : base(items) { }
}

// ============================================================================
// IComparer<ConnInfo> implementations — one per sort option
// Each class mirrors the corresponding Less() method in monitor_sort_opts.go.
// ============================================================================

/// <summary>Sort by connection ID (ascending). Mirrors Go <c>SortByCid</c>.</summary>
public sealed class SortByCid : IComparer<ConnInfo>
{
    public static readonly SortByCid Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        return x.Cid.CompareTo(y.Cid);
    }
}

/// <summary>Sort by number of subscriptions (ascending for underlying sort; caller reverses if needed).</summary>
/// Mirrors Go <c>SortBySubs</c>.
public sealed class SortBySubs : IComparer<ConnInfo>
{
    public static readonly SortBySubs Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        return x.NumSubs.CompareTo(y.NumSubs);
    }
}

/// <summary>Sort by pending bytes. Mirrors Go <c>SortByPending</c>.</summary>
public sealed class SortByPending : IComparer<ConnInfo>
{
    public static readonly SortByPending Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        return x.Pending.CompareTo(y.Pending);
    }
}

/// <summary>Sort by outbound message count. Mirrors Go <c>SortByOutMsgs</c>.</summary>
public sealed class SortByOutMsgs : IComparer<ConnInfo>
{
    public static readonly SortByOutMsgs Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        return x.OutMsgs.CompareTo(y.OutMsgs);
    }
}

/// <summary>Sort by inbound message count. Mirrors Go <c>SortByInMsgs</c>.</summary>
public sealed class SortByInMsgs : IComparer<ConnInfo>
{
    public static readonly SortByInMsgs Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        return x.InMsgs.CompareTo(y.InMsgs);
    }
}

/// <summary>Sort by outbound bytes. Mirrors Go <c>SortByOutBytes</c>.</summary>
public sealed class SortByOutBytes : IComparer<ConnInfo>
{
    public static readonly SortByOutBytes Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        return x.OutBytes.CompareTo(y.OutBytes);
    }
}

/// <summary>Sort by inbound bytes. Mirrors Go <c>SortByInBytes</c>.</summary>
public sealed class SortByInBytes : IComparer<ConnInfo>
{
    public static readonly SortByInBytes Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        return x.InBytes.CompareTo(y.InBytes);
    }
}

/// <summary>Sort by last activity timestamp. Mirrors Go <c>SortByLast</c>.</summary>
public sealed class SortByLast : IComparer<ConnInfo>
{
    public static readonly SortByLast Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        return x.LastActivity.CompareTo(y.LastActivity);
    }
}

/// <summary>
/// Sort by idle duration (time since last activity), relative to a supplied
/// reference time. Mirrors Go <c>SortByIdle</c>.
/// </summary>
public sealed class SortByIdle : IComparer<ConnInfo>
{
    private readonly DateTime _now;

    public SortByIdle(DateTime now) => _now = now;

    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        var idleX = _now - x.LastActivity;
        var idleY = _now - y.LastActivity;
        return idleX.CompareTo(idleY);
    }
}

/// <summary>
/// Sort by uptime (time the connection has been open), relative to a supplied
/// reference time. Mirrors Go <c>SortByUptime</c>.
/// </summary>
public sealed class SortByUptime : IComparer<ConnInfo>
{
    private readonly DateTime _now;

    public SortByUptime(DateTime now) => _now = now;

    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        var uptimeX = (x.Stop is null || x.Stop == default) ? _now - x.Start : x.Stop.Value - x.Start;
        var uptimeY = (y.Stop is null || y.Stop == default) ? _now - y.Start : y.Stop.Value - y.Start;
        return uptimeX.CompareTo(uptimeY);
    }
}

/// <summary>Sort by stop time (closed connections only). Mirrors Go <c>SortByStop</c>.</summary>
public sealed class SortByStop : IComparer<ConnInfo>
{
    public static readonly SortByStop Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        // If either stop is null treat as zero (shouldn't happen for closed-only queries)
        var stopX = x.Stop ?? DateTime.MinValue;
        var stopY = y.Stop ?? DateTime.MinValue;
        return stopX.CompareTo(stopY);
    }
}

/// <summary>Sort by close reason string. Mirrors Go <c>SortByReason</c>.</summary>
public sealed class SortByReason : IComparer<ConnInfo>
{
    public static readonly SortByReason Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        return string.Compare(x.Reason, y.Reason, StringComparison.Ordinal);
    }
}

/// <summary>
/// Sort by round-trip time (nanoseconds, internal field).
/// Mirrors Go <c>SortByRTT</c>.
/// </summary>
public sealed class SortByRtt : IComparer<ConnInfo>
{
    public static readonly SortByRtt Instance = new();
    public int Compare(ConnInfo? x, ConnInfo? y)
    {
        if (x is null || y is null) return 0;
        return x.RttNanos.CompareTo(y.RttNanos);
    }
}
