import * as Redis from 'redis'
const { promisify } = require("util");
let commands = require('redis-commands');

/**
 * Redis操作工具库
 * @date 2020-10-29 09:17:19
 * @author 吴锋霖
 * @doc https://github.com/NodeRedis/node-redis 
 *      https://www.runoob.com/redis/redis-tutorial.html
 */
class RedisClient {


    private client: Redis;
    private redisCommands = {};

    /**
     * 创建Redis客户端
     * @param host 127.0.0.1
     * @param port  6379
     * @param password 可选
     * @param database 可选 默认0
     */
    constructor(host: string, port: number, password: string = '', database: number = 0) {
        const options = {
            host: host,
            port: port,
            no_ready_check: true
        };
        this.client = Redis.createClient(options);
        if (password && password !== "") {
            this.client.auth(password, function () {
            });
        }
        if (database && database > 0) {
            this.client.select(database, function (err, data) {
                if (err) {
                    console.log("redis select db error: %s", err);
                }
            });
        }
        this.client.on("error", function (err) {
            console.log("redis[%s:%s/][db:%s][pwd:%s]连接失败: %s", host, port, database, password, err);
        });
        this.client.on("connect", function () {
            console.log("redis[%s:%s]连接成功!", host, port);
        });

        //当前版本Redis不支持promise，用 util.promisify 转换
        for (let command of commands.list) {
            this.redisCommands[command] = promisify(this.client[command]).bind(this.client);
        }
    }

    /**
     * SET
     * @param key key
     * @param value value
     * @param expires 过期时间(秒),可选参数，默认不过期
     */
    async set(key: string, value: any, expires: number = 0): Promise<any> {
        if (expires > 0) {
            return await this.redisCommands['set'](key, value, "EX", expires);
        } else {
            return await this.redisCommands['set'](key, value);
        }
    }

    /**
     * setNX 
     * @param key key
     * @param value value
     */
    async setNX(key: string, value: any, expires: number = 0): Promise<any> {
        if (expires > 0) {
            return await this.redisCommands['set'](key, value, "EX", expires, 'NX');
        } else {
            return await this.redisCommands['setnx'](key, value);
        }
    }

    /**
     * 获取
     * @param key key
     */
    async get(key: string): Promise<any> {
        return await this.redisCommands['get'](key);
    }


    /**
     * 删除
     * @param key key
     */
    async delete(key: string): Promise<boolean> {
        return await this.redisCommands['del'](key) === 1;
    }

    /**
     * 判断Key是否存在
     * @param key key
     */
    async exists(key: string): Promise<boolean> {
        return await this.redisCommands['exists'](key) === 1;
    }

    /**
     * 用于查找所有符合给定模式 pattern 的 key 
     * @param pattern
     */
    async keys(pattern: string): Promise<Array<string>> {
        return await this.redisCommands['keys'](pattern);
    }

    /**
     * 设置过期时间
     * @param key key
     * @param expires 过期时间(秒)
     */
    async expire(key: string, expires: number): Promise<boolean> {
        return await this.redisCommands['expire'](key, expires) === 1;
    }


    /**
     * 将一个或多个值插入到列表头部
     * @param key key
     * @param value 值
     */
    async lpush(key: string, value: any): Promise<number> {
        return await this.redisCommands['lpush'](key, value);
    }

    /**
     * 获取列表长度
     * @param key key
     */
    async llen(key: string): Promise<number> {
        return await this.redisCommands['llen'](key);
    }

    /**
     * 将一个值插入到已存在的列表头部
     * @param key key
     * @param value 值
     */
    async lpushx(key: string, value: any): Promise<number> {
        return await this.redisCommands['lpushx'](key, value);
    }

    /**
     * 获取列表指定范围内的元素
     * @param key key
     * @param start 开始索引
     * @param stop 截止索引
     */
    async lrange(key: string, start: number, stop: number): Promise<any> {
        return await this.redisCommands['lrange'](key, start, stop);
    }


    /**
     * 移出并获取列表的第一个元素
     * @param key key
     */
    async lpop(key: string): Promise<any> {
        return await this.redisCommands['lpop'](key);
    }


