// Go reference: golang/nats-server/server/client_test.go
// Ports ~52 tests covering client protocol behaviors not yet tested in existing files.

using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging.Abstractions;
using NATS.Server;
using NATS.Server.Auth;
using NATS.Server.Protocol;

namespace NATS.Server.Tests;

/// <summary>
/// Protocol-level parity tests ported from Go client_test.go.
/// Each test starts a real NatsServer and uses raw TCP sockets for
/// wire-level assertions.
/// </summary>
public class ClientProtocolParityTests
{
    // ---------------------------------------------------------------------------
    // Helpers (self-contained, duplicated per task spec)
    // ---------------------------------------------------------------------------

    private static int GetFreePort()
    {
        using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
        return ((IPEndPoint)sock.LocalEndPoint!).Port;
    }

    private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
    {
        using var cts = new CancellationTokenSource(timeoutMs);
        var sb = new StringBuilder();
        var buf = new byte[8192];
        while (!sb.ToString().Contains(expected))
        {
            var n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token);
            if (n == 0) break;
            sb.Append(Encoding.ASCII.GetString(buf, 0, n));
        }

        return sb.ToString();
    }

    private static async Task<string> ReadAllAvailableAsync(Socket sock, int timeoutMs = 1000)
    {
        using var cts = new CancellationTokenSource(timeoutMs);
        var sb = new StringBuilder();
        var buf = new byte[8192];
        try
        {
            while (true)
            {
                var n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token);
                if (n == 0) break;
                sb.Append(Encoding.ASCII.GetString(buf, 0, n));
            }
        }
        catch (OperationCanceledException)
        {
            // Expected
        }

        return sb.ToString();
    }

    private static int CountOccurrences(string haystack, string needle)
    {
        int count = 0, index = 0;
        while ((index = haystack.IndexOf(needle, index, StringComparison.Ordinal)) >= 0)
        {
            count++;
            index += needle.Length;
        }

        return count;
    }

    /// <summary>
    /// Creates a running server and returns (server, port, cts).
    /// Caller must cancel cts and dispose server.
    /// </summary>
    private static async Task<(NatsServer Server, int Port, CancellationTokenSource Cts)>
        StartServerAsync(NatsOptions? options = null)
    {
        var port = GetFreePort();
        options ??= new NatsOptions();
        options.Port = port;
        var cts = new CancellationTokenSource();
        var server = new NatsServer(options, NullLoggerFactory.Instance);
        _ = server.StartAsync(cts.Token);
        await server.WaitForReadyAsync();
        return (server, port, cts);
    }

    /// <summary>
    /// Connects a raw TCP socket, reads INFO, sends CONNECT, and returns the socket.
    /// </summary>
    private static async Task<Socket> ConnectAndHandshakeAsync(int port, string connectJson = "{}")
    {
        var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        await sock.ConnectAsync(IPAddress.Loopback, port);
        await ReadUntilAsync(sock, "\r\n"); // drain INFO
        await sock.SendAsync(Encoding.ASCII.GetBytes($"CONNECT {connectJson}\r\n"));
        return sock;
    }

    /// <summary>
    /// Connects and verifies PING/PONG handshake completes.
    /// </summary>
    private static async Task<Socket> ConnectAndPingAsync(int port, string connectJson = "{}")
    {
        var sock = await ConnectAndHandshakeAsync(port, connectJson);
        await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
        await ReadUntilAsync(sock, "PONG\r\n");
        return sock;
    }

    // ---------------------------------------------------------------------------
    // Test: INFO response parsing (TestClientCreateAndInfo)
    // ---------------------------------------------------------------------------

    // Go: TestClientCreateAndInfo server/client_test.go:202
    [Fact]
    public async Task Info_response_contains_valid_json()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);

            var info = await ReadUntilAsync(sock, "\r\n");
            info.ShouldStartWith("INFO ");

            var jsonStart = info.IndexOf('{');
            var jsonEnd = info.LastIndexOf('}');
            jsonStart.ShouldBeGreaterThanOrEqualTo(0);
            jsonEnd.ShouldBeGreaterThan(jsonStart);

            var jsonStr = info[jsonStart..(jsonEnd + 1)];
            var serverInfo = JsonSerializer.Deserialize<ServerInfo>(jsonStr);
            serverInfo.ShouldNotBeNull();
            serverInfo!.MaxPayload.ShouldBeGreaterThan(0);
            serverInfo.Port.ShouldBe(port);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientCreateAndInfo server/client_test.go:202
    [Fact]
    public async Task Info_response_max_payload_matches_server_config()
    {
        var maxPayload = 512 * 1024; // 512KB
        var (server, port, cts) = await StartServerAsync(new NatsOptions { MaxPayload = maxPayload });
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);

            var info = await ReadUntilAsync(sock, "\r\n");
            var jsonStr = info[(info.IndexOf('{'))..(info.LastIndexOf('}') + 1)];
            var serverInfo = JsonSerializer.Deserialize<ServerInfo>(jsonStr);
            serverInfo!.MaxPayload.ShouldBe(maxPayload);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientCreateAndInfo server/client_test.go:202
    [Fact]
    public async Task Info_auth_required_reflects_server_config()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions { Authorization = "secret" });
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);

            var info = await ReadUntilAsync(sock, "\r\n");
            info.ShouldContain("\"auth_required\":true");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientCreateAndInfo server/client_test.go:202
    [Fact]
    public async Task Info_auth_required_absent_when_no_auth()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);

            var info = await ReadUntilAsync(sock, "\r\n");
            // auth_required should not be present (or should be false/omitted)
            info.ShouldNotContain("\"auth_required\":true");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: CONNECT parsing and flags
    // ---------------------------------------------------------------------------

    // Go: TestClientConnect server/client_test.go:475
    [Fact]
    public async Task Connect_with_verbose_true_returns_ok()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");
            await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));

            // With verbose:true, the CONNECT itself triggers +OK, then PING triggers PONG + +OK
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("+OK\r\n");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientConnect server/client_test.go:475
    [Fact]
    public async Task Connect_with_verbose_false_does_not_return_ok_for_pub()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port, "{\"verbose\":false}");

            // PUB should not trigger +OK when verbose is false
            await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");

            response.ShouldNotContain("+OK");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientConnect server/client_test.go:475
    [Fact]
    public async Task Connect_with_verbose_true_returns_ok_for_sub()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");
            // Drain the +OK from CONNECT
            await ReadUntilAsync(sock, "+OK\r\n");

            await sock.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");

            // SUB should trigger +OK in verbose mode
            response.ShouldContain("+OK\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientConnect server/client_test.go:475
    [Fact]
    public async Task Connect_with_verbose_true_returns_ok_for_unsub()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");
            await ReadUntilAsync(sock, "+OK\r\n"); // drain CONNECT +OK

            await sock.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nUNSUB 1\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");

            // Should get two +OK (SUB + UNSUB) plus PONG
            CountOccurrences(response, "+OK\r\n").ShouldBeGreaterThanOrEqualTo(2);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientConnect server/client_test.go:475
    [Fact]
    public async Task Connect_with_verbose_true_returns_ok_for_pub()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");
            await ReadUntilAsync(sock, "+OK\r\n");

            await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");

            // PUB should trigger +OK in verbose mode
            response.ShouldContain("+OK\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientConnect server/client_test.go:475
    [Fact]
    public async Task Connect_parses_user_and_pass()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions
        {
            Users = [new User { Username = "derek", Password = "foo" }],
        });
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port,
                "{\"user\":\"derek\",\"pass\":\"foo\"}");
            await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientConnect server/client_test.go:475
    [Fact]
    public async Task Connect_parses_auth_token()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions
        {
            Authorization = "YZZ222",
        });
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port,
                "{\"auth_token\":\"YZZ222\"}");
            await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientConnect server/client_test.go:475
    [Fact]
    public async Task Connect_parses_client_name()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port,
                "{\"name\":\"my-test-client\"}");
            await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Protocol version negotiation
    // ---------------------------------------------------------------------------

    // Go: TestClientConnectProto server/client_test.go:537
    [Fact]
    public async Task Connect_proto_zero_accepted()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port,
                "{\"verbose\":false,\"pedantic\":false,\"protocol\":0}");
            await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientConnectProto server/client_test.go:537
    [Fact]
    public async Task Connect_proto_one_accepted()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port,
                "{\"verbose\":false,\"pedantic\":false,\"protocol\":1}");
            await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: PING/PONG
    // ---------------------------------------------------------------------------

    // Go: TestClientPing server/client_test.go:616
    [Fact]
    public async Task Ping_returns_pong()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port);
            await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientPing server/client_test.go:616
    [Fact]
    public async Task Multiple_pings_return_multiple_pongs()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\nPING\r\nPING\r\n"));
            // Read until we get at least 3 PONGs
            var response = await ReadAllAvailableAsync(sock, 3000);
            CountOccurrences(response, "PONG\r\n").ShouldBeGreaterThanOrEqualTo(3);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Max payload enforcement
    // ---------------------------------------------------------------------------

    // Go: TestClientMaxPending / max_payload enforcement (client_test.go:1976)
    [Fact]
    public async Task Max_payload_violation_closes_connection()
    {
        const int maxPayload = 100;
        var (server, port, cts) = await StartServerAsync(new NatsOptions { MaxPayload = maxPayload });
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            // Send a message that exceeds max payload
            var bigPayload = new string('X', maxPayload + 50);
            await sock.SendAsync(Encoding.ASCII.GetBytes(
                $"PUB foo {bigPayload.Length}\r\n{bigPayload}\r\n"));

            var response = await ReadAllAvailableAsync(sock, 3000);
            response.ShouldContain("-ERR 'Maximum Payload Violation'");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: max payload enforcement
    [Fact]
    public async Task Max_payload_exactly_at_limit_succeeds()
    {
        const int maxPayload = 100;
        var (server, port, cts) = await StartServerAsync(new NatsOptions { MaxPayload = maxPayload });
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            // Exactly at the limit should work
            var payload = new string('X', maxPayload);
            await sock.SendAsync(Encoding.ASCII.GetBytes(
                $"SUB foo 1\r\nPUB foo {payload.Length}\r\n{payload}\r\nPING\r\n"));

            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("MSG foo 1");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: max payload enforcement - connection closed after violation
    [Fact]
    public async Task Max_payload_violation_disconnects_client()
    {
        const int maxPayload = 50;
        var (server, port, cts) = await StartServerAsync(new NatsOptions { MaxPayload = maxPayload });
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            var bigPayload = new string('X', maxPayload + 100);
            await sock.SendAsync(Encoding.ASCII.GetBytes(
                $"PUB foo {bigPayload.Length}\r\n{bigPayload}\r\n"));

            // Read remaining data -- server should close the connection
            var response = await ReadAllAvailableAsync(sock, 3000);
            response.ShouldContain("-ERR 'Maximum Payload Violation'");

            // Verify connection is closed
            var buf = new byte[128];
            using var readCts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
            var n = await sock.ReceiveAsync(buf, SocketFlags.None, readCts.Token);
            n.ShouldBe(0);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Pedantic mode
    // ---------------------------------------------------------------------------

    // Go: pedantic mode validates subjects (TestClientConnect)
    [Fact]
    public async Task Pedantic_mode_rejects_invalid_publish_subject()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port, "{\"pedantic\":true}");

            // Publish to an invalid subject (contains space)
            await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo.*.bar 5\r\nhello\r\nPING\r\n"));

            var response = await ReadUntilAsync(sock, "PONG\r\n", 5000);
            response.ShouldContain("-ERR 'Invalid Publish Subject'");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: pedantic mode - valid publish subject should succeed
    [Fact]
    public async Task Pedantic_mode_accepts_valid_publish_subject()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port, "{\"pedantic\":true}");

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo.bar 1\r\nPUB foo.bar 5\r\nhello\r\nPING\r\n"));

            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("MSG foo.bar 1 5\r\nhello\r\n");
            response.ShouldNotContain("-ERR");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: pedantic mode - wildcard in publish subject not allowed
    [Fact]
    public async Task Pedantic_mode_rejects_wildcard_gt_in_publish()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port, "{\"pedantic\":true}");

            await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo.> 5\r\nhello\r\nPING\r\n"));

            var response = await ReadUntilAsync(sock, "PONG\r\n", 5000);
            response.ShouldContain("-ERR 'Invalid Publish Subject'");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Echo mode
    // ---------------------------------------------------------------------------

    // Go: TestClientPubSubNoEcho server/client_test.go:691
    [Fact]
    public async Task Echo_true_delivers_own_messages()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port, "{\"echo\":true}");

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n"));

            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("MSG foo 1 5\r\nhello\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientPubSubNoEcho server/client_test.go:691
    [Fact]
    public async Task Echo_false_suppresses_own_messages()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port, "{\"echo\":false}");

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n"));

            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldNotContain("MSG");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientPubWithQueueSubNoEcho server/client_test.go:1043
    [Fact]
    public async Task Echo_false_queue_sub_messages_delivered_to_other_client()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            // Publisher with echo:false also has a queue sub
            using var pub = await ConnectAndPingAsync(port, "{\"echo\":false}");
            // Other subscriber with echo:true
            using var sub = await ConnectAndPingAsync(port);

            // Both subscribe to same queue group
            await pub.SendAsync(Encoding.ASCII.GetBytes("SUB foo bar 1\r\nPING\r\n"));
            await ReadUntilAsync(pub, "PONG\r\n");
            await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo bar 1\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            // Publish 100 messages from the echo:false client
            var sb = new StringBuilder();
            for (int i = 0; i < 100; i++)
                sb.Append("PUB foo 5\r\nhello\r\n");
            sb.Append("PING\r\n");
            await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
            await ReadUntilAsync(pub, "PONG\r\n");

            // Send PING on sub to flush deliveries
            await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sub, "PONG\r\n");

            // The subscriber should receive all 100 messages since the publisher
            // has echo:false (all queue messages go to the other member)
            var msgCount = CountOccurrences(response, "MSG foo");
            msgCount.ShouldBe(100);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Two-token publish does not match single-token subscribe
    // ---------------------------------------------------------------------------

    // Go: TestTwoTokenPubMatchSingleTokenSub server/client_test.go:1287
    [Fact]
    public async Task Two_token_pub_does_not_match_single_token_sub()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            // Publish first (no subscribers), then subscribe to "foo", then publish "foo.bar"
            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "PUB foo.bar 5\r\nhello\r\nSUB foo 1\r\nPING\r\n"));
            var response1 = await ReadUntilAsync(sock, "PONG\r\n");
            response1.ShouldStartWith("PONG\r\n");

            // Now publish foo.bar again -- should NOT match "foo" subscription
            await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo.bar 5\r\nhello\r\nPING\r\n"));
            var response2 = await ReadUntilAsync(sock, "PONG\r\n");
            response2.ShouldStartWith("PONG\r\n");
            response2.ShouldNotContain("MSG");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Authorization failures
    // ---------------------------------------------------------------------------

    // Go: auth failure -- bad token
    [Fact]
    public async Task Auth_failure_wrong_token_closes_connection()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions
        {
            Authorization = "correct_token",
        });
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            await ReadUntilAsync(sock, "\r\n"); // INFO

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "CONNECT {\"auth_token\":\"wrong_token\"}\r\n"));

            var response = await ReadAllAvailableAsync(sock, 3000);
            response.ShouldContain("-ERR 'Authorization Violation'");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: auth failure -- wrong user/pass
    [Fact]
    public async Task Auth_failure_wrong_password_closes_connection()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions
        {
            Users = [new User { Username = "admin", Password = "secret" }],
        });
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            await ReadUntilAsync(sock, "\r\n"); // INFO

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "CONNECT {\"user\":\"admin\",\"pass\":\"wrongpass\"}\r\n"));

            var response = await ReadAllAvailableAsync(sock, 3000);
            response.ShouldContain("-ERR 'Authorization Violation'");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: auth success -- correct credentials
    [Fact]
    public async Task Auth_success_with_correct_user_pass()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions
        {
            Users = [new User { Username = "admin", Password = "secret" }],
        });
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port,
                "{\"user\":\"admin\",\"pass\":\"secret\"}");
            await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestAuthorizationTimeout server/client_test.go:1260
    [Fact]
    public async Task Auth_timeout_closes_connection()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions
        {
            Authorization = "my_token",
            AuthTimeout = TimeSpan.FromMilliseconds(500),
        });
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            await ReadUntilAsync(sock, "\r\n"); // INFO

            // Do NOT send CONNECT
            var response = await ReadUntilAsync(sock, "Authentication Timeout", timeoutMs: 5000);
            response.ShouldContain("-ERR 'Authentication Timeout'");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Permission violations
    // ---------------------------------------------------------------------------

    // Go: TestQueueSubscribePermissions server/client_test.go:899
    [Fact]
    public async Task Permission_violation_on_sub_denied_subject()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions
        {
            Users =
            [
                new User
                {
                    Username = "limited",
                    Password = "pass",
                    Permissions = new Permissions
                    {
                        Subscribe = new SubjectPermission { Allow = ["allowed.>"] },
                    },
                },
            ],
        });
        try
        {
            using var sock = await ConnectAndPingAsync(port,
                "{\"user\":\"limited\",\"pass\":\"pass\",\"verbose\":false}");

            // Subscribe to a denied subject
            await sock.SendAsync(Encoding.ASCII.GetBytes("SUB denied.topic 1\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("-ERR 'Permissions Violation for Subscription'");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: publish permission violation
    [Fact]
    public async Task Permission_violation_on_pub_denied_subject()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions
        {
            Users =
            [
                new User
                {
                    Username = "limited",
                    Password = "pass",
                    Permissions = new Permissions
                    {
                        Publish = new SubjectPermission { Allow = ["allowed.>"] },
                    },
                },
            ],
        });
        try
        {
            using var sock = await ConnectAndPingAsync(port,
                "{\"user\":\"limited\",\"pass\":\"pass\",\"verbose\":false}");

            await sock.SendAsync(Encoding.ASCII.GetBytes("PUB denied.topic 5\r\nhello\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("-ERR 'Permissions Violation for Publish'");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: publish permission -- allowed subject succeeds
    [Fact]
    public async Task Permission_allowed_publish_succeeds()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions
        {
            Users =
            [
                new User
                {
                    Username = "limited",
                    Password = "pass",
                    Permissions = new Permissions
                    {
                        Publish = new SubjectPermission { Allow = ["allowed.>"] },
                        Subscribe = new SubjectPermission { Allow = ["allowed.>"] },
                    },
                },
            ],
        });
        try
        {
            using var sock = await ConnectAndPingAsync(port,
                "{\"user\":\"limited\",\"pass\":\"pass\",\"verbose\":false}");

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB allowed.topic 1\r\nPUB allowed.topic 5\r\nhello\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("MSG allowed.topic 1 5\r\nhello\r\n");
            response.ShouldNotContain("-ERR");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: deny list for publish
    [Fact]
    public async Task Permission_deny_list_overrides_allow()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions
        {
            Users =
            [
                new User
                {
                    Username = "user1",
                    Password = "pass",
                    Permissions = new Permissions
                    {
                        Publish = new SubjectPermission
                        {
                            Allow = [">"],
                            Deny = ["secret.>"],
                        },
                    },
                },
            ],
        });
        try
        {
            using var sock = await ConnectAndPingAsync(port,
                "{\"user\":\"user1\",\"pass\":\"pass\",\"verbose\":false}");

            // Allowed
            await sock.SendAsync(Encoding.ASCII.GetBytes("PUB public.topic 5\r\nhello\r\nPING\r\n"));
            var r1 = await ReadUntilAsync(sock, "PONG\r\n");
            r1.ShouldNotContain("-ERR");

            // Denied
            await sock.SendAsync(Encoding.ASCII.GetBytes("PUB secret.data 5\r\nhello\r\nPING\r\n"));
            var r2 = await ReadUntilAsync(sock, "PONG\r\n");
            r2.ShouldContain("-ERR 'Permissions Violation for Publish'");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: No Responders (503 HMSG)
    // ---------------------------------------------------------------------------

    // Go: TestClientNoResponderSupport server/client_test.go:230
    [Fact]
    public async Task No_responders_requires_headers_flag()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            await ReadUntilAsync(sock, "\r\n"); // INFO

            // no_responders without headers should fail
            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "CONNECT {\"no_responders\":true}\r\n"));

            var response = await ReadAllAvailableAsync(sock, 3000);
            response.ShouldContain("-ERR");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientNoResponderSupport server/client_test.go:230
    [Fact]
    public async Task No_responders_with_headers_sends_503()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port,
                "{\"headers\":true,\"no_responders\":true}");

            // Subscribe on the reply inbox
            await sock.SendAsync(Encoding.ASCII.GetBytes("SUB reply.inbox 1\r\nPING\r\n"));
            await ReadUntilAsync(sock, "PONG\r\n");

            // Publish to a subject with no subscribers, with a reply subject
            await sock.SendAsync(Encoding.ASCII.GetBytes("PUB no.listeners reply.inbox 0\r\n\r\n"));

            var response = await ReadUntilAsync(sock, "NATS/1.0 503", timeoutMs: 5000);
            response.ShouldContain("HMSG reply.inbox");
            response.ShouldContain("NATS/1.0 503");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Header support
    // ---------------------------------------------------------------------------

    // Go: TestServerHeaderSupport server/client_test.go:259
    [Fact]
    public async Task Server_info_has_headers_true()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            var info = await ReadUntilAsync(sock, "\r\n");
            info.ShouldContain("\"headers\":true");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestServerHeaderSupport server/client_test.go:259
    // The .NET server currently always advertises headers:true (NoHeaderSupport
    // not fully wired to ServerInfo yet). Verify the default behavior.
    [Fact]
    public async Task Server_info_headers_defaults_to_true()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            var info = await ReadUntilAsync(sock, "\r\n");
            info.ShouldContain("\"headers\":true");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientHeaderDeliverMsg server/client_test.go:330
    [Fact]
    public async Task Hpub_delivers_hmsg_to_subscriber()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sub = await ConnectAndPingAsync(port, "{\"headers\":true}");
            using var pub = await ConnectAndPingAsync(port, "{\"headers\":true}");

            await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            // HPUB foo 12 14\r\nName:Derek\r\nOK
            await pub.SendAsync(Encoding.ASCII.GetBytes("HPUB foo 12 14\r\nName:Derek\r\nOK\r\n"));

            var response = await ReadUntilAsync(sub, "OK\r\n", timeoutMs: 5000);
            response.ShouldContain("HMSG foo 1 12 14\r\n");
            response.ShouldContain("Name:Derek");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Max subscriptions per connection
    // ---------------------------------------------------------------------------

    // Go: MaxSubs enforcement
    [Fact]
    public async Task Max_subs_enforced_closes_connection()
    {
        const int maxSubs = 5;
        var (server, port, cts) = await StartServerAsync(new NatsOptions { MaxSubs = maxSubs });
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            var sb = new StringBuilder();
            for (int i = 1; i <= maxSubs; i++)
                sb.Append($"SUB foo.{i} {i}\r\n");
            // One over the limit
            sb.Append($"SUB foo.overflow {maxSubs + 1}\r\n");

            await sock.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));

            var response = await ReadAllAvailableAsync(sock, 3000);
            response.ShouldContain("-ERR 'Maximum Subscriptions Exceeded'");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: MaxSubs -- exactly at limit is fine
    [Fact]
    public async Task Max_subs_exactly_at_limit_succeeds()
    {
        const int maxSubs = 3;
        var (server, port, cts) = await StartServerAsync(new NatsOptions { MaxSubs = maxSubs });
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            var sb = new StringBuilder();
            for (int i = 1; i <= maxSubs; i++)
                sb.Append($"SUB foo.{i} {i}\r\n");
            sb.Append("PING\r\n");

            await sock.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
            var response = await ReadUntilAsync(sock, "PONG\r\n");

            response.ShouldNotContain("-ERR");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Connection info (client ID in INFO)
    // ---------------------------------------------------------------------------

    // Go: TestClientCreateAndInfo -- server_id is unique
    [Fact]
    public async Task Info_contains_server_id()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            var info = await ReadUntilAsync(sock, "\r\n");

            var jsonStr = info[(info.IndexOf('{'))..(info.LastIndexOf('}') + 1)];
            var serverInfo = JsonSerializer.Deserialize<ServerInfo>(jsonStr);
            serverInfo!.ServerId.ShouldNotBeNullOrEmpty();
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: server tracks client count
    [Fact]
    public async Task Client_count_increments_on_connect()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            server.ClientCount.ShouldBe(0);

            using var sock1 = await ConnectAndPingAsync(port);
            server.ClientCount.ShouldBe(1);

            using var sock2 = await ConnectAndPingAsync(port);
            server.ClientCount.ShouldBe(2);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Client disconnect removes subscriptions
    // ---------------------------------------------------------------------------

    // Go: TestClientRemoveSubsOnDisconnect server/client_test.go:1227
    [Fact]
    public async Task Disconnect_removes_subscriptions_from_sublist()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nSUB bar 2\r\nSUB baz 3\r\nPING\r\n"));
            await ReadUntilAsync(sock, "PONG\r\n");

            server.SubList.Count.ShouldBe(3u);

            sock.Shutdown(SocketShutdown.Both);
            sock.Close();

            await Task.Delay(500);
            server.SubList.Count.ShouldBe(0u);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientMapRemoval server/client_test.go:1253
    [Fact]
    public async Task Disconnect_removes_client_from_server_map()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);
            server.ClientCount.ShouldBe(1);

            sock.Shutdown(SocketShutdown.Both);
            sock.Close();

            await Task.Delay(500);
            server.ClientCount.ShouldBe(0);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Close connection very early
    // ---------------------------------------------------------------------------

    // Go: TestCloseConnectionVeryEarly server/client_test.go:2448
    [Fact]
    public async Task Close_connection_immediately_after_connect()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            // Open and immediately close
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            sock.Close();

            await Task.Delay(500);
            server.ClientCount.ShouldBe(0);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Multiple connections
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Server_tracks_multiple_clients()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var c1 = await ConnectAndPingAsync(port);
            using var c2 = await ConnectAndPingAsync(port);
            using var c3 = await ConnectAndPingAsync(port);

            server.ClientCount.ShouldBe(3);

            c1.Shutdown(SocketShutdown.Both);
            c1.Close();
            await Task.Delay(300);

            server.ClientCount.ShouldBe(2);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Pub with reply
    // ---------------------------------------------------------------------------

    // Go: TestClientSimplePubSubWithReply server/client_test.go:712
    [Fact]
    public async Task Pub_with_reply_delivered_in_msg()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nPUB foo reply.to 5\r\nhello\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");

            response.ShouldContain("MSG foo 1 reply.to 5\r\nhello\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientNoBodyPubSubWithReply server/client_test.go:740
    [Fact]
    public async Task Empty_payload_with_reply_subject()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nPUB foo reply.to 0\r\n\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");

            response.ShouldContain("MSG foo 1 reply.to 0\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Unsub and auto-unsub
    // ---------------------------------------------------------------------------

    // Go: TestClientUnSub server/client_test.go:1110
    [Fact]
    public async Task Unsub_removes_subscription_only_matching_sid()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var pub = await ConnectAndPingAsync(port);
            using var sub = await ConnectAndPingAsync(port);

            await sub.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nSUB foo 2\r\nUNSUB 1\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
            await ReadUntilAsync(pub, "PONG\r\n");

            await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sub, "PONG\r\n");

            response.ShouldContain("MSG foo 2 5");
            response.ShouldNotContain("MSG foo 1 5");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientUnSubMax server/client_test.go:1145
    [Fact]
    public async Task Auto_unsub_max_delivers_exact_count()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var pub = await ConnectAndPingAsync(port);
            using var sub = await ConnectAndPingAsync(port);

            await sub.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nUNSUB 1 5\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            // Publish 10 messages
            var sb = new StringBuilder();
            for (int i = 0; i < 10; i++)
                sb.Append("PUB foo 1\r\nx\r\n");
            sb.Append("PING\r\n");
            await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
            await ReadUntilAsync(pub, "PONG\r\n");

            // Collect messages on subscriber
            await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadAllAvailableAsync(sub, 2000);

            CountOccurrences(response, "MSG foo 1").ShouldBe(5);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // Go: TestClientUnsubAfterAutoUnsub server/client_test.go:1205
    [Fact]
    public async Task Explicit_unsub_after_auto_unsub_removes_immediately()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var pub = await ConnectAndPingAsync(port);
            using var sub = await ConnectAndPingAsync(port);

            await sub.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nUNSUB 1 100\r\nUNSUB 1\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
            await ReadUntilAsync(pub, "PONG\r\n");

            await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadAllAvailableAsync(sub, 1000);

            response.ShouldNotContain("MSG foo");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Queue sub distribution
    // ---------------------------------------------------------------------------

    // Go: TestClientPubWithQueueSub server/client_test.go:768
    [Fact]
    public async Task Queue_sub_distributes_messages_across_sids()
    {
        const int count = 100;
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo g1 1\r\nSUB foo g1 2\r\nPING\r\n"));
            await ReadUntilAsync(sock, "PONG\r\n");

            var sb = new StringBuilder();
            for (int i = 0; i < count; i++)
                sb.Append("PUB foo 5\r\nhello\r\n");
            sb.Append("PING\r\n");
            await sock.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));

            var response = await ReadUntilAsync(sock, "PONG\r\n");

            var n1 = CountOccurrences(response, "MSG foo 1 5");
            var n2 = CountOccurrences(response, "MSG foo 2 5");
            (n1 + n2).ShouldBe(count);
            n1.ShouldBeGreaterThanOrEqualTo(20);
            n2.ShouldBeGreaterThanOrEqualTo(20);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Stats tracking
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Server_stats_track_in_msgs()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "PUB foo 5\r\nhello\r\nPUB foo 5\r\nhello\r\nPUB foo 5\r\nhello\r\nPING\r\n"));
            await ReadUntilAsync(sock, "PONG\r\n");

            Interlocked.Read(ref server.Stats.InMsgs).ShouldBeGreaterThanOrEqualTo(3);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    [Fact]
    public async Task Server_stats_track_in_bytes()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "PUB foo 10\r\n0123456789\r\nPING\r\n"));
            await ReadUntilAsync(sock, "PONG\r\n");

            Interlocked.Read(ref server.Stats.InBytes).ShouldBeGreaterThanOrEqualTo(10);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    [Fact]
    public async Task Server_stats_track_out_msgs()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n"));
            await ReadUntilAsync(sock, "PONG\r\n");

            Interlocked.Read(ref server.Stats.OutMsgs).ShouldBeGreaterThanOrEqualTo(1);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Slow consumer detection
    // ---------------------------------------------------------------------------

    // Go: TestNoClientLeakOnSlowConsumer server/client_test.go:2181
    [Fact]
    public async Task Slow_consumer_closes_connection()
    {
        const long maxPendingBytes = 1024;
        var (server, port, cts) = await StartServerAsync(new NatsOptions { MaxPending = maxPendingBytes });
        try
        {
            using var slowSub = await ConnectAndPingAsync(port, "{\"verbose\":false}");

            await slowSub.SendAsync(Encoding.ASCII.GetBytes("SUB flood 1\r\nPING\r\n"));
            await ReadUntilAsync(slowSub, "PONG\r\n");

            using var pub = await ConnectAndPingAsync(port, "{\"verbose\":false}");

            // Flood
            var payload = new string('X', 512);
            var sb = new StringBuilder();
            for (int i = 0; i < 50; i++)
                sb.Append($"PUB flood {payload.Length}\r\n{payload}\r\n");
            sb.Append("PING\r\n");
            await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
            await ReadUntilAsync(pub, "PONG\r\n");

            await Task.Delay(500);

            Interlocked.Read(ref server.Stats.SlowConsumers).ShouldBeGreaterThan(0);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Verbose mode on various operations
    // ---------------------------------------------------------------------------

    // Go: verbose mode -- PING gets +OK and PONG
    [Fact]
    public async Task Verbose_mode_ping_returns_ok_and_pong()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");
            await ReadUntilAsync(sock, "+OK\r\n"); // drain CONNECT +OK

            await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadAllAvailableAsync(sock, 2000);

            // Should get PONG and +OK for the PING
            response.ShouldContain("PONG\r\n");
            response.ShouldContain("+OK\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Cross-client message delivery
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Message_delivered_across_two_clients()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sub = await ConnectAndPingAsync(port);
            using var pub = await ConnectAndPingAsync(port);

            await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
            await ReadUntilAsync(pub, "PONG\r\n");

            await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sub, "PONG\r\n");
            response.ShouldContain("MSG foo 1 5\r\nhello\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    [Fact]
    public async Task Wildcard_sub_receives_matching_messages()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sub = await ConnectAndPingAsync(port);
            using var pub = await ConnectAndPingAsync(port);

            await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo.* 1\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            await pub.SendAsync(Encoding.ASCII.GetBytes(
                "PUB foo.bar 5\r\nhello\r\nPUB foo.baz 5\r\nworld\r\nPING\r\n"));
            await ReadUntilAsync(pub, "PONG\r\n");

            await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sub, "PONG\r\n");
            response.ShouldContain("MSG foo.bar 1 5\r\nhello\r\n");
            response.ShouldContain("MSG foo.baz 1 5\r\nworld\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    [Fact]
    public async Task Gt_wildcard_sub_receives_multi_token_messages()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sub = await ConnectAndPingAsync(port);
            using var pub = await ConnectAndPingAsync(port);

            await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo.> 1\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            await pub.SendAsync(Encoding.ASCII.GetBytes(
                "PUB foo.bar.baz 5\r\nhello\r\nPING\r\n"));
            await ReadUntilAsync(pub, "PONG\r\n");

            await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadUntilAsync(sub, "PONG\r\n");
            response.ShouldContain("MSG foo.bar.baz 1 5\r\nhello\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Auto-unsub exact message count
    // ---------------------------------------------------------------------------

    // Go: TestClientAutoUnsubExactReceived server/client_test.go:1183
    [Fact]
    public async Task Auto_unsub_with_max_1_delivers_exactly_one()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var pub = await ConnectAndPingAsync(port);
            using var sub = await ConnectAndPingAsync(port);

            await sub.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nUNSUB 1 1\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            var sb = new StringBuilder();
            for (int i = 0; i < 5; i++)
                sb.Append("PUB foo 2\r\nok\r\n");
            sb.Append("PING\r\n");
            await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
            await ReadUntilAsync(pub, "PONG\r\n");

            await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var response = await ReadAllAvailableAsync(sub, 2000);
            CountOccurrences(response, "MSG foo 1").ShouldBe(1);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: CONNECT with no_responders:true but without headers:true should error
    // ---------------------------------------------------------------------------

    // Go: TestClientNoResponderSupport server/client_test.go:230
    [Fact]
    public async Task No_responders_without_headers_is_rejected()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            await ReadUntilAsync(sock, "\r\n"); // INFO

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "CONNECT {\"no_responders\":true,\"headers\":false}\r\n"));

            var response = await ReadAllAvailableAsync(sock, 3000);
            response.ShouldContain("-ERR");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: HPUB without headers in CONNECT should fail
    // ---------------------------------------------------------------------------

    // Go: TestClientHeaderSupport server/client_test.go:295
    // Verify that HPUB with headers:true in CONNECT works correctly
    [Fact]
    public async Task Hpub_with_headers_connect_succeeds()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var pub = await ConnectAndPingAsync(port, "{\"headers\":true}");
            using var sub = await ConnectAndPingAsync(port, "{\"headers\":true}");

            await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            // HPUB with valid header block
            await pub.SendAsync(Encoding.ASCII.GetBytes(
                "HPUB foo 12 14\r\nName:Derek\r\nOK\r\n"));

            var response = await ReadUntilAsync(sub, "OK\r\n", timeoutMs: 5000);
            response.ShouldContain("HMSG foo 1 12 14\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Empty message body
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Zero_byte_payload_delivered_correctly()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndPingAsync(port);

            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nPUB foo 0\r\n\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");

            response.ShouldContain("MSG foo 1 0\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Maximum connections limit
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Max_connections_enforced()
    {
        var (server, port, cts) = await StartServerAsync(new NatsOptions { MaxConnections = 2 });
        try
        {
            using var c1 = await ConnectAndPingAsync(port);
            using var c2 = await ConnectAndPingAsync(port);

            // Third connection should be rejected
            using var c3 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await c3.ConnectAsync(IPAddress.Loopback, port);

            var response = await ReadAllAvailableAsync(c3, 3000);
            // The server should send an error about maximum connections
            response.ShouldContain("maximum connections exceeded");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Unsubscribe race (concurrent pub + unsub)
    // ---------------------------------------------------------------------------

    // Go: TestUnsubRace server/client_test.go:1306
    [Fact]
    public async Task Unsub_race_does_not_crash()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sub = await ConnectAndPingAsync(port);
            using var pub = await ConnectAndPingAsync(port);

            await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
            await ReadUntilAsync(sub, "PONG\r\n");

            // Start publishing concurrently
            var pubTask = Task.Run(async () =>
            {
                var sb = new StringBuilder();
                for (int i = 0; i < 1000; i++)
                    sb.Append("PUB foo 5\r\nhello\r\n");
                sb.Append("PING\r\n");
                await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
            });

            await Task.Delay(5);

            // Unsubscribe while messages are flowing
            await sub.SendAsync(Encoding.ASCII.GetBytes("UNSUB 1\r\nPING\r\n"));

            await pubTask;
            // As long as we don't crash, the test passes.
            // Drain remaining data
            await ReadAllAvailableAsync(sub, 2000);
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Verbose mode full lifecycle
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Verbose_mode_full_lifecycle_returns_ok_for_each_operation()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");

            // Drain +OK from CONNECT
            await ReadUntilAsync(sock, "+OK\r\n");

            // SUB -> +OK, PUB -> +OK, UNSUB -> +OK, PING -> PONG + +OK
            await sock.SendAsync(Encoding.ASCII.GetBytes(
                "SUB foo 1\r\nPUB foo 5\r\nhello\r\nUNSUB 1\r\nPING\r\n"));

            var response = await ReadUntilAsync(sock, "PONG\r\n");

            // At least 3 +OK (SUB, PUB, UNSUB) plus the one for PING
            CountOccurrences(response, "+OK\r\n").ShouldBeGreaterThanOrEqualTo(3);
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Multiple subscribers on same subject
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Multiple_subs_on_same_subject_all_receive()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sub1 = await ConnectAndPingAsync(port);
            using var sub2 = await ConnectAndPingAsync(port);
            using var pub = await ConnectAndPingAsync(port);

            await sub1.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
            await ReadUntilAsync(sub1, "PONG\r\n");
            await sub2.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
            await ReadUntilAsync(sub2, "PONG\r\n");

            await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
            await ReadUntilAsync(pub, "PONG\r\n");

            await sub1.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var r1 = await ReadUntilAsync(sub1, "PONG\r\n");
            r1.ShouldContain("MSG foo 1 5\r\nhello\r\n");

            await sub2.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
            var r2 = await ReadUntilAsync(sub2, "PONG\r\n");
            r2.ShouldContain("MSG foo 1 5\r\nhello\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Server info has server_id and version
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Info_has_server_id_and_version()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            var info = await ReadUntilAsync(sock, "\r\n");

            var jsonStr = info[(info.IndexOf('{'))..(info.LastIndexOf('}') + 1)];
            var si = JsonSerializer.Deserialize<ServerInfo>(jsonStr);
            si!.ServerId.ShouldNotBeNullOrEmpty();
            si.Version.ShouldNotBeNullOrEmpty();
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: Server info proto version
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Info_has_proto_version()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            var info = await ReadUntilAsync(sock, "\r\n");
            info.ShouldContain("\"proto\":");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: INFO contains host field
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Info_contains_host_field()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            var info = await ReadUntilAsync(sock, "\r\n");

            // host field should be present
            info.ShouldContain("\"host\":");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }

    // ---------------------------------------------------------------------------
    // Test: CONNECT with all fields
    // ---------------------------------------------------------------------------

    [Fact]
    public async Task Connect_with_all_optional_fields_accepted()
    {
        var (server, port, cts) = await StartServerAsync();
        try
        {
            var connect = """
                CONNECT {"verbose":false,"pedantic":false,"echo":true,"name":"test","lang":"csharp","version":"1.0","protocol":1,"headers":true,"no_responders":true}
                """.Trim();
            using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            await sock.ConnectAsync(IPAddress.Loopback, port);
            await ReadUntilAsync(sock, "\r\n"); // INFO
            await sock.SendAsync(Encoding.ASCII.GetBytes(connect + "\r\nPING\r\n"));
            var response = await ReadUntilAsync(sock, "PONG\r\n");
            response.ShouldContain("PONG\r\n");
        }
        finally
        {
            await cts.CancelAsync();
            server.Dispose();
        }
    }
}
