using NATS.Server.Raft;

namespace NATS.Server.Raft.Tests.Raft;

public class RaftParityBatch3Tests
{
    [Fact]
    public async Task ProposeMulti_proposes_entries_in_order()
    {
        using var leader = ElectSingleNodeLeader();

        var indexes = await leader.ProposeMultiAsync(["cmd-1", "cmd-2", "cmd-3"], CancellationToken.None);

        indexes.Count.ShouldBe(3);
        indexes[0].ShouldBe(1);
        indexes[1].ShouldBe(2);
        indexes[2].ShouldBe(3);
        leader.Log.Entries.Count.ShouldBe(3);
    }

    [Fact]
    public void PeerState_tracks_lag_and_current_flags()
    {
        var peer = new RaftPeerState
        {
            PeerId = "n2",
            NextIndex = 10,
            MatchIndex = 7,
            LastContact = DateTime.UtcNow,
        };

        peer.RecalculateLag();
        peer.RefreshCurrent(TimeSpan.FromSeconds(1));

        peer.Lag.ShouldBe(2);
        peer.Current.ShouldBeTrue();

        peer.LastContact = DateTime.UtcNow - TimeSpan.FromSeconds(5);
        peer.RefreshCurrent(TimeSpan.FromSeconds(1));
        peer.Current.ShouldBeFalse();
    }

    [Fact]
    public void CommittedEntry_contains_index_and_entries()
    {
        var entries = new[]
        {
            new RaftLogEntry(42, 3, "set x"),
            new RaftLogEntry(43, 3, "set y"),
        };

        var committed = new CommittedEntry(43, entries);

        committed.Index.ShouldBe(43);
        committed.Entries.Count.ShouldBe(2);
        committed.Entries[0].Command.ShouldBe("set x");
    }

    [Fact]
    public void RaftEntry_roundtrips_to_wire_shape()
    {
        var entry = new RaftEntry(RaftEntryType.AddPeer, new byte[] { 1, 2, 3 });

        var wire = entry.ToWire();
        var decoded = RaftEntry.FromWire(wire);

        decoded.Type.ShouldBe(RaftEntryType.AddPeer);
        decoded.Data.ShouldBe(new byte[] { 1, 2, 3 });
    }

    private static RaftNode ElectSingleNodeLeader()
    {
        var node = new RaftNode("n1");
        node.ConfigureCluster([node]);
        node.StartElection(1);
        node.IsLeader.ShouldBeTrue();
        return node;
    }
}
