// +build integration

package redis

import (
	"context"
	"fmt"
	"math/rand"
	"strconv"
	"testing"
	"time"

	"code.justin.tv/creator-collab/log"

	"code.justin.tv/live/autohost/internal/hosting/config"
	"code.justin.tv/live/autohost/internal/metrics"
	"github.com/stretchr/testify/require"
)

// TestRateLimiting_RateLimitWithMetrics verifies that the common method is working.
func TestRateLimiting_RateLimitWithMetrics(t *testing.T) {
	ctx := context.Background()
	rateLimiter := newTestRateLimiter(t)

	sourceID := newRandomUserID()
	key := fmt.Sprintf("ratelimittest_%s", sourceID)
	windowDuration := time.Second
	windowSize := 2

	// Verify that we allow up to the window size.
	allow := rateLimiter.rateLimitWithMetrics(ctx, key, windowDuration, windowSize, "Host")
	require.True(t, allow)
	allow = rateLimiter.rateLimitWithMetrics(ctx, key, windowDuration, windowSize, "Host")
	require.True(t, allow)

	// Verify that additional requests are throttled.
	allow = rateLimiter.rateLimitWithMetrics(ctx, key, windowDuration, windowSize, "Host")
	require.False(t, allow)

	// Verify that operations are allowed after the window passes.
	time.Sleep(2 * time.Second)
	allow = rateLimiter.rateLimitWithMetrics(ctx, key, windowDuration, windowSize, "Host")
	require.True(t, allow)
}

func TestRateLimiting_HostAndUnhostUseDifferentKeys(t *testing.T) {
	ctx := context.Background()
	rateLimiter := newTestRateLimiter(t)

	sourceID := newRandomUserID()
	allow := rateLimiter.RateLimitHostBySourceID(ctx, sourceID)
	require.True(t, allow)

	allow = rateLimiter.RateLimitUnhostBySourceID(ctx, sourceID)
	require.True(t, allow)
}

func TestRateLimiting_HostKeysAreBySourceID(t *testing.T) {
	ctx := context.Background()
	rateLimiter := newTestRateLimiter(t)

	allow := rateLimiter.RateLimitHostBySourceID(ctx, newRandomUserID())
	require.True(t, allow)

	allow = rateLimiter.RateLimitHostBySourceID(ctx, newRandomUserID())
	require.True(t, allow)
}

func TestRateLimiting_UnostKeysAreBySourceID(t *testing.T) {
	ctx := context.Background()
	rateLimiter := newTestRateLimiter(t)

	allow := rateLimiter.RateLimitUnhostBySourceID(ctx, newRandomUserID())
	require.True(t, allow)

	allow = rateLimiter.RateLimitUnhostBySourceID(ctx, newRandomUserID())
	require.True(t, allow)
}

func newTestRateLimiter(t *testing.T) *RateLimiterImpl {
	sampleReporter := metrics.NewSampleReporter(&metrics.SampleReporterConfig{
		Environment:    "development",
		Region:         "us-west-2",
		ServiceName:    "Autohost Server",
		OverrideSender: &metrics.StubSampleObserver{},
	})

	conf, err := config.GetConfig()
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotZero(t, conf.Redis.Address)

	return NewRateLimiter(&RateLimiterParams{
		RedisAddress:         conf.Redis.Address,
		IsCluster:            conf.Redis.Cluster,
		SampleReporter:       sampleReporter,
		Logger:               log.NewDevelopmentLogger(),
		MaxHostOperations:    1,
		HostWindowDuration:   time.Second,
		MaxUnhostOperations:  1,
		UnhostWindowDuration: time.Second,
	})
}

func newRandomUserID() string {
	id := ""
	for i := 0; i < 8; i++ {
		digit := rand.Intn(10)
		id += strconv.Itoa(digit)
	}
	return id
}
