package redis

import (
	"context"
	"encoding/json"
	"github.com/go-redis/redis/v8"
	"sync"
	"time"
)

var (
	internalClient *Client
	once           sync.Once
)

// Init 初始化redis客户端
func Init(ctx context.Context, addr, password string, db int) *Client {
	once.Do(func() {
		internalClient = newCli(ctx, addr, password, db)
	})
	return internalClient
}

// GetClient 获取redis客户端
func GetClient() *Client {
	return internalClient
}

func GetRedisClient() *redis.Client {
	return internalClient.cli
}

// New 创建redis客户端实例
func newCli(ctx context.Context, addr, password string, db int) *Client {
	cli := redis.NewClient(&redis.Options{
		Addr:     addr,
		Password: password,
		DB:       db,
	})

	cmd := cli.Ping(ctx)
	if err := cmd.Err(); err != nil {
		panic(err)
	}

	return &Client{
		cli,
	}
}

// Client redis客户端
type Client struct {
	cli *redis.Client
}

// GetRedisClient 获取redis客户端
func (a *Client) GetRedisClient() *redis.Client {
	return a.cli
}

// Set 设定值
func (a *Client) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
	data, _ := json.Marshal(value)

	cmd := a.cli.Set(ctx, key, data, expiration)
	return cmd.Err()
}

// Get 获取值
func (a *Client) Get(ctx context.Context, key string) (string, error) {
	cmd := a.cli.Get(ctx, key)
	if err := cmd.Err(); err != nil {
		if err == redis.Nil {
			return "", nil
		}
	}
	return cmd.Val(), nil
}

// GetForItem 获取缓存数据
func (a *Client) GetForItem(ctx context.Context, key string, item interface{}) error {
	cmd, err := a.cli.Get(ctx, key).Bytes()
	if err != nil {
		if err == redis.Nil {
			return nil
		}
	}
	return json.Unmarshal(cmd, item)
}

// Exists 判断是否存在值
func (a *Client) Exists(ctx context.Context, key string) (bool, error) {
	cmd := a.cli.Exists(ctx, key)

	val, err := cmd.Result()
	if err != nil {
		return false, err
	}
	return val > 0, nil
}

// Del 删除key
func (a *Client) Del(ctx context.Context, key string) error {
	cmd := a.cli.Del(ctx, key)
	return cmd.Err()
}

// Incr 自增加值
func (a *Client) Incr(ctx context.Context, key string) error {
	cmd := a.cli.Incr(ctx, key)
	return cmd.Err()
}

// Close 关闭连接
func (a *Client) Close() {
	a.cli.Close()
}
