using System.Reflection;
using Shouldly;
using ZB.MOM.NatsNet.Server;

namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;

public sealed class JetStreamClusterStreamsGroupATests
{
    [Fact] // T:1578
    public void MonitorCluster_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("MonitorCluster", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1579
    public void CheckClusterSize_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("CheckClusterSize", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1580
    public void ClusterStreamConfig_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("ClusterStreamConfig", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1581
    public void MetaSnapshot_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("MetaSnapshot", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1582
    public void ApplyMetaSnapshot_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("ApplyMetaSnapshot", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1583
    public void DecodeMetaSnapshot_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("DecodeMetaSnapshot", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1584
    public void EncodeMetaSnapshot_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("EncodeMetaSnapshot", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1585
    public void CollectStreamAndConsumerChanges_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("CollectStreamAndConsumerChanges", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1586
    public void SetStreamAssignmentRecovering_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("SetStreamAssignmentRecovering", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1587
    public void SetConsumerAssignmentRecovering_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("SetConsumerAssignmentRecovering", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1588
    public void CopyGroup_StreamAssignment_ShouldCloneGroupPeers()
    {
        var sa = new StreamAssignment
        {
            Group = new RaftGroup { Name = "RG", Peers = ["A", "B"] },
            Config = new StreamConfig { Name = "S", Replicas = 3 },
        };

        var method = typeof(StreamAssignment).GetMethod("CopyGroup", BindingFlags.Instance | BindingFlags.NonPublic);
        method.ShouldNotBeNull();
        var copy = method!.Invoke(sa, []) as StreamAssignment;
        copy.ShouldNotBeNull();
        copy!.Group.ShouldNotBeNull();
        ReferenceEquals(copy.Group, sa.Group).ShouldBeFalse();
        copy.Group.Peers.SequenceEqual(sa.Group!.Peers).ShouldBeTrue();
    }

    [Fact] // T:1589
    public void CopyGroup_ConsumerAssignment_ShouldCloneGroupPeers()
    {
        var ca = new ConsumerAssignment
        {
            Name = "C",
            Stream = "S",
            Group = new RaftGroup { Name = "RG", Peers = ["A", "B"] },
        };

        var method = typeof(ConsumerAssignment).GetMethod("CopyGroup", BindingFlags.Instance | BindingFlags.NonPublic);
        method.ShouldNotBeNull();
        var copy = method!.Invoke(ca, []) as ConsumerAssignment;
        copy.ShouldNotBeNull();
        copy!.Group.ShouldNotBeNull();
        ReferenceEquals(copy.Group, ca.Group).ShouldBeFalse();
        copy.Group.Peers.SequenceEqual(ca.Group!.Peers).ShouldBeTrue();
    }

    [Fact] // T:1590
    public void MissingPeers_StreamAssignment_ShouldReflectReplicaGap()
    {
        var sa = new StreamAssignment
        {
            Config = new StreamConfig { Name = "S", Replicas = 3 },
            Group = new RaftGroup { Peers = ["A", "B"] },
        };
        var method = typeof(StreamAssignment).GetMethod("MissingPeers", BindingFlags.Instance | BindingFlags.NonPublic);
        method.ShouldNotBeNull();
        ((bool)method!.Invoke(sa, [])!).ShouldBeTrue();
    }

    [Fact] // T:1591
    public void ProcessAddPeer_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("ProcessAddPeer", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1592
    public void ProcessRemovePeer_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("ProcessRemovePeer", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1593
    public void RemovePeerFromStream_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("RemovePeerFromStream", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1594
    public void RemovePeerFromStreamLocked_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("RemovePeerFromStreamLocked", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1595
    public void HasPeerEntries_Method_ShouldExist()
    {
        typeof(JetStreamEngine).GetMethod("HasPeerEntries", BindingFlags.Instance | BindingFlags.NonPublic).ShouldNotBeNull();
    }

    [Fact] // T:1596
    public void RecoveryKey_StreamAssignment_ShouldUseAccountAndName()
    {
        var sa = new StreamAssignment
        {
            Client = new ClientInfo { Account = "A", ServiceName = "SA" },
            Config = new StreamConfig { Name = "ORDERS" },
        };
        var method = typeof(StreamAssignment).GetMethod("RecoveryKey", BindingFlags.Instance | BindingFlags.NonPublic);
        method.ShouldNotBeNull();
        method!.Invoke(sa, [])!.ToString().ShouldBe("SA:ORDERS");
    }

    [Fact] // T:1597
    public void StreamRecoveryKey_ConsumerAssignment_ShouldUseAccountAndStream()
    {
        var ca = new ConsumerAssignment
        {
            Client = new ClientInfo { Account = "A", ServiceName = "SA" },
            Stream = "ORDERS",
            Name = "C1",
        };
        var method = typeof(ConsumerAssignment).GetMethod("StreamRecoveryKey", BindingFlags.Instance | BindingFlags.NonPublic);
        method.ShouldNotBeNull();
        method!.Invoke(ca, [])!.ToString().ShouldBe("SA:ORDERS");
    }
}
