// +build integration

package memcached

import (
	"context"
	"testing"

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

const (
	cacheKey = "testKey"
)

func TestGet(t *testing.T) {
	t.Run("Test Get command", func(t *testing.T) {
		cache := newTestCacheClient(t)

		require.NotNil(t, cache)

		err := cache.Set(context.Background(), cacheKey, "foobar")
		require.NoError(t, err)

		var result string
		found, err := cache.Get(context.Background(), cacheKey, &result)
		require.True(t, found)
		require.NoError(t, err)
		require.NotNil(t, result)
		require.Equal(t, "foobar", result)
	})

	t.Run("Test Get when key doesn't exist", func(t *testing.T) {
		cache := newTestCacheClient(t)

		require.NotNil(t, cache)

		var result string
		found, err := cache.Get(context.Background(), cacheKey, &result)
		require.False(t, found)
		require.NoError(t, err)
		require.Empty(t, result)
	})
}

func TestAdd(t *testing.T) {
	t.Run("test basic add command", func(t *testing.T) {
		cache := newTestCacheClient(t)

		require.NotNil(t, cache)

		err := cache.Add(context.Background(), cacheKey, "foobar")
		require.NoError(t, err)

		var result string
		found, err := cache.Get(context.Background(), cacheKey, &result)
		require.True(t, found)
		require.NoError(t, err)
		require.NotNil(t, result)
		require.NotEmpty(t, result)
		require.Equal(t, "foobar", result)
	})

	t.Run("test trying to add when key exists", func(t *testing.T) {
		cache := newTestCacheClient(t)

		require.NotNil(t, cache)

		err := cache.Add(context.Background(), cacheKey, "foobar")
		require.NoError(t, err)

		err = cache.Add(context.Background(), cacheKey, "foobar2")
		require.Error(t, err)

		var result string
		found, err := cache.Get(context.Background(), cacheKey, &result)
		require.True(t, found)
		require.NoError(t, err)
		require.NotNil(t, result)
		require.NotEmpty(t, result)
		require.Equal(t, "foobar", result)
	})

	t.Run("test adding nil", func(t *testing.T) {
		cache := newTestCacheClient(t)

		require.NotNil(t, cache)

		err := cache.Add(context.Background(), cacheKey, nil)
		require.NoError(t, err)

		var result string
		found, err := cache.Get(context.Background(), cacheKey, &result)
		require.True(t, found)
		require.NoError(t, err)
		require.Empty(t, result)
		require.NotNil(t, result)
	})
}

func TestSet(t *testing.T) {
	t.Run("Test basic Set command", func(t *testing.T) {
		cache := newTestCacheClient(t)

		require.NotNil(t, cache)

		err := cache.Set(context.Background(), cacheKey, "foobar")
		require.NoError(t, err)

		var result string
		found, err := cache.Get(context.Background(), cacheKey, &result)
		require.True(t, found)
		require.NoError(t, err)
		require.NotNil(t, result)
		require.NotEmpty(t, result)
		require.Equal(t, "foobar", result)

	})

	t.Run("Test overwriting with set", func(t *testing.T) {
		cache := newTestCacheClient(t)

		require.NotNil(t, cache)

		err := cache.Add(context.Background(), cacheKey, "foobar")
		require.NoError(t, err)

		var result string
		found, err := cache.Get(context.Background(), cacheKey, &result)
		require.True(t, found)
		require.NoError(t, err)
		require.NotNil(t, result)
		require.NotEmpty(t, result)
		require.Equal(t, "foobar", result)

		err = cache.Set(context.Background(), cacheKey, "foobar2")
		require.NoError(t, err)

		found, err = cache.Get(context.Background(), cacheKey, &result)
		require.True(t, found)
		require.NoError(t, err)
		require.NotNil(t, result)
		require.NotEmpty(t, result)
		require.Equal(t, "foobar2", result)
	})

	t.Run("Test setting nil", func(t *testing.T) {
		cache := newTestCacheClient(t)

		require.NotNil(t, cache)

		err := cache.Set(context.Background(), cacheKey, nil)
		require.NoError(t, err)

		var result string
		found, err := cache.Get(context.Background(), cacheKey, &result)
		require.True(t, found)
		require.NoError(t, err)
		require.Empty(t, result)
		require.NotNil(t, result)
	})
}

func TestDelete(t *testing.T) {
	t.Run("Test basic delete command", func(t *testing.T) {
		cache := newTestCacheClient(t)
		require.NotNil(t, cache)

		err := cache.Set(context.Background(), cacheKey, "foobar")
		require.NoError(t, err)

		var result string
		found, err := cache.Get(context.Background(), cacheKey, &result)
		require.True(t, found)
		require.NoError(t, err)
		require.NotNil(t, result)
		require.NotEmpty(t, result)
		require.Equal(t, "foobar", result)

		err = cache.Delete(context.Background(), cacheKey)
		require.NoError(t, err)

		found, err = cache.Get(context.Background(), cacheKey, &result)
		require.False(t, found)
		require.NoError(t, err)
	})

	t.Run("Test delete when key doesn't exist", func(t *testing.T) {
		cache := newTestCacheClient(t)
		require.NotNil(t, cache)

		var result string
		found, err := cache.Get(context.Background(), cacheKey, &result)
		require.False(t, found)
		require.NoError(t, err)

		err = cache.Delete(context.Background(), cacheKey)
		require.NoError(t, err)
	})
}

func newTestCacheClient(t *testing.T) *memcachedClient {
	conf, err := config.GetConfig()
	require.True(t, conf.Environment == config.EnvCI || conf.Environment == config.EnvDev)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotZero(t, conf.Memcached.ConfigurationEndpoint)

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

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

	return cache
}
