package redis

import (
	"errors"
	"fmt"
	//	"errors"
	"strconv"
	"time"

	"gopkg.in/redis.v3"
)

const maxConn = 100

var redisChan chan *redis.Client

var option *redis.Options

func Init(addr string, pwd string, db int64) error {
	option = &redis.Options{
		Addr:     addr,
		Password: pwd,
		DB:       db,
	}
	redisChan = make(chan *redis.Client, maxConn)
	for i := 0; i < maxConn; i++ {
		client, err := creatRedisClient(option)
		if err != nil {
			return err
		}
		redisChan <- client
	}
	return nil
}

// 创建redis对象
func creatRedisClient(option *redis.Options) (*redis.Client, error) {

	client := redis.NewClient(option)
	// 检测client有效性
	if nil != client {
		_, err := client.Ping().Result()
		if nil != err {
			client.Close()
			return nil, errors.New(fmt.Sprintf("fail to ping redis-svr,addr :%s , pwd :%s ,DB :%d", option.Addr, option.Password, option.DB))
		}
	} else {
		return nil, errors.New(fmt.Sprintf("fail to connect redis-svr,,addr :%s , pwd :%s ,DB :%d", option.Addr, option.Password, option.DB))
	}
	return client, nil
}

// 获取redis
func getRedis() (*redis.Client, error) {
	var client *redis.Client

	select {
	case <-time.After(time.Second * 10):
	case client = <-redisChan:
	}

	// 检测client有效性
	if nil != client {
		_, err := client.Ping().Result()
		if nil != err {
			client.Close()
			// 尝试3次重连
			for i := 0; i < 3; i++ {
				client, err = creatRedisClient(option)
				if client != nil {
					return client, err
				}
			}

			return nil, err
		}
	}
	return client, nil
}

// 将redis链接放回连接池
func relaseRedis(client *redis.Client) {
	select {
	case <-time.After(time.Second * 10):
		client.Close()
	case redisChan <- client:
	}
}

func Get(key string) (string, error) {
	client, err := getRedis()
	if err != nil {
		return "", err
	}
	if client == nil {
		return "", errors.New("failed to get rds client")
	}
	defer relaseRedis(client)

	val, err := client.Get(key).Result()
	if nil != err {
		if err.Error() == "redis: nil" {
			return "", nil
		}
		return "", err
	}

	return val, nil
}

// redis查询
func Keys(key string) ([]string, error) {
	client, err := getRedis()
	if err != nil {
		return nil, err
	}
	if client == nil {
		return nil, errors.New("failed to get rds client")
	}
	defer relaseRedis(client)

	val, err := client.Keys(key).Result()
	if nil != err {
		var nullResult = []string{""}
		return nullResult, err
	}

	return val, nil
}

