package infra

import (
	"testing"
	"time"
)

func TestRateLimiterCheck(t *testing.T) {
	t.Run("primer request siempre allowed con burst-1 remaining", func(t *testing.T) {
		rl := RateLimiterNew(10, 20)
		result := RateLimiterCheck(rl, "client-1")
		if !result.Allowed {
			t.Error("primer request rechazado")
		}
		if result.Remaining != 19 {
			t.Errorf("Remaining=%d, want 19", result.Remaining)
		}
		if result.RetryAfter != 0 {
			t.Errorf("RetryAfter=%v, want 0", result.RetryAfter)
		}
	})

	t.Run("consumir todos los tokens bloquea siguiente request", func(t *testing.T) {
		rl := RateLimiterNew(1, 5)
		// Consumir los 5 tokens del burst
		for i := 0; i < 5; i++ {
			r := RateLimiterCheck(rl, "client-2")
			if !r.Allowed {
				t.Fatalf("request %d rechazado, esperaba allowed", i)
			}
		}
		// El sexto deberia ser rechazado
		r := RateLimiterCheck(rl, "client-2")
		if r.Allowed {
			t.Error("request post-burst permitido, esperaba rechazo")
		}
		if r.Remaining != 0 {
			t.Errorf("Remaining=%d, want 0", r.Remaining)
		}
	})

	t.Run("los tokens se recargan con el paso del tiempo", func(t *testing.T) {
		rl := RateLimiterNew(100, 2) // 100 tokens/seg = 1 token cada 10ms
		// Consumir burst
		RateLimiterCheck(rl, "client-3")
		RateLimiterCheck(rl, "client-3")
		// Tercer request rechazado
		r := RateLimiterCheck(rl, "client-3")
		if r.Allowed {
			t.Fatal("tercer request inmediato deberia estar rechazado")
		}
		// Esperar suficiente para recargar al menos 1 token
		time.Sleep(50 * time.Millisecond)
		r2 := RateLimiterCheck(rl, "client-3")
		if !r2.Allowed {
			t.Error("request tras recarga deberia estar permitido")
		}
	})

	t.Run("retryAfter es positivo cuando se rechaza", func(t *testing.T) {
		rl := RateLimiterNew(1, 1) // 1 token/seg, burst 1
		RateLimiterCheck(rl, "client-4")
		r := RateLimiterCheck(rl, "client-4")
		if r.Allowed {
			t.Fatal("segundo request inmediato deberia rechazarse")
		}
		if r.RetryAfter <= 0 {
			t.Errorf("RetryAfter=%v, want > 0", r.RetryAfter)
		}
		if r.RetryAfter > 1.5 {
			t.Errorf("RetryAfter=%v, esperado <= 1s aprox", r.RetryAfter)
		}
	})

	t.Run("keys distintas tienen buckets independientes", func(t *testing.T) {
		rl := RateLimiterNew(1, 2)
		// Consumir todo de A
		RateLimiterCheck(rl, "A")
		RateLimiterCheck(rl, "A")
		ra := RateLimiterCheck(rl, "A")
		if ra.Allowed {
			t.Fatal("A deberia estar agotado")
		}
		// B sigue intacto
		rb := RateLimiterCheck(rl, "B")
		if !rb.Allowed {
			t.Error("B deberia tener tokens propios")
		}
	})
}
