using NATS.Server.Auth;
using Shouldly;

namespace NATS.Server.Auth.Tests.Auth;

// Go reference: server/accounts.go — account expiry / SetExpirationTimer

public sealed class AccountExpirationTests
{
    // 1. ExpiresAt_Default_IsNull
    // Go reference: accounts.go — account.expiry zero-value
    [Fact]
    public void ExpiresAt_Default_IsNull()
    {
        var account = new Account("test-account");
        account.ExpiresAt.ShouldBeNull();
    }

    // 2. SetExpiration_SetsExpiresAt
    // Go reference: accounts.go — SetExpirationTimer stores expiry value
    [Fact]
    public void SetExpiration_SetsExpiresAt()
    {
        var account = new Account("test-account");
        var expiresAt = new DateTime(2030, 6, 15, 12, 0, 0, DateTimeKind.Utc);

        account.SetExpiration(expiresAt);

        account.ExpiresAt.ShouldBe(expiresAt);
    }

    // 3. IsExpired_FutureDate_ReturnsFalse
    // Go reference: accounts.go — isExpired() returns false when expiry is in future
    [Fact]
    public void IsExpired_FutureDate_ReturnsFalse()
    {
        var account = new Account("test-account");
        account.SetExpiration(DateTime.UtcNow.AddHours(1));

        account.IsExpired.ShouldBeFalse();
    }

    // 4. IsExpired_PastDate_ReturnsTrue
    // Go reference: accounts.go — isExpired() returns true when past expiry
    [Fact]
    public void IsExpired_PastDate_ReturnsTrue()
    {
        var account = new Account("test-account");
        account.SetExpiration(DateTime.UtcNow.AddHours(-1));

        account.IsExpired.ShouldBeTrue();
    }

    // 5. ClearExpiration_RemovesExpiry
    // Go reference: accounts.go — clearing expiry resets the field to zero
    [Fact]
    public void ClearExpiration_RemovesExpiry()
    {
        var account = new Account("test-account");
        account.SetExpiration(DateTime.UtcNow.AddHours(1));
        account.ExpiresAt.ShouldNotBeNull();

        account.ClearExpiration();

        account.ExpiresAt.ShouldBeNull();
    }

    // 6. SetExpirationFromTtl_CalculatesCorrectly
    // Go reference: accounts.go — SetExpirationTimer(ttl) sets expiry = now + ttl
    [Fact]
    public void SetExpirationFromTtl_CalculatesCorrectly()
    {
        var account = new Account("test-account");
        var before = DateTime.UtcNow;
        account.SetExpirationFromTtl(TimeSpan.FromHours(1));
        var after = DateTime.UtcNow;

        account.ExpiresAt.ShouldNotBeNull();
        account.ExpiresAt!.Value.ShouldBeGreaterThanOrEqualTo(before.AddHours(1));
        account.ExpiresAt.Value.ShouldBeLessThanOrEqualTo(after.AddHours(1));
    }

    // 7. TimeToExpiry_NoExpiry_ReturnsNull
    // Go reference: accounts.go — no expiry set returns nil duration
    [Fact]
    public void TimeToExpiry_NoExpiry_ReturnsNull()
    {
        var account = new Account("test-account");

        account.TimeToExpiry.ShouldBeNull();
    }

    // 8. TimeToExpiry_Expired_ReturnsZero
    // Go reference: accounts.go — already-expired account has zero remaining time
    [Fact]
    public void TimeToExpiry_Expired_ReturnsZero()
    {
        var account = new Account("test-account");
        account.SetExpiration(DateTime.UtcNow.AddHours(-1));

        account.TimeToExpiry.ShouldBe(TimeSpan.Zero);
    }

    // 9. TimeToExpiry_Future_ReturnsPositive
    // Go reference: accounts.go — unexpired account returns positive remaining duration
    [Fact]
    public void TimeToExpiry_Future_ReturnsPositive()
    {
        var account = new Account("test-account");
        account.SetExpiration(DateTime.UtcNow.AddHours(1));

        var tte = account.TimeToExpiry;
        tte.ShouldNotBeNull();
        tte!.Value.ShouldBeGreaterThan(TimeSpan.Zero);
    }

    // 10. GetExpirationInfo_ReturnsCompleteInfo
    // Go reference: accounts.go — expiry fields exposed for monitoring / JWT renewal
    [Fact]
    public void GetExpirationInfo_ReturnsCompleteInfo()
    {
        var account = new Account("info-account");
        var expiresAt = DateTime.UtcNow.AddHours(2);
        account.SetExpiration(expiresAt);

        var info = account.GetExpirationInfo();

        info.AccountName.ShouldBe("info-account");
        info.HasExpiration.ShouldBeTrue();
        info.ExpiresAt.ShouldBe(expiresAt);
        info.IsExpired.ShouldBeFalse();
        info.TimeToExpiry.ShouldNotBeNull();
        info.TimeToExpiry!.Value.ShouldBeGreaterThan(TimeSpan.Zero);
    }
}
