// +build integration

package redis

import (
	"context"
	"testing"

	telemetry "code.justin.tv/amzn/TwitchTelemetry"
	"code.justin.tv/chat/rediczar"
	"code.justin.tv/live/autohost/internal/hosting/config"
	"code.justin.tv/live/autohost/internal/metrics"
	goredis "github.com/go-redis/redis/v7"
	"github.com/stretchr/testify/require"
)

func TestSingleOperationMetrics(t *testing.T) {
	sampleObserver := &metrics.StubSampleObserver{}
	client := newTestRedisClient(t, sampleObserver)

	// Run a stand-alone Redis command.
	operationName := "TestOp"
	ctx := telemetry.ContextWithOperationName(context.Background(), operationName)
	_, err := client.Get(ctx, "missingkey")
	require.Equal(t, goredis.Nil, err)

	// Verify that we sent success and duration metrics with the correct dimensions.
	expectedDims := map[string]string{
		telemetry.DimensionOperation:  operationName,
		telemetry.DimensionDependency: "redisPrimary:get",
	}
	samples, err := sampleObserver.GetSamplesWithValue(telemetry.MetricDependencySuccess, expectedDims, 1)
	require.NoError(t, err, sampleObserver.String())
	require.Len(t, samples, 1)

	samples, err = sampleObserver.GetSamplesWithValue(telemetry.MetricDependencyServerError, expectedDims, 0)
	require.NoError(t, err, sampleObserver.String())
	require.Len(t, samples, 1)

	samples, err = sampleObserver.GetSamplesWithValue(telemetry.MetricDependencyClientError, expectedDims, 0)
	require.NoError(t, err, sampleObserver.String())
	require.Len(t, samples, 1)

	samples, err = sampleObserver.GetSamplesWithPositiveValue(telemetry.MetricDependencyDuration, expectedDims)
	require.NoError(t, err, sampleObserver.String())
	require.Len(t, samples, 1)
}

func TestPipelinedOperationsMetrics(t *testing.T) {
	sampleObserver := &metrics.StubSampleObserver{}
	client := newTestRedisClient(t, sampleObserver)

	// Run two Redis commands within a pipeline.
	operationName := "TestOp"
	ctx := telemetry.ContextWithOperationName(context.Background(), operationName)

	pipe := client.Pipeline(ctx)
	cmd1 := pipe.Exists("key1")
	cmd2 := pipe.Exists("key2")
	_, err := pipe.Exec()

	require.NoError(t, err)
	require.NotNil(t, cmd1)
	require.NoError(t, cmd1.Err())
	require.NotNil(t, cmd2)
	require.NoError(t, cmd1.Err())

	// Verify that we sent success and duration metrics with the correct dimensions.
	// There should be two of each type of metric because there are two commands in pipeline.
	expectedDims := map[string]string{
		telemetry.DimensionOperation:  operationName,
		telemetry.DimensionDependency: "redisPrimary:pipeline:exists",
	}
	samples, err := sampleObserver.GetSamplesWithValue(telemetry.MetricDependencySuccess, expectedDims, 1)
	require.NoError(t, err, sampleObserver.String())
	require.Len(t, samples, 2)

	samples, err = sampleObserver.GetSamplesWithValue(telemetry.MetricDependencyServerError, expectedDims, 0)
	require.NoError(t, err, sampleObserver.String())
	require.Len(t, samples, 2)

	samples, err = sampleObserver.GetSamplesWithValue(telemetry.MetricDependencyClientError, expectedDims, 0)
	require.NoError(t, err, sampleObserver.String())
	require.Len(t, samples, 2)

	samples, err = sampleObserver.GetSamplesWithPositiveValue(telemetry.MetricDependencyDuration, expectedDims)
	require.NoError(t, err, sampleObserver.String())
	require.Len(t, samples, 2)
}

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

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

	return newRedisClient(conf.Redis.Address, false, false, sampleReporter)
}
