package ratelimit_test

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"

	"github.com/veylant/ia-gateway/internal/ratelimit"
)

func newTestLimiter(tenantRPM, tenantBurst, userRPM, userBurst int) *ratelimit.Limiter {
	cfg := ratelimit.RateLimitConfig{
		RequestsPerMin: tenantRPM,
		BurstSize:      tenantBurst,
		UserRPM:        userRPM,
		UserBurst:      userBurst,
		IsEnabled:      true,
	}
	return ratelimit.New(cfg, zap.NewNop())
}

func TestAllow_WithinLimits(t *testing.T) {
	l := newTestLimiter(600, 5, 300, 5)
	// Should allow up to burst size immediately.
	for i := 0; i < 5; i++ {
		assert.True(t, l.Allow("tenant1", "user1"), "request %d should be allowed", i+1)
	}
}

func TestAllow_TenantLimitExceeded(t *testing.T) {
	// Very low tenant burst (1) — second request should fail.
	l := newTestLimiter(600, 1, 600, 100)
	assert.True(t, l.Allow("t1", "u1"))
	assert.False(t, l.Allow("t1", "u1"), "second request should be blocked by tenant bucket")
}

func TestAllow_UserLimitExceeded(t *testing.T) {
	// High tenant burst, low user burst (1) — second request from same user should fail.
	l := newTestLimiter(600, 100, 600, 1)
	assert.True(t, l.Allow("t1", "u1"))
	assert.False(t, l.Allow("t1", "u1"), "second request from same user should be blocked")
}

func TestAllow_DifferentUsers_IndependentBuckets(t *testing.T) {
	l := newTestLimiter(600, 100, 600, 1)
	// u1 exhausts its bucket, u2 should still be allowed.
	l.Allow("t1", "u1") // consumes u1's burst
	assert.False(t, l.Allow("t1", "u1"), "u1 should be blocked")
	assert.True(t, l.Allow("t1", "u2"), "u2 should be allowed (independent bucket)")
}

func TestAllow_Disabled_AlwaysAllowed(t *testing.T) {
	cfg := ratelimit.RateLimitConfig{
		TenantID:       "t1",
		RequestsPerMin: 1, // extremely low
		BurstSize:      1,
		UserRPM:        1,
		UserBurst:      1,
		IsEnabled:      false, // disabled → bypass
	}
	l := ratelimit.New(cfg, zap.NewNop())
	for i := 0; i < 100; i++ {
		require.True(t, l.Allow("t1", "u1"), "disabled limiter must always allow")
	}
}

func TestSetConfig_OverridesDefault(t *testing.T) {
	// Default: burst=1 (very restrictive)
	l := newTestLimiter(600, 1, 600, 1)

	// Override tenant t1 with a generous limit.
	l.SetConfig(ratelimit.RateLimitConfig{
		TenantID:       "t1",
		RequestsPerMin: 6000,
		BurstSize:      100,
		UserRPM:        6000,
		UserBurst:      100,
		IsEnabled:      true,
	})

	// Should allow many requests now.
	for i := 0; i < 50; i++ {
		assert.True(t, l.Allow("t1", "u1"), "overridden config should allow request %d", i+1)
	}
}

func TestDeleteConfig_RevertsToDefault(t *testing.T) {
	l := newTestLimiter(600, 100, 600, 100)
	l.SetConfig(ratelimit.RateLimitConfig{
		TenantID:       "t1",
		RequestsPerMin: 6000,
		BurstSize:      1000,
		UserRPM:        6000,
		UserBurst:      1000,
		IsEnabled:      true,
	})
	l.DeleteConfig("t1")

	// Default burst is 100, so many requests should still be allowed immediately.
	assert.True(t, l.Allow("t1", "u1"))
}

func TestListConfigs(t *testing.T) {
	l := newTestLimiter(600, 100, 600, 100)
	assert.Empty(t, l.ListConfigs())

	l.SetConfig(ratelimit.RateLimitConfig{TenantID: "t1", RequestsPerMin: 100, BurstSize: 10, UserRPM: 50, UserBurst: 5, IsEnabled: true})
	l.SetConfig(ratelimit.RateLimitConfig{TenantID: "t2", RequestsPerMin: 200, BurstSize: 20, UserRPM: 100, UserBurst: 10, IsEnabled: true})

	cfgs := l.ListConfigs()
	assert.Len(t, cfgs, 2)
}

func TestConcurrentAllow_RaceFree(t *testing.T) {
	l := newTestLimiter(6000, 1000, 6000, 1000)
	done := make(chan struct{})
	for i := 0; i < 10; i++ {
		go func(id int) {
			for j := 0; j < 100; j++ {
				l.Allow("tenant", "user")
				time.Sleep(time.Microsecond)
			}
			done <- struct{}{}
		}(i)
	}
	for i := 0; i < 10; i++ {
		<-done
	}
}
