/* global it, expect, runs, jasmine, waitsFor, describe, spyOn, beforeEach, afterEach */
var rewire = require('rewire');
//var redis = require('redis');
var CredentialsProvider = require('../../lib/credentials/CredentialsProvider.js').CredentialsProvider;
var modulePath = './../../lib/client/Redis.js';

describe('Redis', function () {
      beforeEach(function() {
        jasmine.Clock.useMock();
      });

    describe('should be truthy', function () {
        it('should require', function () {
            var Redis = require(modulePath).Redis;
            expect(Redis).toBeTruthy();
        });

        it('should not throw', function () {
            expect(function () {
                require(modulePath);
            }).not.toThrow();
        });

    });

    describe('methods', function () {
        var RedisMock = null;
        var Redis = null;
        var mockClient = null;
        var redis;

        beforeEach(function () {
            RedisMock = rewire(modulePath);
            /* module */
            Redis = RedisMock.Redis;
            Redis.prototype._heartbeat = function(){};
            /* client */
            redis = RedisMock.__get__('redis');

            //console.log('r', redis);
            mockClient = jasmine.createSpy();
            mockClient.on = jasmine.createSpy();
            mockClient.on.andCallFake(function (ev, cb) {
                if (ev === 'ready') {
                    cb();
                }
            });
            spyOn(redis, 'createClient');
            redis.createClient.andReturn(mockClient);
        });

        it('multiton', function () {
            var cp = new CredentialsProvider({
                'hosts': []
            });
            var r1 = new Redis(cp);
            var r2 = new Redis(cp);
            expect(r1).toEqual(r2);
            r1.destroy();
            r2.destroy();
        });

        it('num connections', function () {
            var cp = new CredentialsProvider({
                'connections': 2,
                'hosts': [{
                    'host': 'localhost',
                    'port': 1234
                }]
            });
            var r1 = new Redis(cp);
            expect(redis.createClient).toHaveBeenCalled();
            expect(mockClient.on).toHaveBeenCalled();
            r1.destroy();
        });

        it('set', function () {
            var called = null,
                called2 = null,
                r1, testKey = 'a',
                testVal = 'b',
                mockCb;
            runs(function () {
                mockClient.set = jasmine.createSpy();
                mockClient.set.andCallFake(function (key, val, cb) {
                    called = true;
                    cb(null, null);
                });

                var cp = new CredentialsProvider({
                    'connections': 2,
                    'hosts': [{
                        'host': 'localhost',
                        'port': 1234
                    }]
                });
                r1 = new Redis(cp);
                mockCb = jasmine.createSpy();
                mockCb.andCallFake(function () {
                    called2 = true;
                });
                r1.set(testKey, testVal, mockCb);

            });

            waitsFor(function () {
                return (called !== null && called2 !== null);
            });

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mockClient.set).toHaveBeenCalled();
                expect(mockClient.set.mostRecentCall.args[0]).toEqual(testKey);
                expect(mockClient.set.mostRecentCall.args[1]).toEqual(testVal);
                expect(mockCb).toHaveBeenCalledWith(null, null);
                r1.destroy();
            });
        });


        it('get', function () {
            var called = null,
                called2 = null,
                r1, testKey = 'a',
                testVal = 'b',
                mockCb;
            runs(function () {
                mockClient.get = jasmine.createSpy();
                mockClient.get.andCallFake(function (key, cb) {
                    called = true;
                    cb(null, testVal);
                });
                var cp = new CredentialsProvider({
                    'connections': 2,
                    'hosts': [{
                        'host': 'localhost',
                        'port': 1234
                    }]
                });
                r1 = new Redis(cp);
                mockCb = jasmine.createSpy();
                mockCb.andCallFake(function () {
                    called2 = true;
                });
                r1.get(testKey, mockCb);
            });

            waitsFor(function () {
                return (called !== null && called2 !== null);
            });

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mockClient.get).toHaveBeenCalled();
                expect(mockClient.get.mostRecentCall.args[0]).toEqual(testKey);
                expect(mockCb).toHaveBeenCalledWith(null, testVal);
                r1.destroy();
            });
        });

        it('set', function () {
            var called = null,
                called2 = null,
                mockCb, cp, r1, testKey = 'a',
                testVal = 'b';
            runs(function () {
                mockClient.set = jasmine.createSpy();
                mockClient.set.andCallFake(function (key, value, cb) {
                    called = true;
                    cb(null, null);
                });
                cp = new CredentialsProvider({
                    'connections': 2,
                    'hosts': [{
                        'host': 'localhost',
                        'port': 1234
                    }]
                });
                r1 = new Redis(cp);
                mockCb = jasmine.createSpy();
                mockCb.andCallFake(function () {
                    called2 = true;
                });
                r1.set(testKey, testVal, mockCb);
            });

            waitsFor(function () {
                return (called !== null && called2 !== null);
            });

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mockClient.set).toHaveBeenCalled();
                expect(mockClient.set.mostRecentCall.args[0]).toEqual(testKey);
                expect(mockClient.set.mostRecentCall.args[1]).toEqual(testVal);
                expect(mockCb).toHaveBeenCalledWith(null, null);
                r1.destroy();
            });
        });

        it('hmget', function () {
            var called = null,
                called2 = null,
                mockCb, cp, r1, testKey = 'a',
                testField = 'b',
                testVal = 'c';
            runs(function () {
                mockClient.hmget = jasmine.createSpy();
                mockClient.hmget.andCallFake(function (key, field, cb) {
                    called = true;
                    cb(null, testVal);
                });
                cp = new CredentialsProvider({
                    'connections': 2,
                    'hosts': [{
                        'host': 'localhost',
                        'port': 1234
                    }]
                });
                r1 = new Redis(cp);
                mockCb = jasmine.createSpy();
                mockCb.andCallFake(function () {
                    called2 = true;
                });
                r1.hmget(testKey, testField, mockCb);
            });

            waitsFor(function () {
                return (called !== null && called2 !== null);
            });

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mockClient.hmget).toHaveBeenCalled();
                expect(mockClient.hmget.mostRecentCall.args[0]).toEqual(testKey);
                expect(mockClient.hmget.mostRecentCall.args[1]).toEqual(testField);
                expect(mockCb).toHaveBeenCalledWith(null, testVal);
                r1.destroy();
            });
        });

        it('hgetall', function () {
            var called = null,
                called2 = null,
                mockCb, cp, r1, testKey = 'a',
                testVal = 'c';
            runs(function () {
                mockClient.hgetall = jasmine.createSpy();
                mockClient.hgetall.andCallFake(function (key, cb) {
                    called = true;
                    cb(null, testVal);
                });
                cp = new CredentialsProvider({
                    'connections': 2,
                    'hosts': [{
                        'host': 'localhost',
                        'port': 1234
                    }]
                });
                r1 = new Redis(cp);
                mockCb = jasmine.createSpy();
                mockCb.andCallFake(function () {
                    called2 = true;
                });
                r1.hgetall(testKey, mockCb);
            });

            waitsFor(function () {
                return (called !== null && called2 !== null);
            });

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mockClient.hgetall).toHaveBeenCalled();
                expect(mockClient.hgetall.mostRecentCall.args[0]).toEqual(testKey);
                expect(mockCb).toHaveBeenCalledWith(null, testVal);
                r1.destroy();
            });
        });

        it('del', function () {
            var called = null,
                called2 = null,
                testKey = 'a',
                cp, r1, mockCb;

            runs(function () {
                mockClient.del = jasmine.createSpy();
                mockClient.del.andCallFake(function (key, cb) {
                    called = true;
                    cb(null, null);
                });
                cp = new CredentialsProvider({
                    'connections': 2,
                    'hosts': [{
                        'host': 'localhost',
                        'port': 1234
                    }]
                });
                r1 = new Redis(cp);
                mockCb = jasmine.createSpy();
                mockCb.andCallFake(function () {
                    called2 = true;
                });
                r1.del(testKey, mockCb);
            });

            waitsFor(function () {
                return (called !== null && called2 !== null);
            });


            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mockClient.del).toHaveBeenCalled();
                expect(mockClient.del.mostRecentCall.args[0]).toEqual(testKey);
                expect(mockCb).toHaveBeenCalledWith(null, null);
                r1.destroy();
            });
        });

        it('mget', function () {
            var called = null,
                called2 = null,
                mockCb, cp, r1;

            runs(function () {
                mockClient.mget = jasmine.createSpy();
                mockClient.mget.andCallFake(function () {
                    var result = [];
                    var cb = arguments[arguments.length - 1];

                    for (var i = 0, l = arguments.length - 1; i < l; i++) {
                        result.push('result-' + arguments[i]);
                    }

                    called = true;
                    cb(null, result);
                });

                cp = new CredentialsProvider({
                    'connections': 2,
                    'hosts': [{
                        'host': 'localhost',
                        'port': 1234
                    }, {
                        'host': 'localhost',
                        'port': 1235
                    }]
                });

                r1 = new Redis(cp);

                mockCb = jasmine.createSpy();
                mockCb.andCallFake(function () {
                    called2 = true;
                });

                r1.mget('k1', 'k2', 'k3', 'k4', mockCb);
            });

            waitsFor(function () {
                return (called !== null && called2 !== null);
            });

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mockClient.mget).toHaveBeenCalled();
                expect(mockCb).toHaveBeenCalledWith(null, ['result-k1', 'result-k2', 'result-k3', 'result-k4']);
                r1.destroy();
            });
        });

        describe('when command error', function () {
            beforeEach(function () {
                var cp = new CredentialsProvider({
                    'connections': 2,
                    'hosts': [{
                        'host': 'localhost',
                        'port': 1234
                    }]
                });
                this.redis = new Redis(cp);
                this.callback = jasmine.createSpy('set_callback');
                mockClient.set = jasmine.createSpy();
                mockClient.set.andCallFake(function (key, data, callback) {
                    callback('error', null);
                });
            });

            afterEach(function () {
                this.redis.destroy();
            });

            describe('callback', function () {
                beforeEach(function () {
                    this.redis.set('key', 'data', this.callback);
                });

                it('should be called', function () {
                    expect(this.callback).toHaveBeenCalled();
                });

                it('should be called with error', function () {
                    expect(this.callback).toHaveBeenCalledWith('error', null);
                });
            });

            describe('console.warn', function () {
                beforeEach(function () {
                    this.consoleWarnSpy = jasmine.createSpy('consoleWarnSpy');
                    var consoleMock = Object.create(RedisMock.__get__('console'));
                    consoleMock.warn = this.consoleWarnSpy;
                    RedisMock.__set__({
                        console: consoleMock
                    });

                    this.redis.set('key', 'data', this.callback);
                });

                it('should be called', function () {
                    expect(this.consoleWarnSpy).toHaveBeenCalled();
                });

                it('should be called with params', function () {
                    expect(this.consoleWarnSpy).toHaveBeenCalledWith(
                        'Redis/Error cmd=%s error="%s"', 'set', 'error');
                });
            });
        });
    });
});
