package source

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestControllerRateLimiter(t *testing.T) {
	t.Run("creates rate limiter with correct configuration", func(t *testing.T) {
		rateLimiter := ControllerRateLimiter()

		assert.NotNil(t, rateLimiter)
	})

	t.Run("rate limiter handles item delays", func(t *testing.T) {
		rateLimiter := ControllerRateLimiter()

		item := "test-item"

		// First attempt should have minimal delay
		delay1 := rateLimiter.When(item)
		assert.GreaterOrEqual(t, delay1, 40*time.Millisecond)

		// Second attempt should have exponential backoff
		delay2 := rateLimiter.When(item)
		assert.Greater(t, delay2, delay1)
	})

	t.Run("rate limiter tracks requeue count", func(t *testing.T) {
		rateLimiter := ControllerRateLimiter()

		item := "test-item"

		assert.Equal(t, 0, rateLimiter.NumRequeues(item))

		rateLimiter.When(item)
		assert.Equal(t, 1, rateLimiter.NumRequeues(item))

		rateLimiter.When(item)
		assert.Equal(t, 2, rateLimiter.NumRequeues(item))
	})

	t.Run("rate limiter forgets item", func(t *testing.T) {
		rateLimiter := ControllerRateLimiter()

		item := "test-item"

		rateLimiter.When(item)
		rateLimiter.When(item)
		assert.Equal(t, 2, rateLimiter.NumRequeues(item))

		rateLimiter.Forget(item)
		assert.Equal(t, 0, rateLimiter.NumRequeues(item))
	})

	t.Run("rate limiter handles multiple items independently", func(t *testing.T) {
		rateLimiter := ControllerRateLimiter()

		item1 := "item-1"
		item2 := "item-2"

		rateLimiter.When(item1)
		rateLimiter.When(item1)

		rateLimiter.When(item2)

		assert.Equal(t, 2, rateLimiter.NumRequeues(item1))
		assert.Equal(t, 1, rateLimiter.NumRequeues(item2))

		rateLimiter.Forget(item1)

		assert.Equal(t, 0, rateLimiter.NumRequeues(item1))
		assert.Equal(t, 1, rateLimiter.NumRequeues(item2))
	})

	t.Run("rate limiter respects max delay", func(t *testing.T) {
		rateLimiter := ControllerRateLimiter()

		item := "test-item"

		// Trigger many requeues to hit max delay
		var lastDelay time.Duration
		for i := 0; i < 20; i++ {
			delay := rateLimiter.When(item)
			if i > 0 {
				// Delay should increase or stay at max
				assert.GreaterOrEqual(t, delay, lastDelay)
			}
			lastDelay = delay
		}

		// Should not exceed 1000 seconds
		assert.LessOrEqual(t, lastDelay, 1000*time.Second)
	})
}
