// +build integration

package logic

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

	"code.justin.tv/live/autohost/internal/hosting/redis"

	"code.justin.tv/live/autohost/internal/metrics"

	"code.justin.tv/live/autohost/internal/hosting/eventbus"
	"code.justin.tv/live/autohost/internal/hosting/pdms"

	"code.justin.tv/live/autohost/internal/hosting/memcached"

	"code.justin.tv/live/autohost/internal/localdynamo"

	"code.justin.tv/creator-collab/log"
	"code.justin.tv/live/autohost/internal/hosting/auth"
	"code.justin.tv/live/autohost/internal/hosting/clients/clue"
	"code.justin.tv/live/autohost/internal/hosting/clients/sns"
	"code.justin.tv/live/autohost/internal/hosting/clients/spade"
	"code.justin.tv/live/autohost/internal/hosting/config"
	"code.justin.tv/live/autohost/internal/hosting/storage"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbiface"
	"github.com/stretchr/testify/require"
)

type testEnv struct {
	localTables    *localdynamo.Tables
	storageClient  storage.Storage
	dynamoDBClient dynamodbiface.DynamoDBAPI
	memcachedCache memcached.Cache
	logic          Logic
}

func newTestEnv(t *testing.T, logicParamOverrides *Params) *testEnv {
	conf, err := config.GetConfig()
	require.True(t, conf.Environment == config.EnvCI || conf.Environment == config.EnvDev)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotEmpty(t, conf.DynamoDB.LocalDynamoEndpoint)
	require.NotEmpty(t, conf.DynamoDB.HostTableName)

	logger := log.NewDevelopmentLogger()

	localTables, err := localdynamo.NewLocalDynamoTables(conf.DynamoDB.LocalDynamoEndpoint, logger)
	require.NoError(t, err)

	localDynamoClient, err := localdynamo.NewLocalDynamoDBClient(conf.DynamoDB.LocalDynamoEndpoint)
	require.NoError(t, err)

	storageClient := storage.New(&storage.Params{
		Client:            localDynamoClient,
		Logger:            log.NewDevelopmentLogger(),
		HostTableName:     conf.DynamoDB.HostTableName,
		SettingsTableName: conf.DynamoDB.SettingsTableName,
	})

	cache, err := memcached.NewCache(conf.Memcached)
	require.NoError(t, err)

	sampleReporter := metrics.NewSampleReporter(&metrics.SampleReporterConfig{
		Environment:             conf.Environment,
		Logger:                  logger,
		Region:                  "us-west-2",
		RunningInEC2:            conf.RunningInEC2,
		SendMetricsToCloudwatch: conf.EnableCloudWatchMetrics,
		ServiceName:             "Autohost Server",
	})

	params := &Params{
		DB:             storageClient,
		Memcached:      cache,
		PDMSClient:     &pdms.Stub{},
		SNSClient:      sns.NewNoopClient(),
		Auth:           &auth.Stub{},
		ClueClient:     clue.NewNoopClient(),
		SpadeClient:    spade.NewNoopClient(),
		EventPublisher: eventbus.NewNoopPublisher(),
		Logger:         logger,
		SampleReporter: sampleReporter,
		RateLimiter:    redis.NewRateLimiterStub(true),
	}

	if logicParamOverrides != nil {
		if logicParamOverrides.DB != nil {
			params.DB = logicParamOverrides.DB
		}
		if logicParamOverrides.Memcached != nil {
			params.Memcached = logicParamOverrides.Memcached
		}
		if logicParamOverrides.PDMSClient != nil {
			params.PDMSClient = logicParamOverrides.PDMSClient
		}
		if logicParamOverrides.TwitchRecsClient != nil {
			params.TwitchRecsClient = logicParamOverrides.TwitchRecsClient
		}
		if logicParamOverrides.SNSClient != nil {
			params.SNSClient = logicParamOverrides.SNSClient
		}
		if logicParamOverrides.Auth != nil {
			params.Auth = logicParamOverrides.Auth
		}
		if logicParamOverrides.ClueClient != nil {
			params.ClueClient = logicParamOverrides.ClueClient
		}
		if logicParamOverrides.SpadeClient != nil {
			params.SpadeClient = logicParamOverrides.SpadeClient
		}
		if logicParamOverrides.Logger != nil {
			params.Logger = logicParamOverrides.Logger
		}
		if logicParamOverrides.RateLimiter != nil {
			params.RateLimiter = logicParamOverrides.RateLimiter
		}
	}

	hostingLogic, err := New(params)
	require.NoError(t, err)
	require.NotNil(t, hostingLogic)

	t.Cleanup(func() {
		// Clear the memcached database and close all open connections
		cache.FlushAll()
		cache.Close()
	})

	return &testEnv{
		localTables:    localTables,
		storageClient:  storageClient,
		dynamoDBClient: localDynamoClient,
		memcachedCache: params.Memcached,
		logic:          hostingLogic,
	}
}

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

func getContextWithTimeout() (context.Context, context.CancelFunc) {
	return context.WithTimeout(context.Background(), time.Second)
}

func boolPtr(b bool) *bool {
	return &b
}
