package cache

import (
	"fmt"
	"github.com/go-redis/redis"
	"github.com/pkg/errors"
	"merchant/config"
	"time"
)

var _ Repo = (*cacheRepo)(nil)

type Repo interface {
	i()
	Set(key, value string, ttl time.Duration) error
	Get(key string) (string, error)
	TTL(key string) (time.Duration, error)
	Expire(key string, ttl time.Duration) bool
	ExpireAt(key string, ttl time.Time) bool
	Del(keys ...string) bool
	Exists(keys ...string) bool
	Incr(key string) int64
	Close() error
}

type cacheRepo struct {
	client *redis.Client
}

var cache *cacheRepo

func init() {
	client, err := redisConnect()
	if err != nil {
		return
	}

	cache = &cacheRepo{
		client: client,
	}
}

func GetRedis() *cacheRepo {
	if cache == nil {
		client, err := redisConnect()
		if err != nil {
			return &cacheRepo{}
		}

		cache = &cacheRepo{
			client: client,
		}
	}
	return cache
}

func (c *cacheRepo) i() {}

func redisConnect() (*redis.Client, error) {
	cfg, err := config.GetRedisCfg()
	if err != nil {
		return redis.NewClient(&redis.Options{}), err
	}
	client := redis.NewClient(&redis.Options{
		Addr:     fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),
		Password: cfg.Password,
		DB:       cfg.DB,
	})

	if err := client.Ping().Err(); err != nil {
		return nil, errors.Wrap(err, "ping redis err")
	}

	return client, nil
}

// Set set some <key,value> into redis
func (c *cacheRepo) Set(key, value string, ttl time.Duration) error {
	if err := c.client.Set(key, value, ttl).Err(); err != nil {
		return errors.Wrapf(err, "redis set key: %s err", key)
	}
	return nil
}

// Get get some key from redis
func (c *cacheRepo) Get(key string) (string, error) {
	value, err := c.client.Get(key).Result()
	if err != nil {
		return "", errors.Wrapf(err, "redis get key: %s err", key)
	}

	return value, nil
}

// TTL get some key from redis
func (c *cacheRepo) TTL(key string) (time.Duration, error) {
	ttl, err := c.client.TTL(key).Result()
	if err != nil {
		return -1, errors.Wrapf(err, "redis get key: %s err", key)
	}

	return ttl, nil
}

// Expire expire some key
func (c *cacheRepo) Expire(key string, ttl time.Duration) bool {
	ok, _ := c.client.Expire(key, ttl).Result()
	return ok
}

// ExpireAt expire some key at some time
func (c *cacheRepo) ExpireAt(key string, ttl time.Time) bool {
	ok, _ := c.client.ExpireAt(key, ttl).Result()
	return ok
}

func (c *cacheRepo) Exists(keys ...string) bool {
	if len(keys) == 0 {
		return true
	}
	value, _ := c.client.Exists(keys...).Result()
	return value > 0
}

// Del del some key from redis
func (c *cacheRepo) Del(keys ...string) bool {
	if len(keys) == 0 {
		return true
	}

	value, _ := c.client.Del(keys...).Result()
	return value > 0
}

func (c *cacheRepo) Incr(key string) int64 {
	value, _ := c.client.Incr(key).Result()
	return value
}

// Close close redis client
func (c *cacheRepo) Close() error {
	return c.client.Close()
}
