using System.Text.Json;
using NATS.Server.Events;

namespace NATS.Server.Monitoring.Tests.Events;

/// <summary>
/// Tests that all event DTOs have complete JSON fields matching Go's output.
/// Go reference: events.go:100-300 — TypedEvent, ServerInfo, ClientInfo,
/// DataStats, ServerStats, ConnectEventMsg, DisconnectEventMsg, AccountNumConns.
/// </summary>
public class EventPayloadTests
{
    // --- EventServerInfo ---

    [Fact]
    public void EventServerInfo_serializes_all_fields_matching_Go()
    {
        var info = new EventServerInfo
        {
            Name = "test-server",
            Host = "127.0.0.1",
            Id = "ABCDEF123456",
            Cluster = "test-cluster",
            Domain = "test-domain",
            Version = "2.10.0",
            Tags = ["tag1", "tag2"],
            Metadata = new Dictionary<string, string> { ["env"] = "test" },
            JetStream = true,
            Flags = 1,
            Seq = 42,
            Time = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc),
        };

        var json = JsonSerializer.Serialize(info);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.GetProperty("name").GetString().ShouldBe("test-server");
        root.GetProperty("host").GetString().ShouldBe("127.0.0.1");
        root.GetProperty("id").GetString().ShouldBe("ABCDEF123456");
        root.GetProperty("cluster").GetString().ShouldBe("test-cluster");
        root.GetProperty("domain").GetString().ShouldBe("test-domain");
        root.GetProperty("ver").GetString().ShouldBe("2.10.0");
        root.GetProperty("tags").GetArrayLength().ShouldBe(2);
        root.GetProperty("metadata").GetProperty("env").GetString().ShouldBe("test");
        root.GetProperty("jetstream").GetBoolean().ShouldBeTrue();
        root.GetProperty("flags").GetUInt64().ShouldBe(1UL);
        root.GetProperty("seq").GetUInt64().ShouldBe(42UL);
        root.GetProperty("time").GetDateTime().Year.ShouldBe(2025);
    }

    [Fact]
    public void EventServerInfo_omits_null_optional_fields()
    {
        var info = new EventServerInfo
        {
            Name = "s",
            Id = "ID",
        };

        var json = JsonSerializer.Serialize(info);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.TryGetProperty("cluster", out _).ShouldBeFalse();
        root.TryGetProperty("domain", out _).ShouldBeFalse();
        root.TryGetProperty("tags", out _).ShouldBeFalse();
        root.TryGetProperty("metadata", out _).ShouldBeFalse();
    }

    // --- EventClientInfo ---

    [Fact]
    public void EventClientInfo_serializes_all_fields_matching_Go()
    {
        var ci = new EventClientInfo
        {
            Start = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc),
            Stop = new DateTime(2025, 1, 1, 1, 0, 0, DateTimeKind.Utc),
            Host = "10.0.0.1",
            Id = 99,
            Account = "$G",
            Service = "orders",
            User = "admin",
            Name = "my-client",
            Lang = "go",
            Version = "1.30.0",
            RttNanos = 5_000_000, // 5ms
            Server = "srv-1",
            Cluster = "cluster-east",
            Alternates = ["alt1", "alt2"],
            Jwt = "eyJ...",
            IssuerKey = "OABC...",
            NameTag = "test-tag",
            Tags = ["dev"],
            Kind = "Client",
            ClientType = "nats",
            MqttClient = "mqtt-abc",
            Nonce = "nonce123",
        };

        var json = JsonSerializer.Serialize(ci);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.GetProperty("host").GetString().ShouldBe("10.0.0.1");
        root.GetProperty("id").GetUInt64().ShouldBe(99UL);
        root.GetProperty("acc").GetString().ShouldBe("$G");
        root.GetProperty("svc").GetString().ShouldBe("orders");
        root.GetProperty("user").GetString().ShouldBe("admin");
        root.GetProperty("name").GetString().ShouldBe("my-client");
        root.GetProperty("lang").GetString().ShouldBe("go");
        root.GetProperty("ver").GetString().ShouldBe("1.30.0");
        root.GetProperty("rtt").GetInt64().ShouldBe(5_000_000);
        root.GetProperty("server").GetString().ShouldBe("srv-1");
        root.GetProperty("cluster").GetString().ShouldBe("cluster-east");
        root.GetProperty("alts").GetArrayLength().ShouldBe(2);
        root.GetProperty("jwt").GetString().ShouldBe("eyJ...");
        root.GetProperty("issuer_key").GetString().ShouldBe("OABC...");
        root.GetProperty("name_tag").GetString().ShouldBe("test-tag");
        root.GetProperty("tags").GetArrayLength().ShouldBe(1);
        root.GetProperty("kind").GetString().ShouldBe("Client");
        root.GetProperty("client_type").GetString().ShouldBe("nats");
        root.GetProperty("client_id").GetString().ShouldBe("mqtt-abc");
        root.GetProperty("nonce").GetString().ShouldBe("nonce123");
    }

    [Fact]
    public void EventClientInfo_omits_null_optional_fields()
    {
        var ci = new EventClientInfo { Id = 1 };
        var json = JsonSerializer.Serialize(ci);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.TryGetProperty("svc", out _).ShouldBeFalse();
        root.TryGetProperty("user", out _).ShouldBeFalse();
        root.TryGetProperty("server", out _).ShouldBeFalse();
        root.TryGetProperty("cluster", out _).ShouldBeFalse();
        root.TryGetProperty("alts", out _).ShouldBeFalse();
        root.TryGetProperty("jwt", out _).ShouldBeFalse();
        root.TryGetProperty("issuer_key", out _).ShouldBeFalse();
        root.TryGetProperty("nonce", out _).ShouldBeFalse();
    }

    // --- DataStats ---

    [Fact]
    public void DataStats_serializes_with_optional_sub_stats()
    {
        var ds = new DataStats
        {
            Msgs = 100,
            Bytes = 2048,
            Gateways = new MsgBytesStats { Msgs = 10, Bytes = 256 },
            Routes = new MsgBytesStats { Msgs = 50, Bytes = 1024 },
            Leafs = new MsgBytesStats { Msgs = 40, Bytes = 768 },
        };

        var json = JsonSerializer.Serialize(ds);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.GetProperty("msgs").GetInt64().ShouldBe(100);
        root.GetProperty("bytes").GetInt64().ShouldBe(2048);
        root.GetProperty("gateways").GetProperty("msgs").GetInt64().ShouldBe(10);
        root.GetProperty("routes").GetProperty("bytes").GetInt64().ShouldBe(1024);
        root.GetProperty("leafs").GetProperty("msgs").GetInt64().ShouldBe(40);
    }

    [Fact]
    public void DataStats_omits_null_sub_stats()
    {
        var ds = new DataStats { Msgs = 5, Bytes = 50 };
        var json = JsonSerializer.Serialize(ds);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.TryGetProperty("gateways", out _).ShouldBeFalse();
        root.TryGetProperty("routes", out _).ShouldBeFalse();
        root.TryGetProperty("leafs", out _).ShouldBeFalse();
    }

    // --- ConnectEventMsg ---

    [Fact]
    public void ConnectEventMsg_has_correct_type_and_required_fields()
    {
        var evt = new ConnectEventMsg
        {
            Id = "evt-1",
            Time = DateTime.UtcNow,
            Server = new EventServerInfo { Name = "s1", Id = "SRV1" },
            Client = new EventClientInfo { Id = 42, Name = "test-client" },
        };

        var json = JsonSerializer.Serialize(evt);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.GetProperty("type").GetString().ShouldBe("io.nats.server.advisory.v1.client_connect");
        root.GetProperty("id").GetString().ShouldBe("evt-1");
        root.GetProperty("server").GetProperty("name").GetString().ShouldBe("s1");
        root.GetProperty("client").GetProperty("id").GetUInt64().ShouldBe(42UL);
    }

    // --- DisconnectEventMsg ---

    [Fact]
    public void DisconnectEventMsg_has_correct_type_and_data_stats()
    {
        var evt = new DisconnectEventMsg
        {
            Id = "evt-2",
            Time = DateTime.UtcNow,
            Server = new EventServerInfo { Name = "s1", Id = "SRV1" },
            Client = new EventClientInfo { Id = 42 },
            Sent = new DataStats { Msgs = 100, Bytes = 2000 },
            Received = new DataStats { Msgs = 50, Bytes = 1000 },
            Reason = "Client Closed",
        };

        var json = JsonSerializer.Serialize(evt);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.GetProperty("type").GetString().ShouldBe("io.nats.server.advisory.v1.client_disconnect");
        root.GetProperty("sent").GetProperty("msgs").GetInt64().ShouldBe(100);
        root.GetProperty("received").GetProperty("bytes").GetInt64().ShouldBe(1000);
        root.GetProperty("reason").GetString().ShouldBe("Client Closed");
    }

    // --- AccountNumConns ---

    [Fact]
    public void AccountNumConns_serializes_all_Go_AccountStat_fields()
    {
        var evt = new AccountNumConns
        {
            Id = "evt-3",
            Time = DateTime.UtcNow,
            Server = new EventServerInfo { Name = "s1", Id = "SRV1" },
            AccountName = "$G",
            Name = "Global",
            Connections = 5,
            LeafNodes = 2,
            TotalConnections = 100,
            NumSubscriptions = 42,
            Sent = new DataStats { Msgs = 500, Bytes = 10_000 },
            Received = new DataStats { Msgs = 400, Bytes = 8_000 },
            SlowConsumers = 1,
        };

        var json = JsonSerializer.Serialize(evt);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.GetProperty("type").GetString().ShouldBe("io.nats.server.advisory.v1.account_connections");
        root.GetProperty("acc").GetString().ShouldBe("$G");
        root.GetProperty("name").GetString().ShouldBe("Global");
        root.GetProperty("conns").GetInt32().ShouldBe(5);
        root.GetProperty("leafnodes").GetInt32().ShouldBe(2);
        root.GetProperty("total_conns").GetInt32().ShouldBe(100);
        root.GetProperty("num_subscriptions").GetUInt32().ShouldBe(42u);
        root.GetProperty("sent").GetProperty("msgs").GetInt64().ShouldBe(500);
        root.GetProperty("received").GetProperty("bytes").GetInt64().ShouldBe(8_000);
        root.GetProperty("slow_consumers").GetInt64().ShouldBe(1);
    }

    // --- ServerStatsMsg ---

    [Fact]
    public void ServerStatsMsg_has_sent_received_and_breakdown_fields()
    {
        var msg = new ServerStatsMsg
        {
            Server = new EventServerInfo { Name = "s1", Id = "SRV1", Seq = 1 },
            Stats = new ServerStatsData
            {
                Start = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                Mem = 100_000_000,
                Cores = 8,
                Cpu = 12.5,
                Connections = 10,
                TotalConnections = 500,
                ActiveAccounts = 3,
                Subscriptions = 50,
                Sent = new DataStats { Msgs = 1000, Bytes = 50_000 },
                Received = new DataStats { Msgs = 800, Bytes = 40_000 },
                InMsgs = 800,
                OutMsgs = 1000,
                InBytes = 40_000,
                OutBytes = 50_000,
                SlowConsumers = 2,
                SlowConsumerStats = new NATS.Server.Events.SlowConsumersStats { Clients = 1, Routes = 1 },
                StaleConnections = 3,
                StaleConnectionStats = new NATS.Server.Events.StaleConnectionStats { Clients = 2, Leafs = 1 },
                ActiveServers = 3,
                Routes = [new RouteStat { Id = 1, Name = "r1", Sent = new DataStats { Msgs = 10 }, Received = new DataStats { Msgs = 5 }, Pending = 0 }],
                Gateways = [new GatewayStat { Id = 1, Name = "gw1", Sent = new DataStats { Msgs = 20 }, Received = new DataStats { Msgs = 15 }, InboundConnections = 2 }],
            },
        };

        var json = JsonSerializer.Serialize(msg);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;
        var stats = root.GetProperty("statsz");

        stats.GetProperty("mem").GetInt64().ShouldBe(100_000_000);
        stats.GetProperty("cores").GetInt32().ShouldBe(8);
        stats.GetProperty("cpu").GetDouble().ShouldBe(12.5);
        stats.GetProperty("connections").GetInt32().ShouldBe(10);
        stats.GetProperty("total_connections").GetInt64().ShouldBe(500);
        stats.GetProperty("active_accounts").GetInt32().ShouldBe(3);
        stats.GetProperty("subscriptions").GetInt64().ShouldBe(50);
        stats.GetProperty("sent").GetProperty("msgs").GetInt64().ShouldBe(1000);
        stats.GetProperty("received").GetProperty("bytes").GetInt64().ShouldBe(40_000);
        stats.GetProperty("in_msgs").GetInt64().ShouldBe(800);
        stats.GetProperty("out_msgs").GetInt64().ShouldBe(1000);
        stats.GetProperty("slow_consumers").GetInt64().ShouldBe(2);
        stats.GetProperty("slow_consumer_stats").GetProperty("clients").GetInt64().ShouldBe(1);
        stats.GetProperty("stale_connections").GetInt64().ShouldBe(3);
        stats.GetProperty("stale_connection_stats").GetProperty("leafs").GetInt64().ShouldBe(1);
        stats.GetProperty("active_servers").GetInt32().ShouldBe(3);
        stats.GetProperty("routes").GetArrayLength().ShouldBe(1);
        stats.GetProperty("routes")[0].GetProperty("rid").GetUInt64().ShouldBe(1UL);
        stats.GetProperty("gateways").GetArrayLength().ShouldBe(1);
        stats.GetProperty("gateways")[0].GetProperty("name").GetString().ShouldBe("gw1");
    }

    // --- AuthErrorEventMsg ---

    [Fact]
    public void AuthErrorEventMsg_has_correct_type()
    {
        var evt = new AuthErrorEventMsg
        {
            Id = "evt-4",
            Time = DateTime.UtcNow,
            Server = new EventServerInfo { Name = "s1", Id = "SRV1" },
            Client = new EventClientInfo { Id = 99, Host = "10.0.0.1" },
            Reason = "Authorization Violation",
        };

        var json = JsonSerializer.Serialize(evt);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.GetProperty("type").GetString().ShouldBe("io.nats.server.advisory.v1.client_auth");
        root.GetProperty("reason").GetString().ShouldBe("Authorization Violation");
        root.GetProperty("client").GetProperty("host").GetString().ShouldBe("10.0.0.1");
    }

    // --- OcspPeerRejectEventMsg ---

    [Fact]
    public void OcspPeerRejectEventMsg_has_correct_type()
    {
        var evt = new OcspPeerRejectEventMsg
        {
            Id = "evt-5",
            Time = DateTime.UtcNow,
            Kind = "client",
            Server = new EventServerInfo { Name = "s1", Id = "SRV1" },
            Reason = "OCSP revoked",
        };

        var json = JsonSerializer.Serialize(evt);
        var doc = JsonDocument.Parse(json);
        var root = doc.RootElement;

        root.GetProperty("type").GetString().ShouldBe("io.nats.server.advisory.v1.ocsp_peer_reject");
        root.GetProperty("kind").GetString().ShouldBe("client");
        root.GetProperty("reason").GetString().ShouldBe("OCSP revoked");
    }

    // --- ShutdownEventMsg ---

    [Fact]
    public void ShutdownEventMsg_serializes_reason()
    {
        var evt = new ShutdownEventMsg
        {
            Server = new EventServerInfo { Name = "s1", Id = "SRV1" },
            Reason = "Server Shutdown",
        };

        var json = JsonSerializer.Serialize(evt);
        var doc = JsonDocument.Parse(json);
        doc.RootElement.GetProperty("reason").GetString().ShouldBe("Server Shutdown");
    }

    // --- AccNumConnsReq ---

    [Fact]
    public void AccNumConnsReq_serializes_account()
    {
        var req = new AccNumConnsReq
        {
            Server = new EventServerInfo { Name = "s1", Id = "SRV1" },
            Account = "myAccount",
        };

        var json = JsonSerializer.Serialize(req);
        var doc = JsonDocument.Parse(json);
        doc.RootElement.GetProperty("acc").GetString().ShouldBe("myAccount");
    }

    // --- Round-trip deserialization ---

    [Fact]
    public void ConnectEventMsg_roundtrips_through_json()
    {
        var original = new ConnectEventMsg
        {
            Id = "rt-1",
            Time = new DateTime(2025, 6, 15, 12, 0, 0, DateTimeKind.Utc),
            Server = new EventServerInfo { Name = "srv", Id = "SRV1", Version = "2.10.0", Seq = 5 },
            Client = new EventClientInfo
            {
                Id = 42,
                Host = "10.0.0.1",
                Account = "$G",
                Name = "test",
                Lang = "dotnet",
                Version = "1.0.0",
                RttNanos = 1_000_000,
                Kind = "Client",
            },
        };

        var json = JsonSerializer.Serialize(original);
        var deserialized = JsonSerializer.Deserialize<ConnectEventMsg>(json);

        deserialized.ShouldNotBeNull();
        deserialized.Type.ShouldBe(ConnectEventMsg.EventType);
        deserialized.Id.ShouldBe("rt-1");
        deserialized.Server.Name.ShouldBe("srv");
        deserialized.Server.Seq.ShouldBe(5UL);
        deserialized.Client.Id.ShouldBe(42UL);
        deserialized.Client.Kind.ShouldBe("Client");
        deserialized.Client.RttNanos.ShouldBe(1_000_000);
    }

    [Fact]
    public void ServerStatsMsg_roundtrips_through_json()
    {
        var original = new ServerStatsMsg
        {
            Server = new EventServerInfo { Name = "srv", Id = "SRV1" },
            Stats = new ServerStatsData
            {
                Connections = 10,
                Sent = new DataStats { Msgs = 100, Bytes = 5000 },
                Received = new DataStats { Msgs = 80, Bytes = 4000 },
                InMsgs = 80,
                OutMsgs = 100,
            },
        };

        var json = JsonSerializer.Serialize(original);
        var deserialized = JsonSerializer.Deserialize<ServerStatsMsg>(json);

        deserialized.ShouldNotBeNull();
        deserialized.Stats.Connections.ShouldBe(10);
        deserialized.Stats.Sent.Msgs.ShouldBe(100);
        deserialized.Stats.Received.Bytes.ShouldBe(4000);
    }
}
