var _               = require('lodash'),
    nock            = require('nock'),
    should          = require('should'),
    sinon           = require('sinon'),
    rewire          = require('rewire'),
    Promise         = require('bluebird'),
    testUtils       = require('../utils'),
    url             = require('url'),

    // Stuff we test
    configUtils    = require('../utils/configUtils'),
    slack          = rewire('../../server/data/slack'),
    events         = require('../../server/events'),
    api            = require('../../server/api/settings'),
    utils          = require('../../server/utils'),
    schema         = require('../../server/data/schema').checks,

    sandbox        = sinon.sandbox.create(),
    // Test data
    slackObjNoUrl =
        {
            id: 17,
            uuid: '50f50671-6e7c-4636-85e0-2962967764fa',
            key: 'slack',
            value: '[{"url":""}]',
            type: 'blog',
            created_at: '2016-04-06T15:19:19.492Z',
            created_by: 1,
            updated_at: '2016-04-06T15:19:19.492Z',
            updated_by: 1
        },
    slackObjWithUrl =
        {
            id: 17,
            uuid: '50f50671-6e7c-4636-85e0-2962967764fa',
            key: 'slack',
            value: '[{"url":"https://hooks.slack.com/services/a-b-c-d"}]',
            type: 'blog',
            created_at: '2016-04-06T15:19:19.492Z',
            created_by: 1,
            updated_at: '2016-04-06T15:19:19.492Z',
            updated_by: 1
        };

// To stop jshint complaining
should.equal(true, true);