    /**
     * 根据参数 COUNT 的值，移除列表中与参数 VALUE 相等的元素。
     * @param key key
     * @param count  
     *  count > 0 : 从表头开始向表尾搜索，移除与 VALUE 相等的元素，数量为 COUNT 。
     *  count < 0 : 从表尾开始向表头搜索，移除与 VALUE 相等的元素，数量为 COUNT 的绝对值。
     *  count = 0 : 移除表中所有与 VALUE 相等的值。
     * @param value
     */
    async lrem(key: string, count: number, value: any): Promise<any> {
        return await this.redisCommands['lrem'](key, count, value);
    }


    /**
     * 向集合添加一个或多个成员
     * @param key
     * @param value 
     */
    async sadd(key: string, value: any): Promise<any> {
        return await this.redisCommands['sadd'](key, value);
    }

    /**
     * 获取集合的成员数
     * @param key 
     */
    async scard(key: string): Promise<any> {
        return await this.redisCommands['scard'](key);
    }

    /**
     * 返回第一个集合与其他集合之间的差异。
     * @param key1
     * @param key2 
     */
    async sdiff(key1: string, key2: string): Promise<any> {
        return await this.redisCommands['sdiff'](key1, key2);
    }

    /**
     * 判断 member 元素是否是集合 key 的成员
     * @param key
     * @param member 
     */
    async sismember(key: string, member: any): Promise<any> {
        return await this.redisCommands['sismember'](key, member);
    }

    /**
     * 返回集合中的所有成员
     * @param key
     */
    async smembers(key: string): Promise<any> {
        return await this.redisCommands['smembers'](key);
    }


    /**
     * 移除并返回集合中的一个随机元素
     * @param key 
     */
    async spop(key: string): Promise<any> {
        return await this.redisCommands['spop'](key);
    }


    /**
     * 返回集合中一个或多个随机数
     * @param key 
     * @param count 
     */
    async srandmember(key: string, count: number): Promise<any> {
        return await this.redisCommands['srandmember'](key, count);
    }


    /**
     * 移除集合中一个或多个成员
     * @param key 
     * @param member
     */
    async srem(key: string, member: any): Promise<any> {
        return await this.redisCommands['srem'](key, member);
    }

    /**
     * 返回所有给定集合的并集
     * @param key 
     */
    async sunion(key: any): Promise<any> {
        return await this.redisCommands['sunion'](key);
    }


    /**
     * 获取存储在哈希表中指定字段的值
     * @param key   
     * @param field 字段名称
     */
    async hget(key: any, field: any): Promise<any> {
        return await this.redisCommands['hget'](key, field);
    }


    /**
     * 获取在哈希表中指定 key 的所有字段和值
     * @param key   
     */
    async hgetall(key: any): Promise<any> {
        return await this.redisCommands['hgetall'](key);
    }


    /**
     * 为哈希表 key 中的指定字段的整数值加上增量 increment
     * @param key   
     * @param field
     * @param increment
     */
    async hincrby(key: any, field: any, increment: number): Promise<any> {
        return await this.redisCommands['hincrby'](key, field, increment);
    }

    /**
     * 将哈希表 key 中的字段 field 的值设为 value 。
     * @param key   
     * @param field
     * @param value
     */
    async hset(key: any, field: any, value: any): Promise<any> {
        return await this.redisCommands['hset'](key, field, value);
    }

    /**
     * 同时将多个 field-value (域-值)对设置到哈希表 key 中。
     * @param key   
     * @param ...args [field1,value1,field2,value2 ]
     */
    async hmset(key: any, ...args): Promise<any> {
        return await this.redisCommands['hmset'](key, args);
    }


    /**
     * 删除存储在哈希表中指定字段的值
     * @param key   
     * @param ...args [field1,value1,field2,value2 ]
     */
    async hdel(key: any, ...args): Promise<any> {
        return await this.redisCommands['hdel'](key, args);
    }



    /**
     * 执行某个Redis指令
     * @param command 指令名称,例： set
     * @param ...args 指令参数,例： [key value 'EX' expires ] 
     */
    async exec(command: string, ...args): Promise<any> {
        return await this.redisCommands[command](args);
    }



}

export { RedisClient }