using System;
using System.Net;
using Microsoft.AspNetCore.Http;
using StellaOps.Authority;
using StellaOps.Configuration;
using Xunit;

namespace StellaOps.Authority.Tests.RateLimiting;

public class AuthorityRateLimiterTests
{
    [Fact]
    public async Task TokenLimiter_Throttles_WhenLimitExceeded()
    {
        var options = CreateOptions();
        options.Security.RateLimiting.Token.PermitLimit = 1;
        options.Security.RateLimiting.Token.Window = TimeSpan.FromMinutes(1);

        using var limiter = AuthorityRateLimiter.CreateGlobalLimiter(options);

        var context = new DefaultHttpContext();
        context.Request.Path = "/token";
        context.Connection.RemoteIpAddress = IPAddress.Parse("203.0.113.10");

        var first = await limiter.AcquireAsync(context);
        Assert.True(first.IsAcquired);

        var second = await limiter.AcquireAsync(context);
        Assert.False(second.IsAcquired);
    }

    [Fact]
    public async Task AuthorizeLimiter_Allows_WhenDisabled()
    {
        var options = CreateOptions();
        options.Security.RateLimiting.Authorize.Enabled = false;

        using var limiter = AuthorityRateLimiter.CreateGlobalLimiter(options);

        var context = new DefaultHttpContext();
        context.Request.Path = "/authorize";
        context.Connection.RemoteIpAddress = IPAddress.Parse("203.0.113.20");

        var lease = await limiter.AcquireAsync(context);
        Assert.True(lease.IsAcquired);
    }

    [Fact]
    public async Task InternalLimiter_Throttles_WhenEnabled()
    {
        var options = CreateOptions();
        options.Security.RateLimiting.Internal.Enabled = true;
        options.Security.RateLimiting.Internal.PermitLimit = 1;
        options.Security.RateLimiting.Internal.Window = TimeSpan.FromMinutes(1);

        using var limiter = AuthorityRateLimiter.CreateGlobalLimiter(options);

        var context = new DefaultHttpContext();
        context.Request.Path = "/internal/health";
        context.Connection.RemoteIpAddress = IPAddress.Parse("203.0.113.30");

        var first = await limiter.AcquireAsync(context);
        Assert.True(first.IsAcquired);

        var second = await limiter.AcquireAsync(context);
        Assert.False(second.IsAcquired);
    }

    private static StellaOpsAuthorityOptions CreateOptions()
    {
        var options = new StellaOpsAuthorityOptions
        {
            Issuer = new Uri("https://authority.stella-ops.test"),
            SchemaVersion = 1
        };

        options.Storage.ConnectionString = "mongodb://localhost/authority";
        return options;
    }
}
