import { createClient, RedisClient, RetryStrategyOptions } from "redis";
import { ErrCode } from "./ErrorCode";

import { CodeMsg } from './Common';
import { CodeMsgAny } from "./CodeMsg";
import { RedisCfg } from './Common';

/**
 * 
 * @date 2023-02-16 15:30:42
 * @author 鲜伟坚
 */
class Redis {

    private _isEnabled: boolean;
    private _client: RedisClient;


    connect(cfg: RedisCfg) {
        return new Promise(resolve => {

            const options = {
                host: cfg.host,
                port: cfg.port,
                db: cfg.database,
                no_ready_check: true,
                socket_keepalive: true,
                retry_strategy: function (options: RetryStrategyOptions) { return 1000; }
            };

            let client = createClient(options);
            this._client = client;
            client.on('ready', () => {
                this._isEnabled = true;
                resolve({ code: ErrCode.OK })
            });
            client.on("error", err => {
                this._isEnabled = false;
                resolve({ code: ErrCode.RedisErr, err: err })
            });
            client.on('reconnecting', () => {
                this._isEnabled = false;
                console.info("[redis] reconnecting...");
            });
            client.on('end', () => {
                this._isEnabled = false;
                console.info("[redis] end...");
            });
            client.on("connect", () => {
                this._isEnabled = false;
                console.info("[redis] connect...");
            });
        })
    }

    SELECT(db: number): Promise<CodeMsg<string>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.select(db, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    GET(key: string): Promise<CodeMsg<string>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.GET(key, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    SET(key: string, value: string): Promise<CodeMsg<string>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.SET(key, value, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    SETEX(key: string, value: string, expireSeconds: number): Promise<CodeMsg<string>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.SETEX(key, expireSeconds, value, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    SETNX(key: string, value: string): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.SETNX(key, value, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    DEL(key: string): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.DEL(key, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    INCR(key: string): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.INCR(key, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    HINCRBY(key: string, field: string, increment: number): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.HINCRBY(key, field, increment, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    INCRBy(key: string, increment: number): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.INCRBY(key, increment, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    DECR(key: string): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.DECR(key, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    DECRBY(key: string, decrement: number): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.DECRBY(key, decrement, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    RENAME(key: string, newkey: string): Promise<CodeMsg<string>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.RENAME(key, newkey, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    RENAMENX(key: string, newkey: string): Promise<CodeMsg<string>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.RENAME(key, newkey, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    STRLEN(key: string): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.STRLEN(key, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    HSET(key: string, field: string, value: string): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.HSET(key, field, value, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }


    HGET(key: string, field: string): Promise<CodeMsg<string>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.HGET(key, field, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }


    HMSET(key: string, ...args: any[]): Promise<CodeMsgAny> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.HMSET(key, ...args, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    HGETALL(key: string): Promise<CodeMsgAny> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.hgetall(key, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }


    MULTI(...args: any[]): Promise<CodeMsgAny> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.MULTI([...args]
            ).EXEC((err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    KEYS(pattern: string): Promise<CodeMsg<string[]>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.KEYS(pattern, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    LPUSH(key: string, value: string): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.LPUSH(key, value, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    RPUSH(key: string, value: string): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.RPUSH(key, value, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    RPOP(key: string): Promise<CodeMsg<string>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.RPOP(key, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    LLEN(key: string): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.LLEN(key, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    LRANGE(key: string, start: number, stop: number): Promise<CodeMsg<string[]>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.LRANGE(key, start, stop, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    HDEL(key: string, ...args: any[]): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.HDEL(key, ...args, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

    LREM(key: string, value: string, count: number = 0, ): Promise<CodeMsg<number>> {
        return new Promise(resolve => {
            if (!this._isEnabled) {
                resolve({ code: ErrCode.RedisErr, err: "redis not enabled" });
                return;
            }
            this._client.LREM(key, count, value, (err, reply) => {
                if (err) {
                    resolve({ code: ErrCode.RedisErr, err: err });
                    return;
                }
                resolve({ code: ErrCode.OK, msg: reply });
            })
        })
    }

}

const defRedisClient = new Redis();
export { defRedisClient };