import * as Redis from "ioredis";
import * as Bluebird from "bluebird"
import { EventEmitter } from "events";
import * as Index from "../index.d";

export default class RedisMock extends EventEmitter implements Index.ExtendedIORedis{
    public Promise: any;
    public status: string = "connecting";
    private internMemory: any = {};

    public constructor() {
        super();
        this.status = "ready";
    }

    public connect(callback?: () => void): Bluebird<any> { return new Bluebird<any>(function(){}); };
    public disconnect(): void {};
    public duplicate(): Redis.Redis { return new Redis();};

    //Extend from Commander
    public getBuiltinCommands(): string[] {return []}
    public createBuiltinCommand(commandName: string): {} {return {}}
    public defineCommand(name: string, definition: {
        numberOfKeys?: number;
        lua?: string;
    }): any {};
    public sendCommand(): void {};
    //--------------------

    public send_command(command: string, ...args: any[]): any {};

    public bitcount(key: string, start?: number | ((err: Error, res: number) => void), end?: number, callback?: (err: Error, res: number) => void): Bluebird<number> {return new Bluebird<number>(function(){})};

    public get(key: string, callback?: (err: Error, res: string) => void): Bluebird<string> { 

        if (this.status != "ready") {
            if(callback != undefined) callback(new Error("Dissconnected"), <any>null);
        } else {
            let value = this.internMemory[key];
            if (value != null) {
                if(callback != undefined) callback(<any>null, value); //Library send null in this case
            } else {
                if(callback != undefined) callback(new Error("Undefined value"), <any>null);
            }
        }

        return new Bluebird<string>(function(){}); 
    };

    public getBuffer(key: string, callback?: (err: Error, res: Buffer) => void): Bluebird<Buffer> { return new Bluebird<Buffer>(function(){}); };

    public set(key: string, value: any, ...args: any[]): any {
        //get last
        if(args != undefined && args.length > 0){
            let callback = args[args.length - 1];
            if(callback instanceof Function){
                if (this.status != "ready"){
                    callback(new Error("Dissconnected"), null);
                    return;
                };
                this.internMemory[key] = value;
                callback(null, null);
            }
        }
    };

