package redisutils_test

import (
	"errors"
	"testing"
	"time"

	"github.com/fiskerinc/cloud-services/pkg/redis/redisutils"
	"github.com/fiskerinc/cloud-services/pkg/redis/tester"
	"github.com/stretchr/testify/assert"
)

func TestGetCacheSet(t *testing.T) {
	key := "someKey"
	redisMock := tester.NewRedisMock()

	tests := map[string]struct {
		getResults map[string]map[string]interface{}
		batchError error
		expRes     map[string]struct{}
		expError   error
	}{
		"correct": {
			getResults: map[string]map[string]interface{}{
				"EXISTS": {
					key: int64(1),
				},
				"SMEMBERS": {
					key: []interface{}{
						[]byte("609:ESP_ActvSig_DTC"),
						[]byte("832:ADAS_FltIndcr"),
						[]byte("832:ADAS_IntegtCrsFltTxt"),
					},
				},
			},
			expRes: map[string]struct{}{
				"609:ESP_ActvSig_DTC":      {},
				"832:ADAS_FltIndcr":        {},
				"832:ADAS_IntegtCrsFltTxt": {},
			},
		},
		"key_doesnt_exist": {
			getResults: map[string]map[string]interface{}{
				"EXISTS": {
					key: int64(0),
				},
			},
			expError: redisutils.ErrCacheDoesntExist,
		},
		"batch_error": {
			batchError: errors.New("some error"),
			expError:   errors.New("some error"),
		},
		"wrong_batch_first_elem": {
			getResults: map[string]map[string]interface{}{
				"EXISTS": {
					key: 1,
				},
				"SMEMBERS": {
					key: []interface{}{},
				},
			},
			expError: errors.New("expected type: int64, got: 1: wrong response format"),
		},
		"wrong_batch_second_elem": {
			getResults: map[string]map[string]interface{}{
				"EXISTS": {
					key: int64(1),
				},
				"SMEMBERS": {
					key: 1,
				},
			},
			expError: errors.New("expected type: []interface{}, got: 1: wrong response format"),
		},
		"wrong_batch_second_sub_elem": {
			getResults: map[string]map[string]interface{}{
				"EXISTS": {
					key: int64(1),
				},
				"SMEMBERS": {
					key: []interface{}{1},
				},
			},
			expError: errors.New("expected type: []byte, got: 1: wrong response format"),
		},
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			redisMock.GetCommandResult = tt.getResults
			redisMock.Error = tt.batchError

			cSet := redisutils.CachedSet{}
			cSet.SetConnection(redisMock)

			res, err := cSet.GetCachedSet(key)
			if err != nil && tt.expError != nil {
				assert.Equal(t, tt.expError.Error(), err.Error())
				return
			}

			assert.Equal(t, tt.expError, err)
			assert.Equal(t, tt.expRes, res)
		})
	}
}

func TestCreateSetCache(t *testing.T) {
	redisMock := tester.NewRedisMock()
	mockTime := time.Date(2022, 8, 11, 15, 53, 0, 0, time.UTC)

	tests := map[string]struct {
		cacheValues  []interface{}
		batchError   error
		expSetValues map[string]tester.ExpiringCache
		expError     error
	}{
		"correct": {
			cacheValues: []interface{}{
				"609:ESP_ActvSig_DTC",
				"832:ADAS_FltIndcr",
				"832:ADAS_IntegtCrsFltTxt",
			},
			expSetValues: map[string]tester.ExpiringCache{
				"someKey": {
					Value: []interface{}{
						"609:ESP_ActvSig_DTC",
						"832:ADAS_FltIndcr",
						"832:ADAS_IntegtCrsFltTxt",
					},
					Expires: 1660233180,
				},
			},
		},
		"fail": {
			batchError: errors.New("some error"),
			expError:   errors.New("some error"),
		},
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			redisMock.Reset()
			redisMock.Error = tt.batchError

			cSet := redisutils.CachedSet{}
			cSet.SetConnection(redisMock)

			err := cSet.CreateSetCache("someKey", tt.cacheValues, mockTime)
			if err != nil && tt.expError != nil {
				assert.Equal(t, tt.expError.Error(), err.Error())
				return
			}

			assert.Equal(t, tt.expError, err)
			assert.Equal(t, tt.expSetValues, redisMock.SetValues)
		})
	}
}

func TestUpdateSetCache(t *testing.T) {
	redisMock := tester.NewRedisMock()

	tests := map[string]struct {
		cacheValues  []interface{}
		execError    error
		expSetValues map[string]tester.ExpiringCache
		expError     error
	}{
		"correct": {
			cacheValues: []interface{}{
				"609:ESP_ActvSig_DTC",
				"832:ADAS_FltIndcr",
				"832:ADAS_IntegtCrsFltTxt",
			},
			expSetValues: map[string]tester.ExpiringCache{
				"someKey": {
					Value: []interface{}{
						"609:ESP_ActvSig_DTC",
						"832:ADAS_FltIndcr",
						"832:ADAS_IntegtCrsFltTxt",
					},
				},
			},
		},
		"fail": {
			execError: errors.New("some error"),
			expError:  errors.New("some error"),
		},
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			redisMock.Reset()
			redisMock.Error = tt.execError

			cSet := redisutils.CachedSet{}
			cSet.SetConnection(redisMock)

			err := cSet.UpdateSetCache("someKey", tt.cacheValues)
			if err != nil && tt.expError != nil {
				assert.Equal(t, tt.expError.Error(), err.Error())
				return
			}

			assert.Equal(t, tt.expError, err)
			assert.Equal(t, tt.expSetValues, redisMock.SetValues)
		})
	}
}
