using NATS.Server.Raft;

namespace NATS.Server.Tests.Raft;

/// <summary>
/// Verifies that RaftSubjects produces the exact $NRG.* subject strings
/// defined in Go's raft.go constants.
///
/// Go reference: golang/nats-server/server/raft.go:2161-2169
///   raftAllSubj        = "$NRG.>"
///   raftVoteSubj       = "$NRG.V.%s"
///   raftAppendSubj     = "$NRG.AE.%s"
///   raftPropSubj       = "$NRG.P.%s"
///   raftRemovePeerSubj = "$NRG.RP.%s"
///   raftReply          = "$NRG.R.%s"
///   raftCatchupReply   = "$NRG.CR.%s"
/// </summary>
public class RaftSubjectsTests
{
    // Go: server/raft.go:2162 — raftAllSubj = "$NRG.>"
    [Fact]
    public void All_constant_matches_go_raftAllSubj()
    {
        RaftSubjects.All.ShouldBe("$NRG.>");
    }

    // Go: server/raft.go:2163 — raftVoteSubj = "$NRG.V.%s"
    [Fact]
    public void Vote_formats_subject_with_group()
    {
        RaftSubjects.Vote("mygroup").ShouldBe("$NRG.V.mygroup");
    }

    // Go: server/raft.go:2163 — fmt.Sprintf(raftVoteSubj, n.group)
    [Fact]
    public void Vote_uses_group_verbatim()
    {
        RaftSubjects.Vote("meta").ShouldBe("$NRG.V.meta");
        RaftSubjects.Vote("stream-A").ShouldBe("$NRG.V.stream-A");
        RaftSubjects.Vote("_raft_").ShouldBe("$NRG.V._raft_");
    }

    // Go: server/raft.go:2164 — raftAppendSubj = "$NRG.AE.%s"
    [Fact]
    public void AppendEntry_formats_subject_with_group()
    {
        RaftSubjects.AppendEntry("mygroup").ShouldBe("$NRG.AE.mygroup");
    }

    // Go: server/raft.go:2164 — fmt.Sprintf(raftAppendSubj, n.group)
    [Fact]
    public void AppendEntry_uses_group_verbatim()
    {
        RaftSubjects.AppendEntry("meta").ShouldBe("$NRG.AE.meta");
        RaftSubjects.AppendEntry("stream-B").ShouldBe("$NRG.AE.stream-B");
    }

    // Go: server/raft.go:2165 — raftPropSubj = "$NRG.P.%s"
    [Fact]
    public void Proposal_formats_subject_with_group()
    {
        RaftSubjects.Proposal("mygroup").ShouldBe("$NRG.P.mygroup");
    }

    // Go: server/raft.go:2165 — fmt.Sprintf(raftPropSubj, n.group)
    [Fact]
    public void Proposal_uses_group_verbatim()
    {
        RaftSubjects.Proposal("meta").ShouldBe("$NRG.P.meta");
        RaftSubjects.Proposal("consumer-1").ShouldBe("$NRG.P.consumer-1");
    }

    // Go: server/raft.go:2166 — raftRemovePeerSubj = "$NRG.RP.%s"
    [Fact]
    public void RemovePeer_formats_subject_with_group()
    {
        RaftSubjects.RemovePeer("mygroup").ShouldBe("$NRG.RP.mygroup");
    }

    // Go: server/raft.go:2166 — fmt.Sprintf(raftRemovePeerSubj, n.group)
    [Fact]
    public void RemovePeer_uses_group_verbatim()
    {
        RaftSubjects.RemovePeer("meta").ShouldBe("$NRG.RP.meta");
        RaftSubjects.RemovePeer("stream-C").ShouldBe("$NRG.RP.stream-C");
    }

    // Go: server/raft.go:2167 — raftReply = "$NRG.R.%s"
    [Fact]
    public void Reply_formats_subject_with_id()
    {
        RaftSubjects.Reply("abc123").ShouldBe("$NRG.R.abc123");
    }

    // Go: server/raft.go:2167 — fmt.Sprintf(raftReply, b[:])
    [Fact]
    public void Reply_uses_id_verbatim()
    {
        RaftSubjects.Reply("ABCDEFGH").ShouldBe("$NRG.R.ABCDEFGH");
        RaftSubjects.Reply("00000001").ShouldBe("$NRG.R.00000001");
    }

    // Go: server/raft.go:2168 — raftCatchupReply = "$NRG.CR.%s"
    [Fact]
    public void CatchupReply_formats_subject_with_id()
    {
        RaftSubjects.CatchupReply("xyz789").ShouldBe("$NRG.CR.xyz789");
    }

    // Go: server/raft.go:2168 — fmt.Sprintf(raftCatchupReply, b[:])
    [Fact]
    public void CatchupReply_uses_id_verbatim()
    {
        RaftSubjects.CatchupReply("ABCDEFGH").ShouldBe("$NRG.CR.ABCDEFGH");
        RaftSubjects.CatchupReply("00000001").ShouldBe("$NRG.CR.00000001");
    }

    // Verify that subjects for different groups are distinct (no collisions)
    [Fact]
    public void Subjects_for_different_groups_are_distinct()
    {
        RaftSubjects.Vote("group1").ShouldNotBe(RaftSubjects.Vote("group2"));
        RaftSubjects.AppendEntry("group1").ShouldNotBe(RaftSubjects.AppendEntry("group2"));
        RaftSubjects.Proposal("group1").ShouldNotBe(RaftSubjects.Proposal("group2"));
        RaftSubjects.RemovePeer("group1").ShouldNotBe(RaftSubjects.RemovePeer("group2"));
    }

    // Verify that different verb subjects for the same group are distinct
    [Fact]
    public void Different_verbs_for_same_group_are_distinct()
    {
        var group = "meta";
        var subjects = new[]
        {
            RaftSubjects.Vote(group),
            RaftSubjects.AppendEntry(group),
            RaftSubjects.Proposal(group),
            RaftSubjects.RemovePeer(group),
        };
        subjects.Distinct().Count().ShouldBe(subjects.Length);
    }

    // All group subjects must be sub-subjects of the wildcard $NRG.>
    [Fact]
    public void All_group_subjects_are_under_NRG_namespace()
    {
        var group = "g";
        RaftSubjects.Vote(group).ShouldStartWith("$NRG.");
        RaftSubjects.AppendEntry(group).ShouldStartWith("$NRG.");
        RaftSubjects.Proposal(group).ShouldStartWith("$NRG.");
        RaftSubjects.RemovePeer(group).ShouldStartWith("$NRG.");
        RaftSubjects.Reply("id").ShouldStartWith("$NRG.");
        RaftSubjects.CatchupReply("id").ShouldStartWith("$NRG.");
    }
}
