package redis

import (
	"fmt"
	"time"

	"github.com/fiskerinc/cloud-services/pkg/logger"
	"github.com/fiskerinc/cloud-services/pkg/utils/envtool"

	"github.com/gomodule/redigo/redis"
	"github.com/pkg/errors"
	redigotrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/gomodule/redigo"
)

// connection vars
var (
	host     = envtool.GetEnv("REDIS_HOST", "localhost")
	port     = envtool.GetEnv("REDIS_PORT", "6379")
	password = envtool.GetEnv("REDIS_PASSWORD", "")
	addr     = fmt.Sprintf("%v:%v", host, port)
)

func UpdateRedisConnection(redisHost string, redisPort string, redisPassword string) {
	host = redisHost
	port = redisPort
	password = redisPassword
	addr = fmt.Sprintf("%v:%v", host, port)

}

// Pool provides redis connections
type Pool interface {
	Get() redis.Conn
	Stats() redis.PoolStats
	ActiveCount() int
}

func NewPool() Pool {
	return &redis.Pool{
		IdleTimeout:     time.Millisecond * time.Duration(envtool.GetEnvInt("REDIS_IDLETIMEOUT_MS", 1000)),
		MaxIdle:         envtool.GetEnvInt("REDIS_MAXIDLECONN", 10),
		MaxActive:       envtool.GetEnvInt("REDIS_MAXACTIVECONN", 10),
		MaxConnLifetime: time.Millisecond * time.Duration(envtool.GetEnvInt("REDIS_MAXCONNLIFETIME_MS", 1000)),
		Wait:            (envtool.GetEnvInt("REDIS_WAITGETCONN", 0) == 1),
		Dial: func() (redis.Conn, error) {
			conn, err := redigotrace.Dial("tcp", addr, redis.DialKeepAlive(time.Minute*time.Duration(envtool.GetEnvInt("REDIS_KEEPALIVE_MINS", 10))))
			if password == "" {
				return conn, errors.WithStack(err)
			}
			if err != nil {
				logger.Error().Err(errors.WithStack(err)).Send()
				return nil, err
			}
			if _, err := conn.Do("AUTH", password); err != nil {
				conn.Close()
				logger.Error().Err(errors.WithStack(err)).Send()
				return nil, err
			}
			return conn, nil
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}
}

type Conn interface {
	Do(string, ...interface{}) (interface{}, error)
	DoWithTimeout(time.Duration, string, ...interface{}) (interface{}, error)

	Receive() (interface{}, error)
	ReceiveWithTimeout(time.Duration) (interface{}, error)

	Send(string, ...interface{}) error
	Err() error
	Close() error
	Flush() error
}
