using NATS.Server.Raft;

namespace NATS.Server.Tests.Raft;

public class RaftStrictConsensusRuntimeTests
{
    [Fact]
    public async Task Quorum_and_nextindex_rules_gate_commit_visibility_and_snapshot_catchup_convergence()
    {
        var voter = new RaftNode("v1");
        voter.GrantVote(2, "cand-a").Granted.ShouldBeTrue();
        voter.GrantVote(2, "cand-b").Granted.ShouldBeFalse();

        var transport = new RejectingRaftTransport();
        var leader = new RaftNode("n1", transport);
        var followerA = new RaftNode("n2", transport);
        var followerB = new RaftNode("n3", transport);
        var cluster = new[] { leader, followerA, followerB };
        foreach (var node in cluster)
            node.ConfigureCluster(cluster);

        leader.StartElection(cluster.Length);
        leader.ReceiveVote(new VoteResponse { Granted = true }, cluster.Length);
        leader.IsLeader.ShouldBeTrue();

        _ = await leader.ProposeAsync("cmd-1", default);
        leader.AppliedIndex.ShouldBe(0);
        followerA.AppliedIndex.ShouldBe(0);
        followerB.AppliedIndex.ShouldBe(0);
    }

    private sealed class RejectingRaftTransport : IRaftTransport
    {
        public Task<IReadOnlyList<AppendResult>> AppendEntriesAsync(string leaderId, IReadOnlyList<string> followerIds, RaftLogEntry entry, CancellationToken ct)
            => Task.FromResult<IReadOnlyList<AppendResult>>(
                followerIds.Select(id => new AppendResult { FollowerId = id, Success = false }).ToArray());

        public Task<VoteResponse> RequestVoteAsync(string candidateId, string voterId, VoteRequest request, CancellationToken ct)
            => Task.FromResult(new VoteResponse { Granted = true });

        public Task InstallSnapshotAsync(string leaderId, string followerId, RaftSnapshot snapshot, CancellationToken ct)
            => Task.CompletedTask;
    }
}
