package redis

import (
	"fmt"
	"github.com/FZambia/sentinel"
	"github.com/gomodule/redigo/redis"
	"github.com/spf13/viper"
	"strings"
	"time"
)

// sentinel适配器
type SentinelAdapter struct {
	pool *redis.Pool
}

// 返回sentinel适配器新实例
func NewSentinelAdapter(nodes []string, masterName string) (IRedisAdapter, error) {
	opts := []redis.DialOption{redis.DialConnectTimeout(5 * time.Second)}
	password := viper.GetString("redis.password")
	if len(strings.TrimSpace(password)) > 0 {
		opts = append(opts, redis.DialPassword(strings.TrimSpace(password)))
	}
	s := &sentinel.Sentinel{
		Addrs:      nodes,
		MasterName: masterName,
		Dial: func(addr string) (redis.Conn, error) {
			c, err := redis.Dial("tcp", addr, opts...)
			if err != nil {
				return nil, err
			}
			return c, nil
		},
	}
	maxIdle := viper.GetInt("redis.max_idle")
	maxActive := viper.GetInt("redis.max_active")
	idleTimeout := viper.GetDuration("redis.timeout")

	pool := &redis.Pool{
		MaxIdle:     maxIdle,
		MaxActive:   maxActive,
		IdleTimeout: idleTimeout,
		Dial: func() (redis.Conn, error) {
			addr, err := s.MasterAddr()
			if err != nil {
				return nil, err
			}
			if conn, err := redis.Dial("tcp", addr, opts...); nil != err {
				return nil, err
			} else {
				return conn, nil
			}
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if !sentinel.TestRole(c, "master") {
				return fmt.Errorf("redis sentinel role check failed")
			} else {
				return nil
			}
		},
	}
	return &SentinelAdapter{
		pool: pool,
	}, nil
}

// 关闭Redis连接
func (a *SentinelAdapter) Close() error {
	return a.pool.Close()
}

// 执行Redis命令
func (a *SentinelAdapter) Do(commandName string, args ...interface{}) (interface{}, error) {
	conn := a.pool.Get()
	defer func(conn redis.Conn) {
		_ = conn.Close()
	}(conn)

	if nil == conn {
		return nil, ErrRedisConnNil
	} else {
		return conn.Do(commandName, args...)
	}
}

// 返回命令管道操作对象
func (a *SentinelAdapter) Pipeline() IRedisPipeline {
	return &RedigoPipeline{ // sentinel库兼容redigo库,返回RedigoPipeline即可
		conn: a.pool.Get(),
	}
}