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

namespace NATS.Server.Raft.Tests.Raft;

/// <summary>
/// Tests for core RAFT types: RaftState/RaftRole enum values, RaftLogEntry record,
/// VoteRequest/VoteResponse, AppendResult, RaftTermState, RaftSnapshot construction.
/// Go: server/raft.go core type definitions and server/raft_test.go encoding tests.
/// </summary>
public class RaftCoreTypeTests
{
    // Go: State constants in server/raft.go:50-54
    [Fact]
    public void RaftState_enum_has_correct_values()
    {
        ((byte)RaftState.Follower).ShouldBe((byte)0);
        ((byte)RaftState.Leader).ShouldBe((byte)1);
        ((byte)RaftState.Candidate).ShouldBe((byte)2);
        ((byte)RaftState.Closed).ShouldBe((byte)3);
    }

    // Go: State constants in server/raft.go:50-54
    [Fact]
    public void RaftRole_enum_has_follower_candidate_leader()
    {
        RaftRole.Follower.ShouldBe((RaftRole)0);
        RaftRole.Candidate.ShouldBe((RaftRole)1);
        RaftRole.Leader.ShouldBe((RaftRole)2);
    }

    // Go: Entry type in server/raft.go:63-72
    [Fact]
    public void RaftLogEntry_record_equality()
    {
        var a = new RaftLogEntry(Index: 1, Term: 1, Command: "test");
        var b = new RaftLogEntry(Index: 1, Term: 1, Command: "test");
        a.ShouldBe(b);
        (a == b).ShouldBeTrue();
    }

    // Go: Entry type in server/raft.go:63-72
    [Fact]
    public void RaftLogEntry_record_inequality_on_different_index()
    {
        var a = new RaftLogEntry(Index: 1, Term: 1, Command: "test");
        var b = new RaftLogEntry(Index: 2, Term: 1, Command: "test");
        a.ShouldNotBe(b);
        (a != b).ShouldBeTrue();
    }

    // Go: Entry type in server/raft.go:63-72
    [Fact]
    public void RaftLogEntry_record_inequality_on_different_term()
    {
        var a = new RaftLogEntry(Index: 1, Term: 1, Command: "test");
        var b = new RaftLogEntry(Index: 1, Term: 2, Command: "test");
        a.ShouldNotBe(b);
    }

    // Go: Entry type in server/raft.go:63-72
    [Fact]
    public void RaftLogEntry_record_inequality_on_different_command()
    {
        var a = new RaftLogEntry(Index: 1, Term: 1, Command: "alpha");
        var b = new RaftLogEntry(Index: 1, Term: 1, Command: "beta");
        a.ShouldNotBe(b);
    }

    // Go: TestNRGAppendEntryEncode server/raft_test.go:82
    [Fact]
    public void RaftLogEntry_json_round_trip()
    {
        var original = new RaftLogEntry(Index: 42, Term: 7, Command: "set-key-value");
        var json = JsonSerializer.Serialize(original);
        json.ShouldNotBeNullOrWhiteSpace();

        var decoded = JsonSerializer.Deserialize<RaftLogEntry>(json);
        decoded.ShouldNotBeNull();
        decoded.ShouldBe(original);
    }

    // Go: TestNRGAppendEntryEncode server/raft_test.go:82 — nil data case
    [Fact]
    public void RaftLogEntry_json_round_trip_empty_command()
    {
        var original = new RaftLogEntry(Index: 1, Term: 1, Command: string.Empty);
        var json = JsonSerializer.Serialize(original);
        var decoded = JsonSerializer.Deserialize<RaftLogEntry>(json);
        decoded.ShouldNotBeNull();
        decoded.Command.ShouldBe(string.Empty);
    }

    // Go: voteRequest struct in server/raft.go
    [Fact]
    public void VoteRequest_default_values()
    {
        var vr = new VoteRequest();
        vr.Term.ShouldBe(0);
        vr.CandidateId.ShouldBe(string.Empty);
    }

    // Go: voteRequest struct in server/raft.go
    [Fact]
    public void VoteRequest_init_properties()
    {
        var vr = new VoteRequest { Term = 5, CandidateId = "node-1" };
        vr.Term.ShouldBe(5);
        vr.CandidateId.ShouldBe("node-1");
    }

    // Go: voteResponse struct in server/raft.go
    [Fact]
    public void VoteResponse_granted_and_denied()
    {
        var granted = new VoteResponse { Granted = true };
        granted.Granted.ShouldBeTrue();

        var denied = new VoteResponse { Granted = false };
        denied.Granted.ShouldBeFalse();
    }

    // Go: appendEntryResponse struct in server/raft.go
    [Fact]
    public void AppendResult_success_and_failure()
    {
        var success = new AppendResult { FollowerId = "f1", Success = true };
        success.FollowerId.ShouldBe("f1");
        success.Success.ShouldBeTrue();

        var failure = new AppendResult { FollowerId = "f2", Success = false };
        failure.Success.ShouldBeFalse();
    }

    // Go: raft term/vote state in server/raft.go
    [Fact]
    public void RaftTermState_initial_values()
    {
        var ts = new RaftTermState();
        ts.CurrentTerm.ShouldBe(0);
        ts.VotedFor.ShouldBeNull();
    }

    // Go: raft term/vote state in server/raft.go
    [Fact]
    public void RaftTermState_term_increment_and_vote()
    {
        var ts = new RaftTermState();
        ts.CurrentTerm = 3;
        ts.VotedFor = "candidate-x";
        ts.CurrentTerm.ShouldBe(3);
        ts.VotedFor.ShouldBe("candidate-x");
    }

    // Go: snapshot struct in server/raft.go
    [Fact]
    public void RaftSnapshot_default_values()
    {
        var snap = new RaftSnapshot();
        snap.LastIncludedIndex.ShouldBe(0);
        snap.LastIncludedTerm.ShouldBe(0);
        snap.Data.ShouldBeEmpty();
    }

    // Go: snapshot struct in server/raft.go
    [Fact]
    public void RaftSnapshot_init_properties()
    {
        var data = new byte[] { 1, 2, 3, 4 };
        var snap = new RaftSnapshot
        {
            LastIncludedIndex = 100,
            LastIncludedTerm = 5,
            Data = data,
        };
        snap.LastIncludedIndex.ShouldBe(100);
        snap.LastIncludedTerm.ShouldBe(5);
        snap.Data.ShouldBe(data);
    }
}
