// Tests for Account.AddReverseRespMapEntry / CheckForReverseEntries and related helpers.
// Go reference: server/accounts.go — addRespMapEntry (~line 2800), checkForReverseEntries (~line 2810).

using NATS.Server.Auth;

namespace NATS.Server.Auth.Tests.Auth;

public class ReverseResponseMapTests
{
    // ---------------------------------------------------------------------------
    // AddReverseRespMapEntry / CheckForReverseEntries
    // ---------------------------------------------------------------------------

    [Fact]
    public void AddReverseRespMapEntry_StoresEntry()
    {
        // Go ref: accounts.go addRespMapEntry — stores respMapEntry keyed by rewritten reply
        var account = new Account("A");
        account.AddReverseRespMapEntry("_R_.abc", "B", "reply.1");

        account.ReverseResponseMapCount.ShouldBe(1);
    }

    [Fact]
    public void CheckForReverseEntries_Found_ReturnsEntry()
    {
        // Go ref: accounts.go checkForReverseEntries — returns entry when key exists
        var account = new Account("A");
        account.AddReverseRespMapEntry("_R_.xyz", "origin-account", "original.reply.subject");

        var entry = account.CheckForReverseEntries("_R_.xyz");

        entry.ShouldNotBeNull();
        entry.ReplySubject.ShouldBe("_R_.xyz");
        entry.OriginAccount.ShouldBe("origin-account");
        entry.OriginalReply.ShouldBe("original.reply.subject");
    }

    [Fact]
    public void CheckForReverseEntries_NotFound_ReturnsNull()
    {
        // Go ref: accounts.go checkForReverseEntries — returns nil when key absent
        var account = new Account("A");

        var entry = account.CheckForReverseEntries("_R_.nonexistent");

        entry.ShouldBeNull();
    }

    // ---------------------------------------------------------------------------
    // RemoveReverseRespMapEntry
    // ---------------------------------------------------------------------------

    [Fact]
    public void RemoveReverseRespMapEntry_Found_ReturnsTrue()
    {
        // Go ref: accounts.go — reverse map cleanup after response is routed
        var account = new Account("A");
        account.AddReverseRespMapEntry("_R_.del", "B", "orig.reply");

        var removed = account.RemoveReverseRespMapEntry("_R_.del");

        removed.ShouldBeTrue();
        account.ReverseResponseMapCount.ShouldBe(0);
    }

    [Fact]
    public void RemoveReverseRespMapEntry_NotFound_ReturnsFalse()
    {
        // Go ref: accounts.go — removing an absent entry is a no-op
        var account = new Account("A");

        var removed = account.RemoveReverseRespMapEntry("_R_.missing");

        removed.ShouldBeFalse();
    }

    // ---------------------------------------------------------------------------
    // ReverseResponseMapCount
    // ---------------------------------------------------------------------------

    [Fact]
    public void ReverseResponseMapCount_MatchesEntries()
    {
        // Go ref: accounts.go — map length reflects outstanding response mappings
        var account = new Account("A");

        account.ReverseResponseMapCount.ShouldBe(0);

        account.AddReverseRespMapEntry("_R_.1", "B", "r1");
        account.AddReverseRespMapEntry("_R_.2", "C", "r2");
        account.AddReverseRespMapEntry("_R_.3", "D", "r3");

        account.ReverseResponseMapCount.ShouldBe(3);

        account.RemoveReverseRespMapEntry("_R_.2");

        account.ReverseResponseMapCount.ShouldBe(2);
    }

    // ---------------------------------------------------------------------------
    // ClearReverseResponseMap
    // ---------------------------------------------------------------------------

    [Fact]
    public void ClearReverseResponseMap_EmptiesAll()
    {
        // Go ref: accounts.go — clearing map after bulk expiry / account teardown
        var account = new Account("A");
        account.AddReverseRespMapEntry("_R_.a", "B", "ra");
        account.AddReverseRespMapEntry("_R_.b", "C", "rb");

        account.ClearReverseResponseMap();

        account.ReverseResponseMapCount.ShouldBe(0);
        account.CheckForReverseEntries("_R_.a").ShouldBeNull();
        account.CheckForReverseEntries("_R_.b").ShouldBeNull();
    }

    // ---------------------------------------------------------------------------
    // GetReverseResponseMapKeys
    // ---------------------------------------------------------------------------

    [Fact]
    public void GetReverseResponseMapKeys_ReturnsAllKeys()
    {
        // Go ref: accounts.go — iterating active respMapEntry keys for diagnostics
        var account = new Account("A");
        account.AddReverseRespMapEntry("_R_.k1", "B", "r1");
        account.AddReverseRespMapEntry("_R_.k2", "C", "r2");

        var keys = account.GetReverseResponseMapKeys();

        keys.Count.ShouldBe(2);
        keys.ShouldContain("_R_.k1");
        keys.ShouldContain("_R_.k2");
    }

    // ---------------------------------------------------------------------------
    // Overwrite and CreatedAt preservation
    // ---------------------------------------------------------------------------

    [Fact]
    public void AddReverseRespMapEntry_OverwritesPrevious()
    {
        // Go ref: accounts.go addRespMapEntry — map assignment overwrites existing key
        var account = new Account("A");
        account.AddReverseRespMapEntry("_R_.ov", "B", "first.reply");
        account.AddReverseRespMapEntry("_R_.ov", "C", "second.reply");

        var entry = account.CheckForReverseEntries("_R_.ov");

        entry.ShouldNotBeNull();
        entry.OriginAccount.ShouldBe("C");
        entry.OriginalReply.ShouldBe("second.reply");
        account.ReverseResponseMapCount.ShouldBe(1);
    }

    [Fact]
    public void ReverseRespMapEntry_PreservesCreatedAt()
    {
        // Go ref: accounts.go respMapEntry — timestamp recorded at map insertion time
        var before = DateTime.UtcNow;
        var account = new Account("A");
        account.AddReverseRespMapEntry("_R_.ts", "B", "ts.reply");
        var after = DateTime.UtcNow;

        var entry = account.CheckForReverseEntries("_R_.ts");

        entry.ShouldNotBeNull();
        entry.CreatedAt.ShouldBeGreaterThanOrEqualTo(before);
        entry.CreatedAt.ShouldBeLessThanOrEqualTo(after);
    }
}
