/* 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 () {

    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 mock_client = null;
        var redis;

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

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

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

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

        it('set', function () {
            var called = null, called2 = null, r1, test_key = 'a', test_val = 'b', mock_cb;
            runs(function () {
                mock_client.set = jasmine.createSpy();
                mock_client.set.andCallFake(function (key, val, cb) {
                    called = true;
                    cb(null, null);
                });
                //cp = new CredentialsProvider({'hosts': []});
                var cp = new CredentialsProvider({'max_connections': 2, 'hosts': [{'host': 'localhost', 'port': 1234}]});
                r1 = new Redis(cp);
                mock_cb = jasmine.createSpy();
                mock_cb.andCallFake(function () {
                    called2 = true;
                });
                r1.set(test_key, test_val, mock_cb);
            });

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

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mock_client.set).toHaveBeenCalled();
                expect(mock_client.set.mostRecentCall.args[0]).toEqual(test_key);
                expect(mock_client.set.mostRecentCall.args[1]).toEqual(test_val);
                expect(mock_cb).toHaveBeenCalledWith(null, null);
                r1.destroy();
            });
        });


        it('get', function () {
            var called = null, called2 = null, r1, test_key = 'a', test_val = 'b', mock_cb;
            runs(function () {
                mock_client.get = jasmine.createSpy();
                mock_client.get.andCallFake(function (key, cb) {
                    called = true;
                    cb(null, test_val);
                });
                var cp = new CredentialsProvider({'max_connections': 2, 'hosts': [{'host': 'localhost', 'port': 1234}]});
                r1 = new Redis(cp);
                mock_cb = jasmine.createSpy();
                mock_cb.andCallFake(function () {
                    called2 = true;
                });
                r1.get(test_key, mock_cb);
            });

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

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mock_client.get).toHaveBeenCalled();
                expect(mock_client.get.mostRecentCall.args[0]).toEqual(test_key);
                expect(mock_cb).toHaveBeenCalledWith(null, test_val);
                r1.destroy();
            });
        });

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

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

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mock_client.set).toHaveBeenCalled();
                expect(mock_client.set.mostRecentCall.args[0]).toEqual(test_key);
                expect(mock_client.set.mostRecentCall.args[1]).toEqual(test_val);
                expect(mock_cb).toHaveBeenCalledWith(null, null);
                r1.destroy();
            });
        });

        it('hmget', function () {
            var called = null, called2 = null, mock_cb, cp, r1, test_key = 'a', test_field = 'b', test_val = 'c';
            runs(function () {
                mock_client.hmget = jasmine.createSpy();
                mock_client.hmget.andCallFake(function (key, field, cb) {
                    called = true;
                    cb(null, test_val);
                });
                cp = new CredentialsProvider({'max_connections': 2, 'hosts': [{'host': 'localhost', 'port': 1234}]});
                r1 = new Redis(cp);
                mock_cb = jasmine.createSpy();
                mock_cb.andCallFake(function () {
                    called2 = true;
                });
                r1.hmget(test_key, test_field, mock_cb);
            });

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

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mock_client.hmget).toHaveBeenCalled();
                expect(mock_client.hmget.mostRecentCall.args[0]).toEqual(test_key);
                expect(mock_client.hmget.mostRecentCall.args[1]).toEqual(test_field);
                expect(mock_cb).toHaveBeenCalledWith(null, test_val);
                r1.destroy();
            });
        });

        it('hgetall', function () {
            var called = null, called2 = null, mock_cb, cp, r1, test_key = 'a', test_val = 'c';
            runs(function () {
                mock_client.hgetall = jasmine.createSpy();
                mock_client.hgetall.andCallFake(function (key, cb) {
                    called = true;
                    cb(null, test_val);
                });
                cp = new CredentialsProvider({'max_connections': 2, 'hosts': [{'host': 'localhost', 'port': 1234}]});
                r1 = new Redis(cp);
                mock_cb = jasmine.createSpy();
                mock_cb.andCallFake(function () {
                    called2 = true;
                });
                r1.hgetall(test_key, mock_cb);
            });

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

            runs(function () {
                expect(called).toBeTruthy();
                expect(called2).toBeTruthy();
                expect(mock_client.hgetall).toHaveBeenCalled();
                expect(mock_client.hgetall.mostRecentCall.args[0]).toEqual(test_key);
                expect(mock_cb).toHaveBeenCalledWith(null, test_val);
                r1.destroy();
            });
        });

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

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

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


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

        describe('when command error', function () {
            beforeEach(function () {
                var cp = new CredentialsProvider({'max_connections': 2, 'hosts': [{'host': 'localhost', 'port': 1234}]});
                this.redis = new Redis(cp);
                this.callback = jasmine.createSpy('set_callback');
                mock_client.set = jasmine.createSpy();
                mock_client.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');
                });
            });
        });
    });
});
