package redis

import (
	"testing"

	m "github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/testhelper"

	"github.com/gomodule/redigo/redis"
)

func newMockConnClient() Client {
	return NewClient(GetMockPool().Get())
}

func TestConnClient(t *testing.T) {
	MockRedisConnection()
	conn := newMockConnClient()
	defer conn.Close()

	c := conn.GetConn()
	conn.SetConn(c)

	if c == nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnClient", "conn client", c)
	}
}

func TestConnClose(t *testing.T) {
	MockRedisConnection()
	conn := newMockConnClient()

	_ = conn.GetConn()
	err := conn.Close()

	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnClose", nil, err)
	}
}

func TestConnQueueMessage(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	err := conn.SafeQueueMessage("TESTVIN123", "hello fisker!")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnQueueMessage", nil, err)
	}
}

func TestConnPublishMessage(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	err := conn.SafePublishMessage("TESTVIN123", "hello fisker!")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnPublishMessage", nil, err)
	}
}

func TestConnBatchQueueMessages(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	err := conn.BatchQueueMessages(
		[]string{"TESTVIN123", "TESTVIN456"},
		[]interface{}{"hello ocean!", "hello pear!"})
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnBatchQueueMessages", nil, err)
	}
}

func TestConnBatchPublishMessages(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	err := conn.BatchPublishMessages(
		[]string{"TESTVIN123", "TESTVIN456"},
		[]interface{}{"hello ocean!", "hello pear!"})
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnBatchPublishMessages", nil, err)
	}
}

func TestConnSafeQueueMessage(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	err := conn.SafeQueueMessage("TESTVIN123", "hello fisker!")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnQueueMessage", nil, err)
	}
}

func TestConnSafePublishMessage(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	err := conn.SafePublishMessage("TESTVIN123", "hello fisker!")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnPublishMessage", nil, err)
	}
}

func TestConnSet(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	err := conn.Set("TESTKEY", true)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSet", nil, err)
	}
}

func TestConnGet(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	_, err := conn.Get("TESTKEY")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGet", nil, err)
	}
}

func TestConnDelete(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	err := conn.Delete("TESTKEY")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnDelete", nil, err)
	}
}

func TestConnNewSet(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	err := conn.NewSet("TESTKEY", []string{"cognito-id-1", "cognito-id-2"}, 0)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnNewSet", nil, err)
	}
}

func TestConnGetSet(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	var ids []string
	err := conn.GetSet("TESTKEY", &ids)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGetSet", nil, err)
	}
}

func TestConnSetObjectStruct(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	l := m.Locks{
		Driver: true,
		All:    false,
	}

	err := conn.SetObject("TESTVIN123:locks", &l, -1)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSetObjectStruct", nil, err)
	}
}

func TestConnSetObjectStructExpiring(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	l := m.Locks{
		Driver: true,
		All:    false,
	}

	err := conn.SetObject("TESTVIN123:locks", &l, 100)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSetObjectStruct", nil, err)
	}
}

func TestConnSetObjectMap(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	l := map[string]interface{}{
		"left_front":  true,
		"left_rear":   true,
		"right_front": false,
		"right_rear":  false,
		"trunk":       false,
	}

	err := conn.SetObject("TESTVIN123:locks", l, -1)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSetObjectMap", nil, err)
	}
}

func TestConnSetObjectField(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	location := m.Location{
		Altitude:  10,
		Longitude: 15,
		Latitude:  20,
	}

	serialized, err := location.Marshal()
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSetObjectField", nil, err)
	}

	err = conn.SetObjectField(CarLocationsKey(), "TESTVIN123", serialized)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSetObjectField", nil, err)

	}
}

func TestConnSetObjects(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	ids := []string{"TESTKEY1", "TESTKEY2", "TESTKEY3"}
	objects := []interface{}{
		m.Locks{
			Driver: true,
			All:    true,
		},
		m.Locks{
			Driver: false,
			All:    false,
		},
		m.Locks{
			Driver: true,
			All:    false,
		},
	}

	err := conn.SetObjects(ids, objects, 60)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSetObjects", nil, err)
	}
}

func TestConnSetObjectsError(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	ids := []string{"TESTKEY1", "TESTKEY2", "TESTKEY3"}
	objects := []interface{}{}

	err := conn.SetObjects(ids, objects, 60)
	if err == nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSetObjectsError", "error", err)
	}
}

func TestConnGetObjectStruct(t *testing.T) {
	MockRedisConnection()
	var conn Client = newMockConnClient()
	defer conn.Close()

	var o m.Locks
	err := conn.GetObject("TESTVIN123:locks", &o)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGetObjectStruct", nil, err)
	}
}