    public setnx(key: string, value: any, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public setex(key: string, seconds: number, value: any, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public psetex(key: string, milliseconds: number, value: any, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public append(key: string, value: any, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public strlen(key: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public del(key: string, ...keys: any[]): any {
        this.internMemory = {};
    };

    public exists(...keys: string[]): any {};

    public setbit(key: string, offset: number, value: any, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public getbit(key: string, offset: number, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public setrange(key: string, offset: number, value: any, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public getrange(key: string, start: number, end: number, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public substr(key: string, start: number, end: number, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public incr(key: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public decr(key: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public mget(...keys: string[]): any {};

    public rpush(key: string, ...values: any[]): any {};

    public lpush(key: string, ...values: any[]): any {};

    public rpushx(key: string, value: any, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public lpushx(key: string, value: any, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public linsert(key: string, direction: "BEFORE" | "AFTER", pivot: string, value: any, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public rpop(key: string, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public lpop(key: string, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public brpop(...keys: string[]): any {};

    public blpop(...keys: string[]): any {};

    public brpoplpush(source: string, destination: string, timeout: number, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public llen(key: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public lindex(key: string, index: number, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public lset(key: string, index: number, value: any, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public lrange(key: string, start: number, stop: number, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public ltrim(key: string, start: number, stop: number, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public lrem(key: string, count: number, value: any, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public rpoplpush(source: string, destination: string, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public sadd(key: string, ...members: any[]): any {
        let callback = members[1];
        if(callback instanceof Function){
            if (this.status != "ready"){
                callback(false, null);
                return;
            };
            callback(null, null);
        }
    }

    public srem(key: string, ...members: any[]): any {};

    public smove(source: string, destination: string, member: string, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public sismember(key: string, member: string, callback?: (err: Error, res: 1 | 0) => void): Bluebird<1 | 0> { return new Bluebird<1 | 0>(function(){}); };

    public scard(key: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public spop(key: string, count?: number | ((err: any, res: any) => void), callback?: (err: Error, res: any) => void): Bluebird<any>{ 
        if(count instanceof Function){
            count(null, "1234"); 
        }
        return new Bluebird<any>(function(){}); 
    };

    public spop_keyset?(free_set:string, used_key:string, timestamp:any): Bluebird<any>{
        return new Bluebird<any>(function(resolve, reject){ resolve("1234");}); 
    }

    public srandmember(key: string, count?: number | ((err: Error, res: any) => void), callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public sinter(...keys: string[]): any {};

    public sinterstore(destination: string, ...keys: string[]): any {};

    public sunion(...keys: string[]): any {};

    public sunionstore(destination: string, ...keys: string[]): any {};

    public sdiff(...keys: string[]): any {};

    public sdiffstore(destination: string, ...keys: string[]): any {};

    public smembers(key: string, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public zadd(key: string, ...args: string[]): any {};

    public zincrby(key: string, increment: number, member: string, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public zrem(key: string, ...members: any[]): any {};

    public zremrangebyscore(key: string, min: number | string, max: number | string, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public zremrangebyrank(key: string, start: number, stop: number, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public zunionstore(destination: string, numkeys: number, key: string, ...args: string[]): any {};

    public zinterstore(destination: string, numkeys: number, key: string, ...args: string[]): any {};

    public zrange(key: string, start: number, stop: number, withScores?: "WITHSCORES" | ((err: Error, res: any) => void), callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public zrevrange(key: string, start: number, stop: number, withScores?: "WITHSCORES" | ((err: Error, res: any) => void), callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public zrangebyscore(key: string, min: number | string, max: number | string, ...args: string[]): any {};

    public zrevrangebyscore(key: string, max: number | string, min: number | string, ...args: string[]): any {};

    public zcount(key: string, min: number | string, max: number | string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public zcard(key: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public zscore(key: string, member: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public zrank(key: string, member: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public zrevrank(key: string, member: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public hset(key: string, field: string, value: any, callback?: (err: Error, res: 0 | 1) => void): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public hsetnx(key: string, field: string, value: any, callback?: (err: Error, res: 0 | 1) => void): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public hget(key: string, field: string, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public hmset(key: string, field: any, value?: any, ...args: string[]): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public hmget(key: string, ...fields: string[]): any {};

    public hincrby(key: string, field: string, increment: number, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public hincrbyfloat(key: string, field: string, increment: number, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public hdel(key: string, field?: string, ...fields: any[]): any {};

    public hlen(key: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public hkeys(key: string, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public hvals(key: string, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public hgetall(key: string, callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public hexists(key: string, field: string, callback?: (err: Error, res: 0 | 1) => void): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public incrby(key: string, increment: number, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public incrbyfloat(key: string, increment: number, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public decrby(key: string, decrement: number, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public getset(key: string, value: any, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public mset(key: string, value: any, ...args: string[]): any {};

    public msetnx(key: string, value: any, ...args: string[]): any {};

    public randomkey(callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public select(index: number, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public move(key: string, db: string, callback?: (err: Error, res: 0 | 1) => void): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public rename(key: string, newkey: string, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public renamenx(key: string, newkey: string, callback?: (err: Error, res: 0 | 1) => void): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public expire(key: string, seconds: number, callback?: (err: Error, res: 0 | 1) => void): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public pexpire(key: string, milliseconds: number, callback?: (err: Error, res: 0 | 1) => void): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public expireat(key: string, timestamp: number, callback?: (err: Error, res: 0 | 1) => void): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public pexpireat(key: string, millisecondsTimestamp: number, callback?: (err: Error, res: 0 | 1) => void): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public keys(pattern: string, callback?: (err: Error, res: string[]) => void): Bluebird<string[]> { return new Bluebird<string[]>(function(){}); };

    public dbsize(callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public auth(password: string, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public ping(message?: string | ((err: Error, res: string) => void), callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public echo(message: string, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public save(callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public bgsave(callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public bgrewriteaof(callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public shutdown(save: "SAVE" | "NOSAVE", callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public lastsave(callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public type(key: string, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public multi(commands?: string[][] | { pipeline: false }, options?: Redis.MultiOptions): Bluebird<string> & Redis.Pipeline {
        return <any>this; 
    };

    public exec(callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public discard(callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public sync(callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public flushdb(callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public flushall(callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public sort(key: string, ...args: string[]): any {};

    public info(section?: string | ((err: Error, res: any) => void), callback?: (err: Error, res: any) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public time(callback?: (err: Error, res: any) => void): Bluebird<any> { return new Bluebird<any>(function(){}); };

    public monitor(callback?: (err: Error, res: NodeJS.EventEmitter) => void): Bluebird<NodeJS.EventEmitter> { return new Bluebird<NodeJS.EventEmitter>(function(){}); };

    public ttl(key: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public persist(key: string, callback?: (err: Error, res: 0 | 1) => void): Bluebird<0 | 1> { return new Bluebird<0 | 1>(function(){}); };

    public slaveof(host: string, port: number, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public debug(...args: any[]): any {};

    public config(...args: any[]): any {};

    public subscribe(...channels: any[]): any {};

    public unsubscribe(...channels: string[]): any {};

    public psubscribe(...patterns: string[]): any {};

    public punsubscribe(...patterns: string[]): any {};

    public publish(channel: string, message: string, callback?: (err: Error, res: number) => void): Bluebird<number> { return new Bluebird<number>(function(){}); };

    public watch(...keys: string[]): any {};

    public unwatch(callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public cluster(...args: any[]): any {};

    public restore(...args: any[]): any {};

    public migrate(...args: any[]): any {};

    public dump(key: string, callback?: (err: Error, res: string) => void): Bluebird<string> { return new Bluebird<string>(function(){}); };

    public object(subcommand: string, ...args: any[]): any {};

    public client(...args: any[]): any {};

    public eval(...args: any[]): any {};

    public evalsha(...args: any[]): any {};

    public script(...args: any[]): any {};

    public quit(callback?: (err: Error, res: string) => void): Bluebird<string> {
        this.status = "end";
        return new Bluebird<string>(function(){});
    };

    public scan(cursor: number, ...args: any[]): any {};

    public hscan(key: string, cursor: number, ...args: any[]): any {};

    public zscan(key: string, cursor: number, ...args: any[]): any {};

    public pfmerge(destkey: string, ...sourcekeys: string[]): any {};

    public pfadd(key: string, ...elements: string[]): any {};

    public pfcount(...keys: string[]): any {};

    public pipeline(commands?: string[][]): Redis.Pipeline {return <any>{};};

    public scanStream(options?: Redis.ScanStreamOption): NodeJS.EventEmitter { return new EventEmitter();};
    public hscanStream(key: string, options?: Redis.ScanStreamOption): NodeJS.EventEmitter { return new EventEmitter();};
    public zscanStream(key: string, options?: Redis.ScanStreamOption): NodeJS.EventEmitter { return new EventEmitter();};
}