import json
import redis as rd
from func_timeout import func_set_timeout
from utils.common import singleton, md5


REDIS = {
    # 'host': '127.0.0.1',
    'host': '120.79.147.190',
    'port': 6379,
    'password': 'fG7#vT6kQ1pX',
    'db': 10
}


@singleton
class Redis(object):
    def __init__(self):
        self.host = REDIS['host']
        self.port = REDIS['port']
        self.db = REDIS['db']
        self.password = REDIS['password']

    def get_instance(self, db=10):
        self.pool = rd.ConnectionPool(
            host=self.host,
            port=self.port,
            db=db,
            password=self.password,
            max_connections=3,
            socket_timeout=5,
            socket_connect_timeout=5,
            retry_on_timeout=True,
        )
        return rd.Redis(connection_pool=self.pool)


@func_set_timeout(10)
def set_string(key, value):
    """set string

    :param key: key
    :param value: value
    :return:
    """

    if type(value) is dict:
        value = json.dumps(value)
    r = Redis().get_instance()
    r.set(key, value)


@func_set_timeout(10)
def get(key):
    """get string

    :param key: key
    :return:
    """

    r = Redis().get_instance()
    value = r.get(key)
    r.close()
    if value:
        return json.loads(value)


@func_set_timeout(10)
def delete(key):
    """del a key

    :param key: key
    :return: True for success.
    """

    r = Redis().get_instance()
    r.delete(key)
    r.close()


@func_set_timeout(10)
def exists(key):
    """check if a key exists.

    :param key: key
    :return: True for exists, False for not.
    """

    r = Redis().get_instance()
    d = r.exists(key)
    r.close()
    return d


@func_set_timeout(10)
def expire(key, time):
    """set expire time for a key.

    :param key: key
    :param time: expire time
    :return: True for success.
    """

    r = Redis().get_instance()
    d = r.expire(key, time)
    r.close()
    return d


@func_set_timeout(10)
def smembers(key):
    """smembers

    :param key: key
    :return:
    """

    r = Redis().get_instance()
    d = r.smembers(key)
    r.close()
    return d


@func_set_timeout(10)
def sadd(key, value, use_md5=True):
    """add key-value to the sorted set.

    :param key: key
    :param value: value
    :param use_md5: weather use md5.
    :return: True for done, False for not.
    """

    r = Redis().get_instance()
    if use_md5:
        added = r.sadd(key, md5(value, digits=16))
    else:
        added = r.sadd(key, value)
    r.close()
    return added == 1


@func_set_timeout(10)
def spop(key, count=20) -> list:
    """spop

    :param count:
    :param key:
    :return:
    """

    r = Redis().get_instance()
    d = r.spop(key, count)
    r.close()
    return d


@func_set_timeout(10)
def sismember(key, value, md5=True):
    """check weather a key-value exists in sorted set.

    :param key: key
    :param value: value
    :return: True for exists, False for not.
    """

    r = Redis().get_instance()
    member = md5(value) if md5 else value
    res = r.sismember(key, member)
    r.close()
    return res


@func_set_timeout(10)
def srem(key, value, md5=True):
    """remove a key-value from set.

    :param key: key
    :param value: value
    :return: boolean
    """

    r = Redis().get_instance()
    member = md5(value, digits=16) if md5 else value
    res = r.srem(key, member)
    r.close()
    return res


@func_set_timeout(10)
def hget(name, key):
    """hash get

    :param name: hash name
    :param key: key
    :return: boolean
    """

    r = Redis().get_instance()
    res = r.hget(name, key)
    r.close()
    if res:
        d = res.decode()
        return d


@func_set_timeout(10)
def hdel(name, key):
    """hash del

    :param name: hash name
    :param key: key
    :return: boolean
    """

    r = Redis().get_instance()
    d = r.hdel(name, key)
    r.close()
    return d