func Set(key string, val string, expire ...time.Duration) error {
	client, err := getRedis()
	if err != nil {
		return err
	}
	if client == nil {
		return errors.New("failed to get rds client")
	}
	defer relaseRedis(client)

	var t time.Duration = 0

	if len(expire) == 1 {
		t = expire[0]
	}

	_, err = client.Set(key, val, t).Result()

	return err
}
func HSet(key string, filed, val string) error {
	client, err := getRedis()
	if err != nil {
		return err
	}
	if client == nil {
		return errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	_, err = client.HSet(key, filed, val).Result()
	return err
}
func SIsMember(key string, val int32) (bool, error) {
	client, err := getRedis()
	if err != nil {
		return false, err
	}
	if client == nil {
		return false, errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	isExist, err := client.SIsMember(key, val).Result()
	return isExist, err
}
func SAdd(key string, members ...string) error {
	client, err := getRedis()
	if err != nil {
		return err
	}
	if client == nil {
		return errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	_, err = client.SAdd(key, members...).Result()
	return err
}
func SRem(key string, members ...string) error {
	client, err := getRedis()
	if err != nil {
		return err
	}
	if client == nil {
		return errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	_, err = client.SRem(key, members...).Result()
	return err
}
func SMembers(key string) ([]string, error) {
	client, err := getRedis()
	if err != nil {
		return nil, err
	}
	if client == nil {
		return nil, errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	members, err := client.SMembers(key).Result()
	return members, err
}
func HIncrBy(key, filed string, val int64) error {
	client, err := getRedis()
	if err != nil {
		return err
	}
	if client == nil {
		return errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	_, err = client.HIncrBy(key, filed, val).Result()
	return err
}
func LPush(key string, val ...string) error {
	client, err := getRedis()
	if err != nil {
		return err
	}
	if client == nil {
		return errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	_, err = client.LPush(key, val...).Result()
	return err
}
func BRPopLPush(source string, dest string, time time.Duration) (string, error) {
	client, err := getRedis()
	if err != nil {
		return "", err
	}
	if client == nil {
		return "", errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	val, err := client.BRPopLPush(source, dest, time).Result()
	return val, err
}
func RPop(key string) (string, error) {
	client, err := getRedis()
	if err != nil {
		return "", err
	}
	if client == nil {
		return "", errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	val, err := client.RPop(key).Result()
	return val, err
}
func LLen(key string) (int64, error) {
	client, err := getRedis()
	if err != nil {
		return 0, err
	}
	if client == nil {
		return 0, errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	val, err := client.LLen(key).Result()
	return val, err
}
func LTrim(key string, start, stop int64) (string, error) {
	client, err := getRedis()
	if err != nil {
		return "", err
	}
	if client == nil {
		return "", errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	val, err := client.LTrim(key, start, stop).Result()
	return val, err
}
func LRange(key string, start, stop int64) ([]string, error) {
	client, err := getRedis()
	if err != nil {
		return nil, err
	}
	if client == nil {
		return nil, errors.New("failed to get rds client")
	}
	defer relaseRedis(client)

	val, err := client.LRange(key, start, stop).Result()
	if nil != err {
		return nil, err
	}

	return val, nil
}
func UpdateExpire(key string, expire time.Duration) error {
	client, err := getRedis()
	if err != nil {
		return err
	}
	if client == nil {
		return errors.New("failed to get rds client")
	}
	defer relaseRedis(client)

	_, err = client.Expire(key, expire).Result()

	return err
}

func HGet(key string, hash string) (string, error) {
	client, err := getRedis()
	if err != nil {
		return "", err
	}
	if client == nil {
		return "", errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	val, err := client.HGet(key, hash).Result()
	if nil != err {
		return "", err
	}

	return val, nil
}
func HGetInt64(key string, hash string) (int64, error) {
	client, err := getRedis()
	if err != nil {
		return 0, err
	}
	if client == nil {
		return 0, errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	val, err := client.HGet(key, hash).Int64()
	if nil != err {
		return 0, err
	}

	return val, nil
}
func SCard(key string) (int64, error) {
	client, err := getRedis()
	if err != nil {
		return 0, err
	}
	if client == nil {
		return 0, errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	val, err := client.SCard(key).Result()
	if nil != err {
		return 0, err
	}

	return val, nil
}

// 获取时间
func Time() (int64, error) {
	client, err := getRedis()
	if err != nil {
		return 0, err
	}
	if client == nil {
		return 0, errors.New("failed to get rds client")
	}
	defer relaseRedis(client)
	// 读取redis时间
	r := client.Time()
	if nil == r {
		return 0, errors.New("read redis error")
	}

	if nil != r.Err() {
		return 0, r.Err()
	}

	return strconv.ParseInt(r.Val()[0], 10, 0)
}

// 删除指定key的redis记录
func Del(key string) (int64, error) {
	client, err := getRedis()
	if err != nil {
		return 0, err
	}
	if client == nil {
		return 0, errors.New("failed to get rds client")
	}
	defer relaseRedis(client)

	var iResult int64
	cmdResult := client.Del(key)
	if nil == cmdResult.Err() {
		iResult, _ = cmdResult.Result()
	} else {
		iResult = 0
	}

	return iResult, nil
}

// 清空rendis_db
func TruncateDB() (string, error) {
	client, err := getRedis()
	if err != nil {
		return "", err
	}
	if client == nil {
		return "", errors.New("failed to get rds client")
	}
	defer relaseRedis(client)

	cmdResult := client.FlushDb()
	if cmdResult.Err() != nil {
		return "", cmdResult.Err()
	}

	return cmdResult.Result()
}
