using NATS.Server.Monitoring;

namespace NATS.Server.Monitoring.Tests.Monitoring;

/// <summary>
/// Unit tests for ConnzSortOption enum, ConnzSorter.Parse, and ConnzSorter.Sort.
/// All tests exercise the types in isolation — no running server required.
/// Go reference: monitor_test.go — TestConnzSortedByCid, TestConnzSortedByBytesTo,
///               TestConnzSortedByMsgsFrom, TestConnzSortedByStart, monitor_sort_opts.go.
/// </summary>
public class ConnzSortTests
{
    // -----------------------------------------------------------------------
    // Helper — build a ConnInfo with specific field values
    // -----------------------------------------------------------------------

    private static ConnInfo MakeConn(
        ulong cid = 1,
        long outBytes = 0,
        long inBytes = 0,
        long outMsgs = 0,
        long inMsgs = 0,
        uint numSubs = 0,
        int pending = 0,
        DateTime connectedAt = default,
        DateTime lastActivity = default) =>
        new()
        {
            Cid = cid,
            OutBytes = outBytes,
            InBytes = inBytes,
            OutMsgs = outMsgs,
            InMsgs = inMsgs,
            NumSubs = numSubs,
            Pending = pending,
            Start = connectedAt == default ? DateTime.UtcNow : connectedAt,
            LastActivity = lastActivity == default ? DateTime.UtcNow : lastActivity,
        };

    // -----------------------------------------------------------------------
    // ConnzSorter.Parse tests
    // -----------------------------------------------------------------------

    [Fact]
    public void Parse_ConnectionId_Default()
    {
        // Go reference: monitor_sort_opts.go — empty/unknown value defaults to CID sort
        ConnzSorter.Parse(null).ShouldBe(ConnzSortOption.ConnectionId);
        ConnzSorter.Parse("").ShouldBe(ConnzSortOption.ConnectionId);
        ConnzSorter.Parse("   ").ShouldBe(ConnzSortOption.ConnectionId);
        ConnzSorter.Parse("cid").ShouldBe(ConnzSortOption.ConnectionId);
    }

    [Fact]
    public void Parse_BytesTo_Parsed()
    {
        // Go reference: monitor_sort_opts.go — "bytes_to" => ByBytesTo
        ConnzSorter.Parse("bytes_to").ShouldBe(ConnzSortOption.BytesTo);
        ConnzSorter.Parse("BYTES_TO").ShouldBe(ConnzSortOption.BytesTo);
    }

    [Fact]
    public void Parse_MsgsFrom_Parsed()
    {
        // Go reference: monitor_sort_opts.go — "msgs_from" => ByMsgsFrom
        ConnzSorter.Parse("msgs_from").ShouldBe(ConnzSortOption.MsgsFrom);
        ConnzSorter.Parse("MSGS_FROM").ShouldBe(ConnzSortOption.MsgsFrom);
    }

    [Fact]
    public void Parse_Start_Parsed()
    {
        // Go reference: monitor_sort_opts.go — "start" => ByStart
        ConnzSorter.Parse("start").ShouldBe(ConnzSortOption.Start);
        ConnzSorter.Parse("START").ShouldBe(ConnzSortOption.Start);
    }

    [Fact]
    public void Parse_Unknown_ReturnsDefault()
    {
        // Go reference: monitor_sort_opts.go — unrecognised string falls back to CID
        ConnzSorter.Parse("not_a_sort_key").ShouldBe(ConnzSortOption.ConnectionId);
        ConnzSorter.Parse("xyz").ShouldBe(ConnzSortOption.ConnectionId);
        ConnzSorter.Parse("RANDOM").ShouldBe(ConnzSortOption.ConnectionId);
    }

    // -----------------------------------------------------------------------
    // ConnzSorter.Sort tests
    // -----------------------------------------------------------------------

    [Fact]
    public void Sort_ByConnectionId_Ascending()
    {
        // Go reference: monitor_test.go TestConnzSortedByCid — CID ascending is default
        var conns = new[]
        {
            MakeConn(cid: 3),
            MakeConn(cid: 1),
            MakeConn(cid: 2),
        };

        var result = ConnzSorter.Sort(conns, ConnzSortOption.ConnectionId);

        result.Count.ShouldBe(3);
        result[0].Cid.ShouldBe(1UL);
        result[1].Cid.ShouldBe(2UL);
        result[2].Cid.ShouldBe(3UL);
    }

    [Fact]
    public void Sort_ByBytesTo_Descending()
    {
        // Go reference: monitor_test.go TestConnzSortedByBytesTo — OutBytes descending
        var conns = new[]
        {
            MakeConn(cid: 1, outBytes: 100),
            MakeConn(cid: 2, outBytes: 300),
            MakeConn(cid: 3, outBytes: 200),
        };

        var result = ConnzSorter.Sort(conns, ConnzSortOption.BytesTo);

        result.Count.ShouldBe(3);
        result[0].OutBytes.ShouldBe(300);
        result[1].OutBytes.ShouldBe(200);
        result[2].OutBytes.ShouldBe(100);
    }

    [Fact]
    public void Sort_ByStart_Ascending()
    {
        // Go reference: monitor_test.go TestConnzSortedByStart — ConnectedAt ascending
        var t0 = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        var t1 = t0.AddMinutes(1);
        var t2 = t0.AddMinutes(2);

        var conns = new[]
        {
            MakeConn(cid: 1, connectedAt: t2),
            MakeConn(cid: 2, connectedAt: t0),
            MakeConn(cid: 3, connectedAt: t1),
        };

        var result = ConnzSorter.Sort(conns, ConnzSortOption.Start);

        result.Count.ShouldBe(3);
        result[0].Start.ShouldBe(t0);
        result[1].Start.ShouldBe(t1);
        result[2].Start.ShouldBe(t2);
    }

    [Fact]
    public void Sort_ByMsgsFrom_Descending()
    {
        // Go reference: monitor_test.go TestConnzSortedByMsgsFrom — InMsgs descending
        var conns = new[]
        {
            MakeConn(cid: 1, inMsgs: 50),
            MakeConn(cid: 2, inMsgs: 200),
            MakeConn(cid: 3, inMsgs: 10),
        };

        var result = ConnzSorter.Sort(conns, ConnzSortOption.MsgsFrom);

        result.Count.ShouldBe(3);
        result[0].InMsgs.ShouldBe(200);
        result[1].InMsgs.ShouldBe(50);
        result[2].InMsgs.ShouldBe(10);
    }

    [Fact]
    public void Sort_EmptyList_ReturnsEmpty()
    {
        // Sorting an empty input should not throw and should return an empty list.
        var result = ConnzSorter.Sort([], ConnzSortOption.BytesTo);

        result.ShouldBeEmpty();
    }
}
