/*globals describe, it, beforeEach, afterEach */

var should         = require('should'),
    sinon          = require('sinon'),
    when           = require('when'),
    path           = require('path'),
    fs             = require('fs'),
    _              = require('underscore'),
    rewire         = require("rewire"),

    testUtils      = require('../utils'),

    // Thing we are testing
    defaultConfig  = require('../../../config.example')[process.env.NODE_ENV],
    loader         = rewire('../../server/config/loader'),
    theme          = rewire('../../server/config/theme'),
    paths          = rewire('../../server/config/paths');

describe('Config', function () {

    describe('Loader', function () {
        var sandbox,
            rejectMessage = loader.__get__('rejectMessage'),
            overrideConfig = function (newConfig) {
                loader.__set__("readConfigFile",  sandbox.stub().returns(
                    _.extend({}, defaultConfig, newConfig)
                ));
            };



        beforeEach(function () {
            sandbox = sinon.sandbox.create();
        });

        afterEach(function () {
            loader         = rewire('../../server/config/loader');
            sandbox.restore();

        });

        it('loads the config file if one exists', function (done) {
            // the test infrastructure is setup so that there is always config present,
            // but we want to overwrite the test to actually load config.example.js, so that any local changes
            // don't break the tests
            loader.__set__("configFile",  path.join(paths().appRoot, 'config.example.js'));

            loader().then(function (config) {
                config.url.should.equal(defaultConfig.url);
                config.database.client.should.equal(defaultConfig.database.client);
                config.database.connection.should.eql(defaultConfig.database.connection);
                config.server.host.should.equal(defaultConfig.server.host);
                config.server.port.should.equal(defaultConfig.server.port);

                done();
            }).then(null, done);
        });

        it('creates the config file if one does not exist', function (done) {

            var deferred = when.defer(),
                // trick loader into thinking that the config file doesn't exist yet
                existsStub  = sandbox.stub(fs, 'exists', function (file, cb) { return cb(false); }),
                // create a method which will return a pre-resolved promise
                resolvedPromise = sandbox.stub().returns(deferred.promise);

            deferred.resolve();

            // ensure that the file creation is a stub, the tests shouldn't really create a file
            loader.__set__("writeConfigFile",  resolvedPromise);
            loader.__set__("validateConfigEnvironment",  resolvedPromise);

            loader().then(function () {
                existsStub.calledOnce.should.be.true;
                resolvedPromise.calledTwice.should.be.true;
                done();
            }).then(null, done);
        });

        it('accepts valid urls', function (done) {
            // replace the config file with invalid data
            overrideConfig({url: 'http://testurl.com'});

            loader().then(function (localConfig) {
                localConfig.url.should.equal('http://testurl.com');

                // Next test
                overrideConfig({url: 'https://testurl.com'});
                return loader();
            }).then(function (localConfig) {
                localConfig.url.should.equal('https://testurl.com');

                 // Next test
                overrideConfig({url: 'http://testurl.com/blog/'});
                return loader();
            }).then(function (localConfig) {
                localConfig.url.should.equal('http://testurl.com/blog/');

                 // Next test
                overrideConfig({url: 'http://testurl.com/ghostly/'});
                return loader();
            }).then(function (localConfig) {
                localConfig.url.should.equal('http://testurl.com/ghostly/');

                // Next test
                overrideConfig({url: '//testurl.com'});
                return loader();
            }).then(function (localConfig) {
                localConfig.url.should.equal('//testurl.com');

                done();
            }).then(null, done);
        });

        it('rejects invalid urls', function (done) {
            // replace the config file with invalid data
            overrideConfig({url: 'notvalid'});

            loader().otherwise(function (error) {
                error.should.include(rejectMessage);

                // Next test
                overrideConfig({url: 'something.com'});
                return loader();
            }).otherwise(function (error) {
                error.should.include(rejectMessage);

                done();
            }).then(function () {
                should.fail('no error was thrown when it should have been');
                done();
            }).then(done, null);
        });

        it('does not permit subdirectories named ghost', function (done) {
            // replace the config file with invalid data
            overrideConfig({url: 'http://testurl.com/ghost/'});

            loader().otherwise(function (error) {
                error.should.include(rejectMessage);

                 // Next test
                overrideConfig({url: 'http://testurl.com/ghost/blog/'});
                return loader();
            }).otherwise(function (error) {
                error.should.include(rejectMessage);

                // Next test
                overrideConfig({url: 'http://testurl.com/blog/ghost'});
                return loader();
            }).otherwise(function (error) {
                error.should.include(rejectMessage);

                done();
            }).then(function () {
                should.fail('no error was thrown when it should have been');
                done();
            }).then(done, null);
        });

        it('requires a database config', function (done) {
            // replace the config file with invalid data
            overrideConfig({database: null});

            loader().otherwise(function (error) {
                error.should.include(rejectMessage);

                // Next test
                overrideConfig({database: {}});
                return loader();
            }).otherwise(function (error) {
                error.should.include(rejectMessage);

                done();
            }).then(function () {
                should.fail('no error was thrown when it should have been');
                done();
            }).then(done, null);
        });


        it('requires a socket or a host and port', function (done) {
            // replace the config file with invalid data
            overrideConfig({server: {socket: 'test'}});

            loader().then(function (localConfig) {
                localConfig.server.socket.should.equal('test');

                  // Next test
                overrideConfig({server: null});
                return loader();
            }).otherwise(function (error) {
                error.should.include(rejectMessage);

                // Next test
                overrideConfig({server: {host: null}});
                return loader();
            }).otherwise(function (error) {
                error.should.include(rejectMessage);

                // Next test
                overrideConfig({server: {port: null}});
                return loader();
            }).otherwise(function (error) {
                error.should.include(rejectMessage);

                // Next test
                overrideConfig({server: {host: null, port: null}});
                return loader();
            }).otherwise(function (error) {
                error.should.include(rejectMessage);

                done();
            }).then(function () {
                should.fail('no error was thrown when it should have been');
                done();
            }).then(done, null);
        });
    });

    describe('Theme', function () {

        var sandbox,
            settings,
            settingsStub;

        beforeEach(function (done) {
            sandbox = sinon.sandbox.create();

            settings = {'read': function read() {}};

            settingsStub = sandbox.stub(settings, 'read', function () {
                return when({value: 'casper'});
            });

            theme.update(settings, 'http://my-ghost-blog.com')
                .then(done)
                .then(null, done);
        });

        afterEach(function (done) {
            theme.update(settings, defaultConfig.url)
                .then(done)
                .then(null, done);

            sandbox.restore();
        });

        it('should have exactly the right keys', function () {
            var themeConfig = theme();

            // This will fail if there are any extra keys
            themeConfig.should.have.keys('url', 'title', 'description', 'logo', 'cover');
        });

        it('should have the correct values for each key', function () {
            var themeConfig = theme();

            // Check values are as we expect
            themeConfig.should.have.property('url', 'http://my-ghost-blog.com');
            themeConfig.should.have.property('title', 'casper');
            themeConfig.should.have.property('description', 'casper');
            themeConfig.should.have.property('logo', 'casper');
            themeConfig.should.have.property('cover', 'casper');

            // Check settings.read gets called exactly 4 times
            settingsStub.callCount.should.equal(4);
        });
    });

    describe('Paths', function () {
        var sandbox;

        beforeEach(function () {
            sandbox = sinon.sandbox.create();
        });

        afterEach(function (done) {
            sandbox.restore();
            paths.update(defaultConfig.url)
                .then(done)
                .then(null, done);
        });

        it('should have exactly the right keys', function () {
            var pathConfig = paths();

            // This will fail if there are any extra keys
            pathConfig.should.have.keys(
                'appRoot',
                'subdir',
                'config',
                'configExample',
                'contentPath',
                'corePath',
                'themePath',
                'appPath',
                'imagesPath',
                'imagesRelPath',
                'adminViews',
                'helperTemplates',
                'exportPath',
                'lang',
                'debugPath',
                'availableThemes',
<<<<<<< HEAD
                'availablePlugins',
                'builtScriptPath'
=======
                'availableApps'
>>>>>>> Rename 'plugins' to 'apps'
            );
        });

        it('should have the correct values for each key', function () {
            var pathConfig = paths(),
                appRoot = path.resolve(__dirname, '../../../');

            pathConfig.should.have.property('appRoot', appRoot);
            pathConfig.should.have.property('subdir', '');
        });

        it('should not return a slash for subdir', function (done) {
            paths.update('http://my-ghost-blog.com').then(function () {
                paths().should.have.property('subdir', '');

                return paths.update('http://my-ghost-blog.com/');
            }).then(function () {
                paths().should.have.property('subdir', '');

                done();
            }).otherwise(done);
        });

        it('should handle subdirectories properly', function (done) {
            paths.update('http://my-ghost-blog.com/blog').then(function () {
                paths().should.have.property('subdir', '/blog');

                return paths.update('http://my-ghost-blog.com/blog/');
            }).then(function () {
                paths().should.have.property('subdir', '/blog');

                return paths.update('http://my-ghost-blog.com/my/blog');
            }).then(function () {
                paths().should.have.property('subdir', '/my/blog');

                return paths.update('http://my-ghost-blog.com/my/blog/');
            }).then(function () {
                paths().should.have.property('subdir', '/my/blog');

                done();
            }).otherwise(done);
        });
    });

    describe('urlFor', function () {

        afterEach(function (done) {
            paths.update(defaultConfig.url)
                .then(done)
                .then(null, done);
        });

        it('should return the home url with no options', function (done) {
            paths.urlFor().should.equal('/');
            paths.update('http://my-ghost-blog.com/blog').then(function () {
                paths.urlFor().should.equal('/blog/');

                done();
            });
        });

        it('should return home url when asked for', function (done) {
            var testContext = 'home';

            paths.update('http://my-ghost-blog.com').then(function () {
                paths.urlFor(testContext).should.equal('/');
                paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/');

                return paths.update('http://my-ghost-blog.com/blog');
            }).then(function () {
                paths.urlFor(testContext).should.equal('/blog/');
                paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/');

                done();
            });
        });

        it('should return rss url when asked for', function (done) {
            var testContext = 'rss';

            paths.update('http://my-ghost-blog.com').then(function () {
                paths.urlFor(testContext).should.equal('/rss/');
                paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/rss/');
                return paths.update('http://my-ghost-blog.com/blog');
            }).then(function () {
                paths.urlFor(testContext).should.equal('/blog/rss/');
                paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/rss/');

                done();
            });
        });

        it('should return url for a random path when asked for', function (done) {
            var testContext = {relativeUrl: '/about/'};

            paths.update('http://my-ghost-blog.com').then(function () {
                paths.urlFor(testContext).should.equal('/about/');
                paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/about/');

                return paths.update('http://my-ghost-blog.com/blog');
            }).then(function () {
                paths.urlFor(testContext).should.equal('/blog/about/');
                paths.urlFor(testContext, true).should.equal('http://my-ghost-blog.com/blog/about/');

                done();
            });
        });

        it('should return url for a post when asked for', function (done) {
            var testContext = 'post',
                testData = {post: testUtils.DataGenerator.Content.posts[2], permalinks: {value: '/:slug/'}};

            paths.update('http://my-ghost-blog.com').then(function () {
                paths.urlFor(testContext, testData).should.equal('/short-and-sweet/');
                paths.urlFor(testContext, testData, true).should.equal('http://my-ghost-blog.com/short-and-sweet/');

                return paths.update('http://my-ghost-blog.com/blog');
            }).then(function () {
                paths.urlFor(testContext, testData).should.equal('/blog/short-and-sweet/');
                paths.urlFor(testContext, testData, true).should.equal('http://my-ghost-blog.com/blog/short-and-sweet/');

                done();
            }).then(null, done);
        });

        it('should return url for a dated post when asked for', function (done) {
            var testContext = 'post',
                testData = {
                    post: testUtils.DataGenerator.Content.posts[2],
                    permalinks: {value: '/:year/:month/:day/:slug/'}
                },
                today = new Date(),
                dd = ("0" + today.getDate()).slice(-2),
                mm = ("0" + (today.getMonth() + 1)).slice(-2),
                yyyy = today.getFullYear(),
                postLink = '/' + yyyy + '/' + mm + '/' + dd + '/short-and-sweet/';

            paths.update('http://my-ghost-blog.com').then(function () {
                paths.urlFor(testContext, testData).should.equal(postLink);
                paths.urlFor(testContext, testData, true).should.equal('http://my-ghost-blog.com' + postLink);

                return paths.update('http://my-ghost-blog.com/blog');
            }).then(function () {
                paths.urlFor(testContext, testData).should.equal('/blog' + postLink);
                paths.urlFor(testContext, testData, true).should.equal('http://my-ghost-blog.com/blog' + postLink);

                done();
            }).then(null, done);
        });

    });

    describe('urlForPost', function () {
        var sandbox;

        beforeEach(function () {
            sandbox = sinon.sandbox.create();
        });

        afterEach(function (done) {
            sandbox.restore();
            paths.update(defaultConfig.url)
                .then(done)
                .then(null, done);
        });

        it('should output correct url for post', function (done) {
            var  settings = {'read': function read() {}},
                settingsStub = sandbox.stub(settings, 'read', function () {
                    return when({value: '/:slug/'});
                }),
                testData = testUtils.DataGenerator.Content.posts[2],
                postLink = '/short-and-sweet/';

            paths.update('http://my-ghost-blog.com').then(function () {

                // next test
                return paths.urlForPost(settings, testData);
            }).then(function (url) {
                url.should.equal(postLink);

                // next test
                return paths.urlForPost(settings, testData, true);
            }).then(function (url) {
                url.should.equal('http://my-ghost-blog.com' + postLink);

                return paths.update('http://my-ghost-blog.com/blog');
            }).then(function () {

                // next test
                return paths.urlForPost(settings, testData);
            }).then(function (url) {
                url.should.equal('/blog' + postLink);

                // next test
                return paths.urlForPost(settings, testData, true);
            }).then(function (url) {
                url.should.equal('http://my-ghost-blog.com/blog' + postLink);

                done();
            }).then(null, done);

        });

        it('should output correct url for post with date permalink', function (done) {
            var settings = {'read': function read() {}},
                settingsStub = sandbox.stub(settings, 'read', function () {
                    return when({value: '/:year/:month/:day/:slug/'});
                }),
                testData = testUtils.DataGenerator.Content.posts[2],
                today = new Date(),
                dd = ("0" + today.getDate()).slice(-2),
                mm = ("0" + (today.getMonth() + 1)).slice(-2),
                yyyy = today.getFullYear(),
                postLink = '/' + yyyy + '/' + mm + '/' + dd + '/short-and-sweet/';

            paths.update('http://my-ghost-blog.com').then(function () {

                // next test
                return paths.urlForPost(settings, testData);
            }).then(function (url) {
                url.should.equal(postLink);

                // next test
                return paths.urlForPost(settings, testData, true);
            }).then(function (url) {
                url.should.equal('http://my-ghost-blog.com' + postLink);

                return paths.update('http://my-ghost-blog.com/blog');
            }).then(function () {

                // next test
                return paths.urlForPost(settings, testData);
            }).then(function (url) {
                url.should.equal('/blog' + postLink);

                // next test
                return paths.urlForPost(settings, testData, true);
            }).then(function (url) {
                url.should.equal('http://my-ghost-blog.com/blog' + postLink);

                done();
            }).then(null, done);
        });

        it('should output correct url for page with date permalink', function (done) {
            var settings = {'read': function read() {}},
                settingsStub = sandbox.stub(settings, 'read', function () {
                    return when({value: '/:year/:month/:day/:slug/'});
                }),
                testData = testUtils.DataGenerator.Content.posts[5],
                postLink = '/static-page-test/';

            paths.update('http://my-ghost-blog.com').then(function () {

                // next test
                return paths.urlForPost(settings, testData);
            }).then(function (url) {
                url.should.equal(postLink);

                // next test
                return paths.urlForPost(settings, testData, true);
            }).then(function (url) {
                url.should.equal('http://my-ghost-blog.com' + postLink);

                return paths.update('http://my-ghost-blog.com/blog');
            }).then(function () {

                // next test
                return paths.urlForPost(settings, testData);
            }).then(function (url) {
                url.should.equal('/blog' + postLink);

                // next test
                return paths.urlForPost(settings, testData, true);
            }).then(function (url) {
                url.should.equal('http://my-ghost-blog.com/blog' + postLink);

                done();
            }).then(null, done);
        });
    });
});