package redisutils

import (
	"time"

	"github.com/fiskerinc/cloud-services/pkg/redis"
	"github.com/pkg/errors"
)

var (
	ErrCacheDoesntExist    = errors.New("key doesn't exist")
	ErrWrongResponseFormat = errors.New("wrong response format")
)

type CachedSet struct {
	redis redis.Client
}

func (s *CachedSet) SetConnection(client redis.Client) {
	s.redis = client
}

func (s *CachedSet) GetCachedSet(key string) (map[string]struct{}, error) {
	batch := redis.NewRedisBatchCommands()
	batch.Add("EXISTS", key)
	batch.Add("SMEMBERS", key)

	resultsI, err := s.redis.ExecuteBatch(batch)
	if err != nil {
		return nil, err
	}

	results, ok := resultsI.([]interface{})
	if !ok || len(results) != 2 {
		return nil, errResponseFormat("[2]interface{}", resultsI)
	}

	keyExists, ok := results[0].(int64)
	if !ok {
		return nil, errResponseFormat("int64", results[0])
	}
	if keyExists == 0 {
		return nil, ErrCacheDoesntExist
	}

	cacheRes, ok := results[1].([]interface{})
	if !ok {
		return nil, errResponseFormat("[]interface{}", results[1])
	}

	cacheSet := make(map[string]struct{}, len(cacheRes))
	for _, signal := range cacheRes {
		s, ok := signal.([]byte)
		if !ok {
			return nil, errResponseFormat("[]byte", signal)
		}
		cacheSet[string(s)] = struct{}{}
	}

	return cacheSet, nil
}

func (s *CachedSet) UpdateSetCache(key string, cacheValues []interface{}) error {
	saddCommand := append([]interface{}{"SADD", key}, cacheValues...)
	_, err := s.redis.Execute(saddCommand...)

	return err
}

func (s *CachedSet) CreateSetCache(key string, cacheValues []interface{}, expireTime time.Time) error {
	batch := redis.NewRedisBatchCommands()

	saddCommand := append([]interface{}{"SADD", key}, cacheValues...)
	batch.Add(saddCommand...)
	batch.Add("EXPIREAT", key, expireTime.Unix())

	_, err := s.redis.ExecuteBatch(batch)

	return err
}

func errResponseFormat(expectedType string, got interface{}) error {
	return errors.WithStack(
		errors.WithMessagef(ErrWrongResponseFormat, "expected type: %s, got: %v", expectedType, got),
	)
}
