// Tests for user NKey revocation on Account.
// Go reference: accounts_test.go TestJWTUserRevocation, checkUserRevoked (~line 3202),
//               isRevoked with jwt.All global key (~line 2929).

using NATS.Server.Auth;

namespace NATS.Server.Auth.Tests.Auth;

public class NKeyRevocationTests
{
    // ── 1 ──────────────────────────────────────────────────────────────────────
    [Fact]
    public void RevokeUser_AddsToRevokedList()
    {
        var account = new Account("A");

        account.RevokeUser("UNKEY1", 100L);

        account.RevokedUserCount.ShouldBe(1);
    }

    // ── 2 ──────────────────────────────────────────────────────────────────────
    [Fact]
    public void IsUserRevoked_Revoked_ReturnsTrue()
    {
        // A JWT issued at t=50 revoked when the revocation timestamp is 100
        // means issuedAt (50) <= revokedAt (100) → revoked.
        // Go reference: accounts.go isRevoked — t < issuedAt ⇒ NOT revoked (inverted).
        var account = new Account("A");
        account.RevokeUser("UNKEY1", 100L);

        account.IsUserRevoked("UNKEY1", 50L).ShouldBeTrue();
    }

    // ── 3 ──────────────────────────────────────────────────────────────────────
    [Fact]
    public void IsUserRevoked_NotRevoked_ReturnsFalse()
    {
        // A JWT issued at t=200 with revocation timestamp 100 means
        // issuedAt (200) > revokedAt (100) → NOT revoked.
        var account = new Account("A");
        account.RevokeUser("UNKEY1", 100L);

        account.IsUserRevoked("UNKEY1", 200L).ShouldBeFalse();
    }

    // ── 4 ──────────────────────────────────────────────────────────────────────
    [Fact]
    public void RevokedUserCount_MatchesRevocations()
    {
        var account = new Account("A");

        account.RevokedUserCount.ShouldBe(0);

        account.RevokeUser("UNKEY1", 1L);
        account.RevokedUserCount.ShouldBe(1);

        account.RevokeUser("UNKEY2", 2L);
        account.RevokedUserCount.ShouldBe(2);

        // Revoking the same key again does not increase count.
        account.RevokeUser("UNKEY1", 99L);
        account.RevokedUserCount.ShouldBe(2);
    }

    // ── 5 ──────────────────────────────────────────────────────────────────────
    [Fact]
    public void GetRevokedUsers_ReturnsAllKeys()
    {
        var account = new Account("A");
        account.RevokeUser("UNKEY1", 1L);
        account.RevokeUser("UNKEY2", 2L);
        account.RevokeUser("UNKEY3", 3L);

        var keys = account.GetRevokedUsers();

        keys.Count.ShouldBe(3);
        keys.ShouldContain("UNKEY1");
        keys.ShouldContain("UNKEY2");
        keys.ShouldContain("UNKEY3");
    }

    // ── 6 ──────────────────────────────────────────────────────────────────────
    [Fact]
    public void UnrevokeUser_RemovesRevocation()
    {
        var account = new Account("A");
        account.RevokeUser("UNKEY1", 100L);
        account.RevokedUserCount.ShouldBe(1);

        var removed = account.UnrevokeUser("UNKEY1");

        removed.ShouldBeTrue();
        account.RevokedUserCount.ShouldBe(0);
        account.IsUserRevoked("UNKEY1", 50L).ShouldBeFalse();
    }

    // ── 7 ──────────────────────────────────────────────────────────────────────
    [Fact]
    public void UnrevokeUser_NonExistent_ReturnsFalse()
    {
        var account = new Account("A");

        var removed = account.UnrevokeUser("DOES_NOT_EXIST");

        removed.ShouldBeFalse();
        account.RevokedUserCount.ShouldBe(0);
    }

    // ── 8 ──────────────────────────────────────────────────────────────────────
    [Fact]
    public void ClearAllRevocations_EmptiesList()
    {
        var account = new Account("A");
        account.RevokeUser("UNKEY1", 1L);
        account.RevokeUser("UNKEY2", 2L);
        account.RevokeAllUsers(999L);
        account.RevokedUserCount.ShouldBe(3);

        account.ClearAllRevocations();

        account.RevokedUserCount.ShouldBe(0);
        account.GetRevokedUsers().ShouldBeEmpty();
        account.IsGlobalRevocation().ShouldBeFalse();
    }

    // ── 9 ──────────────────────────────────────────────────────────────────────
    [Fact]
    public void RevokeAllUsers_SetsGlobalRevocation()
    {
        // Go reference: accounts.go — Revocations[jwt.All] used in isRevoked (~line 2934).
        // The "*" key causes any user whose issuedAt <= timestamp to be revoked.
        var account = new Account("A");

        account.RevokeAllUsers(500L);

        account.IsGlobalRevocation().ShouldBeTrue();
        // User issued at 500 is revoked (≤ 500).
        account.IsUserRevoked("ANY_USER", 500L).ShouldBeTrue();
        // User issued at 499 is also revoked.
        account.IsUserRevoked("ANY_USER", 499L).ShouldBeTrue();
        // User issued at 501 is NOT revoked (> 500).
        account.IsUserRevoked("ANY_USER", 501L).ShouldBeFalse();
    }

    // ── 10 ─────────────────────────────────────────────────────────────────────
    [Fact]
    public void GetRevocationInfo_ReturnsComplete()
    {
        var account = new Account("A");
        account.RevokeUser("UNKEY1", 10L);
        account.RevokeUser("UNKEY2", 20L);
        account.RevokeAllUsers(999L);

        var info = account.GetRevocationInfo();

        // Two per-user keys + one global "*" key = 3 total.
        info.RevokedCount.ShouldBe(3);
        info.HasGlobalRevocation.ShouldBeTrue();
        info.RevokedNKeys.Count.ShouldBe(3);
        info.RevokedNKeys.ShouldContain("UNKEY1");
        info.RevokedNKeys.ShouldContain("UNKEY2");
        info.RevokedNKeys.ShouldContain("*");
    }
}