@func_set_timeout(10)
def hexists(name, key):
    """hash exist.

    :param name: hash name
    :param key: key
    :return: boolean
    """

    r = Redis().get_instance()
    d = r.hexists(name, key)
    r.close()
    return d


@func_set_timeout(10)
def hset(name, key, value):
    """hash set

    :param name: hash name
    :param key: key
    :param value: value
    :return: boolean
    """

    r = Redis().get_instance()
    return r.hset(name, key, value)


@func_set_timeout(10)
def zadd(key, value):
    """add key-value to the sorted set.

    :param key: key
    :param value: value
    :param use_md5: weather use md5.
    :return: True for done, False for not.
    """
    # value dict {value: score}
    r = Redis().get_instance()
    added = r.zadd(key, value)
    r.close()
    return added == 1


@func_set_timeout(10)
def zpop(key, count):
    # value dict {value: score}
    r = Redis().get_instance()
    pipe = r.pipeline(transaction=False)
    pipe.multi()
    pipe.zrange(key, 0, count-1).zremrangebyrank(key, 0, count-1)
    results, count = pipe.execute()
    r.close()
    return results


@func_set_timeout(10)
def hincrby(key, field, count):
    """
    :param key: key
    :param key: field
    :param key: count
    :return: count.
    """

    r = Redis().get_instance()
    added = r.hincrby(key, field, count)
    r.close()
    return added


@func_set_timeout(10)
def hgetall(key):
    r = Redis().get_instance()
    added = r.hgetall(key)
    r.close()
    return added


def srandmembers(key, count):
    """SRANDMEMBER

    :param key: key
    :return:
    """
    r = Redis().get_instance()
    return r.srandmember(key, count)


def lpop(key) -> list:
    """lpop
    :param key:
    :return:
    """

    r = Redis().get_instance()
    return r.lpop(key)


@func_set_timeout(10)
def lpush(key, value):
    """add key-value to the sorted set.

    :param key: key
    :param value: value
    :return: True for done, False for not.
    """
    # value dict {value: score}
    r = Redis().get_instance()
    added = r.lpush(key, value)
    r.close()
    return added == 1


@func_set_timeout(30)
def xadd(key, data):
    r = Redis().get_instance()
    added = r.xadd(key, data)
    r.close()
    return added == 1


@func_set_timeout(30)
def xadd_db0(key, data):
    r = Redis().get_instance(0)
    added = r.xadd(key, data)
    r.close()
    return added == 1


@func_set_timeout(10)
def hexists(name, key):
    """hash exist.

    :param name: hash name
    :param key: key
    :return: boolean
    """

    r = Redis().get_instance()
    d = r.hexists(name, key)
    r.close()
    return d


def hset(name, key, value):
    """hash set

    :param name: hash name
    :param key: key
    :param value: value
    :return: boolean
    """

    r = Redis().get_instance()
    return r.hset(name, key, value)


@func_set_timeout(10)
def zadd(key, value):
    """add key-value to the sorted set.

    :param key: key
    :param value: value
    :param use_md5: weather use md5.
    :return: True for done, False for not.
    """
    # value dict {value: score}
    r = Redis().get_instance()
    added = r.zadd(key, value)
    r.close()
    return added == 1


@func_set_timeout(10)
def zpop(key, count):
    # value dict {value: score}
    r = Redis().get_instance()
    pipe = r.pipeline(transaction=False)
    pipe.multi()
    pipe.zrange(key, 0, count-1).zremrangebyrank(key, 0, count-1)
    results, count = pipe.execute()
    r.close()
    return results


@func_set_timeout(10)
def hincrby(key, field, count):
    """
    :param key: key
    :param key: field
    :param key: count
    :return: count.
    """

    r = Redis().get_instance()
    added = r.hincrby(key, field, count)
    r.close()
    return added


@func_set_timeout(10)
def hgetall(key):
    r = Redis().get_instance()
    added = r.hgetall(key)
    r.close()
    return added