package redisdo

import (
	"errors"
	"leafstalk/covenant/monitor"
	"strconv"
	"time"

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

var (
	pool *redis.Pool
)

func newRedisPool(addr string, password string, db int) *redis.Pool {
	// addr := config.GetString("redis.addr")
	// password := config.GetString("redis.auth")
	// db := config.GetInt("redis.db")

	return &redis.Pool{
		MaxIdle:     10,
		IdleTimeout: 240 * time.Second,
		// Dial or DialContext must be set. When both are set, DialContext takes precedence over Dial.
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", addr)
			if err != nil {
				return nil, err
			}
			if len(password) != 0 {
				if _, err := c.Do("AUTH", password); err != nil {
					c.Close()
					return nil, err
				}
			}
			if _, err := c.Do("SELECT", db); err != nil {
				c.Close()
				return nil, err
			}
			return c, nil
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if time.Since(t) < time.Minute {
				return nil
			}
			_, err := c.Do("PING")
			return err
		},
	}
}

func InitRedisPool(addr string, password string, db int) {
	pool = newRedisPool(addr, password, db)
}

// CloseRedisPool 关闭reids池
func CloseRedisPool() {
	if pool != nil {
		pool.Close()
	}
}

func Do(commandName string, args ...interface{}) (reply interface{}, err error) {
	defer monitor.RedisDoTimeoutWarn("Do", map[string]interface{}{
		"commandName": commandName,
		"args":        args,
	}, time.Now())

	conn := pool.Get()
	defer conn.Close()
	return conn.Do(commandName, args...)
}

func DoMulti(f func(conn redis.Conn) error) error {
	defer monitor.RedisDoTimeoutWarn("DoMulti", "", time.Now())
	conn := pool.Get()
	defer conn.Close()
	return f(conn)
	//return conn.Do(commandName, args...)
}

// 保存一个集合
func DoFlat(commandName string, key string, val interface{}) (reply interface{}, err error) {
	conn := pool.Get()
	defer conn.Close()

	//组织为串
	return conn.Do(commandName, redis.Args{key}.AddFlat(val)...)
}

func IsNilError(err error) bool {
	return err == redis.ErrNil
}

func DoHashSet(key string, val interface{}) (reply interface{}, err error) {
	conn := pool.Get()
	defer conn.Close()

	//以hash类型保存
	return conn.Do("hmset", redis.Args{key}.AddFlat(val)...)
}

// 是否发现，是否出错
func DoHashGet(key string, strc interface{}) (bool, error) {
	conn := pool.Get()
	defer conn.Close()

	//获取缓存
	value, err := redis.Values(conn.Do("hgetall", key))
	if err != nil {
		return false, err
	}
	if len(value) == 0 {
		return false, nil
	}

	//将values转成结构体
	err = redis.ScanStruct(value, strc)
	if err != nil {
		return true, err
	}
	return true, nil
}

// 模糊匹配搜索
func ScanKeys(start int, pattern string, count int) (int, []string, error) {
	rep, err := Do("scan", start, "match", pattern, "count", count)
	if err != nil {
		return 0, nil, err
	}
	rep2 := rep.([]interface{})
	if len(rep2) != 2 {
		return 0, nil, errors.New("replay length is error")
	}

	nextSt := ""
	if strSt, ok := rep2[0].([]byte); !ok {
		return 0, nil, errors.New("replay type is error")
	} else {
		nextSt = string(strSt)
	}

	var lst []string
	if ks, ok := rep2[1].([]interface{}); !ok {
		return 0, nil, errors.New("replay type is error")
	} else {
		for _, v := range ks {
			if nv, ok := v.([]byte); ok {
				k := string(nv)
				lst = append(lst, k)
			}
		}
	}
	st, err := strconv.Atoi(nextSt)
	if err != nil {
		return 0, lst, nil
	}
	return st, lst, nil
}

func HScanKeys(key string, start int, pattern string, count int) (int, map[string]string, error) {
	rep, err := redis.Values(Do("hscan", key, start, "match", pattern, "count", count))
	if err != nil {
		return 0, nil, err
	}

	next, err := redis.Int(rep[0], err)
	if err != nil {
		return 0, nil, err
	}
	lst, err := redis.StringMap(rep[1], err)
	if err != nil {
		return 0, nil, err
	}
	return next, lst, nil
}

func test() {

	//onlinestate.Do("SET", "foo", "bar")

	// currentTimeStart := time.Now()
	// onlinestate.DoMulti(func(conn redis.Conn) error {

	// 	for i := 0; i < 100000; i++ {
	// 		conn.Do("INCR", "TESTKEY")
	// 	}
	// 	return err
	// })
	// fmt.Println(time.Since(currentTimeStart))

	// currentTimeStart = time.Now()
	// onlinestate.DoMulti(func(conn redis.Conn) error {

	// 	for i := 0; i < 100000; i++ {
	// 		conn.Send("INCR", "TESTKEY")
	// 	}
	// 	conn.Flush()
	// 	conn.Receive()

	// 	return err
	// })
	// fmt.Println(time.Since(currentTimeStart))

	// currentTimeStart = time.Now()
	// onlinestate.DoMulti(func(conn redis.Conn) error {

	// 	for i := 0; i < 100000; i++ {
	// 		conn.Send("INCR", "TESTKEY")
	// 		conn.Flush()
	// 	}
	// 	conn.Receive()

	// 	return err
	// })
	// fmt.Println(time.Since(currentTimeStart))
}