describe('Slack', function () {
    var eventStub;

    beforeEach(function () {
        eventStub = sandbox.stub(events, 'on');
    });

    afterEach(function () {
        sandbox.restore();
        configUtils.restore();
    });

    it('listen() should initialise event correctly', function () {
        slack.listen();
        eventStub.calledTwice.should.be.true();
        eventStub.firstCall.calledWith('post.published', slack.__get__('listener')).should.be.true();
        eventStub.secondCall.calledWith('slack.test', slack.__get__('testPing')).should.be.true();
    });

    it('listener() calls ping() with toJSONified model', function () {
        var testPost = _.clone(testUtils.DataGenerator.Content.posts[2]),
            testModel = {toJSON: function () {return testPost; }},
            pingStub = sandbox.stub(),
            resetSlack = slack.__set__('ping', pingStub),
            listener = slack.__get__('listener');

        listener(testModel);

        pingStub.calledOnce.should.be.true();
        pingStub.calledWith(testPost).should.be.true();

        // Reset slack ping method
        resetSlack();
    });

    it('testPing() calls ping() with default message', function () {
        var pingStub = sandbox.stub(),
            resetSlack = slack.__set__('ping', pingStub),
            testPing = slack.__get__('testPing');

        testPing();

        pingStub.calledOnce.should.be.true();
        pingStub.calledWith(sinon.match.has('message')).should.be.true();

        // Reset slack ping method
        resetSlack();
    });

    describe('makeRequest()', function () {
        var makeRequest = slack.__get__('makeRequest');

        it('should make request to slack correctly', function () {
            // set up
            var reqOptions,
                pingSlack;

            // fill the options for https request
            reqOptions = url.parse('https://hooks.slack.com/services/a-b-c-d');
            reqOptions.method = 'POST';
            reqOptions.headers = {'Content-type': 'application/json'};

            pingSlack = nock('https://hooks.slack.com/')
                .post('/services/a-b-c-d', {text:'http://myblog.com/mypost', icon_url: 'http://myblog.com/someImageurl.jpg', username: 'Ghost'})
                .reply(200);

            // execute code
            makeRequest(reqOptions, {text:'http://myblog.com/mypost', icon_url: 'http://myblog.com/someImageurl.jpg', username: 'Ghost'});

            // assertions
            pingSlack.isDone().should.be.true();
        });

        it('can handle an error response correctly', function () {
            // set up
            var reqOptions,
                pingSlack;

            // fill the options for https request
            reqOptions = url.parse('https://hooks.slack.com/services/a-b-c-d');
            reqOptions.method = 'POST';
            reqOptions.headers = {'Content-type': 'application/json'};

            pingSlack = nock('https://hooks.slack.com/')
                .post('/services/a-b-c-d', {text:'http://myblog.com/mypost', icon_url: 'http://myblog.com/someImageurl.jpg', username: 'Ghost'})
                .replyWithError(404);

            // execute code
            makeRequest(reqOptions, {text:'http://myblog.com/mypost', icon_url: 'http://myblog.com/someImageurl.jpg', username: 'Ghost'});

            // assertions
            pingSlack.isDone().should.be.true();
        });
    });

    describe('ping()', function () {
        var makeRequestAssertions,
            isPostStub,
            urlForSpy,
            settingsAPIStub,
            settingsObj,
            slackReset,
            makeRequestMock,
            makeRequestSpy,
            ping =  slack.__get__('ping');

        beforeEach(function () {
            isPostStub = sandbox.stub(schema, 'isPost');
            urlForSpy = sandbox.spy(utils.url, 'urlFor');
            settingsObj = {settings: [], meta: {}};
            settingsAPIStub = sandbox.stub(api, 'read').returns(Promise.resolve(settingsObj));

            makeRequestMock = function () {
                makeRequestAssertions.apply(this, arguments);
            };

            makeRequestSpy = sandbox.spy(makeRequestMock);
            slackReset = slack.__set__('makeRequest', makeRequestMock);

            configUtils.set('url', 'http://myblog.com');
        });

        afterEach(function () {
            slackReset();
        });

        it('makes a request for a post if url is provided', function (done) {
            isPostStub.returns(true);
            settingsObj.settings[0] = slackObjWithUrl;

            // assertions
            makeRequestAssertions = function (requestOptions, requestData) {
                isPostStub.calledOnce.should.be.true();
                urlForSpy.calledTwice.should.be.true();
                settingsAPIStub.calledOnce.should.be.true();
                requestOptions.should.have.property('href').and.be.equal('https://hooks.slack.com/services/a-b-c-d');
                requestData.should.have.property('text').and.be.equal('http://myblog.com/');
                requestData.should.have.property('icon_url').and.be.equal('http://myblog.com/ghost/img/ghosticon.jpg');
                requestData.should.have.property('username').and.be.equal('Ghost');
                done();
            };

            // execute code
            ping({}).catch(done);
        });

        it('makes a request for a message if url is provided', function (done) {
            isPostStub.returns(false);
            settingsObj.settings[0] = slackObjWithUrl;

            configUtils.set('url', 'https://myblog.com');

            // assertions
            makeRequestAssertions = function (requestOptions, requestData) {
                isPostStub.calledOnce.should.be.true();
                urlForSpy.calledOnce.should.be.true();
                settingsAPIStub.calledOnce.should.be.true();
                requestOptions.should.have.property('href').and.be.equal('https://hooks.slack.com/services/a-b-c-d');
                requestData.should.have.property('text').and.be.equal('Hi!');
                requestData.should.have.property('icon_url').and.be.equal('https://myblog.com/ghost/img/ghosticon.jpg');
                requestData.should.have.property('username').and.be.equal('Ghost');
                done();
            };

            ping({message: 'Hi!'}).catch(done);
        });

        it('does not make a request if post is a page', function (done) {
            // set up
            isPostStub.returns(true);
            settingsObj.settings[0] = slackObjWithUrl;

            // execute code
            ping({page: true}).then(function (result) {
                // assertions
                isPostStub.calledOnce.should.be.true();
                urlForSpy.calledOnce.should.be.true();
                settingsAPIStub.calledOnce.should.be.true();
                makeRequestSpy.called.should.be.false();
                should.not.exist(result);
                done();
            }).catch(done);
        });

        it('does not make a request if no url is provided', function (done) {
            // set up
            isPostStub.returns(true);
            settingsObj.settings[0] = slackObjNoUrl;

            // execute code
            ping({}).then(function (result) {
                // assertions
                isPostStub.calledOnce.should.be.true();
                urlForSpy.calledOnce.should.be.true();
                settingsAPIStub.calledOnce.should.be.true();
                makeRequestSpy.called.should.be.false();
                should.not.exist(result);
                done();
            }).catch(done);
        });

        it('does not send webhook for \'welcome-to-ghost\' post', function (done) {
            // set up
            isPostStub.returns(true);
            settingsObj.settings[0] = slackObjWithUrl;

            // execute code
            ping({slug: 'welcome-to-ghost'}).then(function (result) {
                // assertions
                isPostStub.calledOnce.should.be.true();
                urlForSpy.calledOnce.should.be.true();
                settingsAPIStub.calledOnce.should.be.true();
                makeRequestSpy.called.should.be.false();
                should.not.exist(result);
                done();
            }).catch(done);
        });

        it('handles broken slack settings', function (done) {
            settingsObj.settings[0] = '';

            ping({}).then(function () {
                done('This should not get called');
            }).catch(function () {
                isPostStub.calledOnce.should.be.true();
                urlForSpy.calledOnce.should.be.false();
                settingsAPIStub.calledOnce.should.be.true();
                makeRequestSpy.called.should.be.false();
                done();
            });
        });
    });
});