func TestConnGetObjectMap(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	_, err := client.GetObjectMap("TESTVIN123:locks")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGetObjectMap", nil, err)
	}
}

func TestConnGetObjectRaw(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	_, err := client.GetObjectRaw(CarLocationsKey())
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGetObjectRaw", nil, err)
	}
}

func TestConnGetObjectField(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	_, err := client.GetObjectField(CarLocationsKey(), "TESTVIN123")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGetObjectField", nil, err)
	}
}

func TestConnGetObjectsMulti(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	keys := []string{"TESTVIN123:locks", CarUpdateStatusHashKey(1234)}
	os := []interface{}{&m.Locks{}, &m.CarUpdateProgress{}}

	err := client.GetObjectsMulti(keys, os)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGetObjectsMulti", nil, err)
	}

	if len(keys) != len(os) {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGetObjectsMulti", len(keys), len(os))
	}
}

func TestConnGetObjectsMultiMap(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	keys := []string{"TESTVIN123:locks", CarUpdateStatusHashKey(1234)}

	os, err := client.GetObjectsMultiMap(keys)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGetObjectsMultiMap", nil, err)
	}

	if os == nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGetObjectsMultiMap", "map", os)
	}
}

func TestConnGetValuesMulti(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	ids := []string{"test1", "test2", "test3"}
	data := make([]string, len(ids))
	err := client.GetValuesMulti(ids, &data)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnGetValuesMulti", nil, err)
	}
}

func TestConnRetrieve(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	var value interface{}
	err := client.Retrieve("HGET test1", &value)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnRetrieve", nil, err)
	}
}

func TestConnSafeSet(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	err := client.SafeSet("TESTKEY", true)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSafeSet", nil, err)
	}
}

func TestConnSafeGet(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	_, err := client.SafeGet("TESTKEY")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSafeGet", nil, err)
	}
}

func TestConnSafeDelete(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	err := client.SafeDelete("TESTKEY")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSafeDelete", nil, err)
	}
}

func TestConnSafeNewSet(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	err := client.SafeNewSet("TESTKEY", []string{"cognito-id-1", "cognito-id-2"}, 0)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSafeNewSet", nil, err)
	}
}

func TestConnSafeGetSet(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	var ids []string
	err := client.SafeGetSet("TESTKEY", &ids)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSafeGetSet", nil, err)
	}
}

func TestConnSafeSetObjectStruct(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	l := m.Locks{
		Driver: true,
		All:    false,
	}

	err := client.SafeSetObject("TESTVIN123:locks", &l, -1)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSafeSetObjectStruct", nil, err)
	}
}

func TestConnSafeGetObjectStruct(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	var o m.Locks
	err := client.SafeGetObject("TESTVIN123:locks", &o)
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnSafeGetObjectStruct", nil, err)
	}
}

func TestConnExecute(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	_, err := client.Execute("GET", "test_key")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnExecute", nil, err)
	}

	_, err = client.Execute("SET", "test_key", "test_value")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnExecute", nil, err)
	}
}

func TestConnSafeExecute(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()

	_, err := client.Execute("HGETALL", "test_key")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnExecute", nil, err)
	}

	_, err = client.Execute("HGET", "test_key", "test_field")
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnExecute", nil, err)
	}
}

func TestConnAddToBatch(t *testing.T) {
	batch := NewRedisBatchCommands()
	batch.Add("SET", "test_key", "test_value")
}

func TestConnExecuteBatch(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()
	batch := NewRedisBatchCommands()

	batch.Add("SET", "test_key", "test_value")
	result, err := redis.Values(client.ExecuteBatch(batch))
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnExecuteBatch", nil, err)
	}

	if len(result) != 0 {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnExecuteBatch", 0, len(result))
	}
}

func TestConnSafeExecuteBatch(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()
	batch := NewRedisBatchCommands()

	batch.Add("SET", "test_key", "test_value")
	result, err := redis.Values(client.SafeExecuteBatch(batch))
	if err != nil {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnExecuteBatch", nil, err)
	}

	if len(result) != 0 {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnExecuteBatch", 0, len(result))
	}
}

func TestConnBatchIsEmpty(t *testing.T) {
	MockRedisConnection()
	client := newMockConnClient()
	defer client.Close()
	batch := NewRedisBatchCommands()

	if !batch.IsEmpty() {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnBatchIsEmpty", true, false)
	}

	batch.Add("SET", "test_key", "test_value")

	if batch.IsEmpty() {
		t.Errorf(testhelper.TestErrorTemplate, "TestConnBatchIsEmpty", false, true)
	}
}
