package recon

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	"golang.org/x/time/rate"
)

func TestLimiterPerSourceIsolation(t *testing.T) {
	r := NewLimiterRegistry()
	a := r.For("a", 10, 1)
	b := r.For("b", 10, 1)
	require.NotNil(t, a)
	require.NotNil(t, b)
	require.NotSame(t, a, b, "different source names must yield distinct limiters")
}

func TestLimiterIdempotent(t *testing.T) {
	r := NewLimiterRegistry()
	a1 := r.For("a", 10, 1)
	a2 := r.For("a", 10, 1)
	require.Same(t, a1, a2, "repeat calls with same name must return same *rate.Limiter")
}

func TestWaitRespectsContext(t *testing.T) {
	r := NewLimiterRegistry()
	// Prime limiter with a very slow rate so Wait would block
	_ = r.For("slow", rate.Limit(0.001), 1)
	// Consume the single burst token so the next Wait actually blocks
	l := r.For("slow", rate.Limit(0.001), 1)
	require.True(t, l.Allow())

	ctx, cancel := context.WithCancel(context.Background())
	cancel() // pre-cancelled

	err := r.Wait(ctx, "slow", rate.Limit(0.001), 1, false)
	require.Error(t, err)
}

func TestJitterRange(t *testing.T) {
	r := NewLimiterRegistry()
	// High rate so only jitter meaningfully contributes
	start := time.Now()
	err := r.Wait(context.Background(), "fast", rate.Limit(1000), 100, true)
	elapsed := time.Since(start)
	require.NoError(t, err)
	require.GreaterOrEqual(t, elapsed, 90*time.Millisecond, "jitter should be >= 100ms (with slack)")
	require.LessOrEqual(t, elapsed, 1200*time.Millisecond, "jitter should be <= 1s (with slack)")
}

func TestJitterDisabled(t *testing.T) {
	r := NewLimiterRegistry()
	start := time.Now()
	err := r.Wait(context.Background(), "fast2", rate.Limit(1000), 100, false)
	elapsed := time.Since(start)
	require.NoError(t, err)
	require.Less(t, elapsed, 50*time.Millisecond, "no jitter when stealth=false")
}
