package storage

import (
	"errors"
	"fmt"
	"time"

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

type Redis struct {
	client redis.Conn
}

func NewRedis(addr, user, pass string) (*Redis, error) {
	opts := []redis.DialOption{}
	if pass != "" {
		opts = append(opts, redis.DialPassword(pass))
	}
	opts = append(opts, redis.DialConnectTimeout(time.Second*10))
	client, err := redis.Dial("tcp", addr, opts...)
	return &Redis{
		client: client,
	}, err
}

func (m *Redis) String() string {
	return fmt.Sprintf("%v", *m)
}

func (m *Redis) Close() error {
	return m.client.Close()
}

func (m *Redis) List(ns []string, limits ...string) ([]string, error) {
	limits = resolveLimits(limits)
	limits[0] = resolveNamespace(append(ns, limits[0]))
	limits[1] = resolveNamespace(append(ns, limits[1]))

	resp, err := m.client.Do("KEYS", "*")
	if err != nil {
		return nil, err
	}

	keys := []string{}
	if results, ok := resp.([]interface{}); !ok {
		return nil, ErrNotFound
	} else {
		for i := range results {
			_, ok := results[i].([]uint8)
			if !ok {
				return nil, errors.New("not a []byte key")
			}
			k := fmt.Sprintf("%s", results[i])
			if k < limits[0] {
				continue
			}
			if k > limits[1] {
				break
			}
			keys = append(keys, k)
		}
	}
	return keys, nil
}

func (m *Redis) Get(key string, ns ...string) ([]byte, error) {
	key = resolveNamespace(append(ns, key))
	resp, err := m.client.Do("GET", key)
	if err != nil {
		return nil, err
	}
	if resp == nil {
		return nil, ErrNotFound
	}
	b, ok := resp.([]byte)
	if !ok {
		return nil, errors.New("resp not a []byte")
	}
	return b, nil
}

func (m *Redis) Set(key string, value []byte, ns ...string) error {
	namespace := resolveNamespace(append(ns, key))
	_, err := m.client.Do("SET", namespace, value)
	return err
}
