/*globals describe, beforeEach, afterEach, it*/
var should = require('should'),
    sinon = require('sinon'),
    crypto = require('crypto'),
    fs = require('fs-extra'),
    common = require('../../../../server/lib/common'),
    settingsCache = require('../../../../server/services/settings/cache'),
    privateBlogging = require('../../../../server/apps/private-blogging/lib/middleware'),
    sandbox = sinon.sandbox.create();

function hash(password, salt) {
    var hasher = crypto.createHash('sha256');
    hasher.update(password + salt, 'utf8');
    return hasher.digest('hex');
}

describe('Private Blogging', function () {
    var settingsStub;

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

    describe('passProtect', function () {
        var req, res, next;

        beforeEach(function () {
            req = {
                query: {}
            };
            res = {};
            settingsStub = sandbox.stub(settingsCache, 'get');
            next = sandbox.spy();
        });

        it('checkIsPrivate should call next if not private', function () {
            settingsStub.withArgs('is_private').returns(false);

            privateBlogging.checkIsPrivate(req, res, next);
            next.called.should.be.true();
            res.isPrivateBlog.should.be.false();
        });

        it('checkIsPrivate should load session if private', function () {
            settingsStub.withArgs('is_private').returns(true);

            privateBlogging.checkIsPrivate(req, res, next);
            res.isPrivateBlog.should.be.true();
        });

        describe('not private', function () {
            beforeEach(function () {
                res.isPrivateBlog = false;
            });

            it('filterPrivateRoutes should call next if not private', function () {
                privateBlogging.filterPrivateRoutes(req, res, next);
                next.called.should.be.true();
            });

            it('isPrivateSessionAuth should redirect if blog is not private', function () {
                res = {
                    redirect: sandbox.spy(),
                    isPrivateBlog: false
                };
                privateBlogging.isPrivateSessionAuth(req, res, next);
                res.redirect.called.should.be.true();
            });
        });

        describe('private', function () {
            beforeEach(function () {
                res = {
                    status: function () {
                        return this;
                    },
                    send: function () {
                    },
                    set: function () {
                    },
                    redirect: sandbox.spy(),
                    isPrivateBlog: true
                };

                req.session = {};
            });

            it('filterPrivateRoutes should call next if admin', function () {
                res.isAdmin = true;
                privateBlogging.filterPrivateRoutes(req, res, next);
                next.called.should.be.true();
            });

            it('filterPrivateRoutes should call next if is the "private" route', function () {
                req.path = req.url = '/private/';
                privateBlogging.filterPrivateRoutes(req, res, next);
                next.called.should.be.true();
            });

            it('filterPrivateRoutes: sitemap redirects to /private', function () {
                req.path = req.url = '/sitemap.xml';

                privateBlogging.filterPrivateRoutes(req, res, next);
                res.redirect.calledOnce.should.be.true();
            });

            it('filterPrivateRoutes: sitemap with params redirects to /private', function () {
                req.url = '/sitemap.xml?weird=param';
                req.path = '/sitemap.xml';

                privateBlogging.filterPrivateRoutes(req, res, next);
                res.redirect.calledOnce.should.be.true();
            });

            it('filterPrivateRoutes: rss redirects to /private', function () {
                req.path = req.url = '/rss/';

                privateBlogging.filterPrivateRoutes(req, res, next);
                res.redirect.calledOnce.should.be.true();
            });

            it('filterPrivateRoutes: author rss redirects to /private', function () {
                req.path = req.url = '/author/halfdan/rss/';

                privateBlogging.filterPrivateRoutes(req, res, next);
                res.redirect.calledOnce.should.be.true();
            });

            it('filterPrivateRoutes: tag rss redirects to /private', function () {
                req.path = req.url = '/tag/slimer/rss/';

                privateBlogging.filterPrivateRoutes(req, res, next);
                res.redirect.calledOnce.should.be.true();
            });

            it('filterPrivateRoutes: rss plus something redirects to /private', function () {
                req.path = req.url = '/rss/sometag';

                privateBlogging.filterPrivateRoutes(req, res, next);
                res.redirect.calledOnce.should.be.true();
            });

            it('filterPrivateRoutes should render custom robots.txt', function () {
                req.url = req.path = '/robots.txt';
                res.writeHead = sandbox.spy();
                res.end = sandbox.spy();
                sandbox.stub(fs, 'readFile').callsFake(function (file, cb) {
                    cb(null, 'User-agent: * Disallow: /');
                });
                privateBlogging.filterPrivateRoutes(req, res, next);
                res.writeHead.called.should.be.true();
                res.end.called.should.be.true();
            });

            it('authenticateProtection should call next if error', function () {
                res.error = 'Test Error';
                privateBlogging.authenticateProtection(req, res, next);
                next.called.should.be.true();
            });

            describe('with hash verification', function () {
                beforeEach(function () {
                    settingsStub.withArgs('password').returns('rightpassword');
                });

                it('authenticatePrivateSession should return next if hash is verified', function () {
                    var salt = Date.now().toString();

                    req.session = {
                        token: hash('rightpassword', salt),
                        salt: salt
                    };

                    privateBlogging.authenticatePrivateSession(req, res, next);
                    next.called.should.be.true();
                });

                it('authenticatePrivateSession should redirect if hash is not verified', function () {
                    req.url = '/welcome';
                    req.session = {
                        token: 'wrongpassword',
                        salt: Date.now().toString()
                    };
                    res.redirect = sandbox.spy();

                    privateBlogging.authenticatePrivateSession(req, res, next);
                    res.redirect.called.should.be.true();
                });

                it('isPrivateSessionAuth should redirect if hash is verified', function () {
                    var salt = Date.now().toString();

                    req.session = {
                        token: hash('rightpassword', salt),
                        salt: salt
                    };
                    res.redirect = sandbox.spy();

                    privateBlogging.isPrivateSessionAuth(req, res, next);
                    res.redirect.called.should.be.true();
                });

                it('isPrivateSessionAuth should return next if hash is not verified', function () {
                    req.session = {
                        token: 'wrongpassword',
                        salt: Date.now().toString()
                    };

                    privateBlogging.isPrivateSessionAuth(req, res, next);
                    next.called.should.be.true();
                });

                it('authenticateProtection should return next if password is incorrect', function () {
                    req.body = {password: 'wrongpassword'};

                    privateBlogging.authenticateProtection(req, res, next);
                    res.error.should.not.be.empty();
                    next.called.should.be.true();
                });

                it('authenticateProtection should redirect if password is correct', function () {
                    req.body = {password: 'rightpassword'};
                    req.session = {};
                    res.redirect = sandbox.spy();

                    privateBlogging.authenticateProtection(req, res, next);
                    res.redirect.called.should.be.true();
                });

                it('authenticateProtection should redirect to "/" if r param is a full url', function () {
                    req.body = {password: 'rightpassword'};
                    req.session = {};
                    req.query = {
                        r: encodeURIComponent('http://britney.com')
                    };
                    res.redirect = sandbox.spy();

                    privateBlogging.authenticateProtection(req, res, next);
                    res.redirect.called.should.be.true();
                    res.redirect.args[0][0].should.be.equal('/');
                });

                it('filterPrivateRoutes should 404 for /rss/ requests', function () {
                    var salt = Date.now().toString();
                    req.url = req.path = '/rss/';

                    req.session = {
                        token: hash('rightpassword', salt),
                        salt: salt
                    };

                    res.isPrivateBlog = true;
                    res.redirect = sandbox.spy();

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    next.called.should.be.true();
                    (next.firstCall.args[0] instanceof common.errors.NotFoundError).should.eql(true);
                });

                it('filterPrivateRoutes should 404 for /rss requests', function () {
                    var salt = Date.now().toString();
                    req.url = req.path = '/rss';

                    req.session = {
                        token: hash('rightpassword', salt),
                        salt: salt
                    };

                    res.isPrivateBlog = true;
                    res.redirect = sandbox.spy();

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    next.called.should.be.true();
                    (next.firstCall.args[0] instanceof common.errors.NotFoundError).should.eql(true);
                });

                it('filterPrivateRoutes should 404 for rss with pagination requests', function () {
                    var salt = Date.now().toString();
                    req.url = req.path = '/rss/1';

                    req.session = {
                        token: hash('rightpassword', salt),
                        salt: salt
                    };

                    res.isPrivateBlog = true;
                    res.redirect = sandbox.spy();

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    next.called.should.be.true();
                    (next.firstCall.args[0] instanceof common.errors.NotFoundError).should.eql(true);
                });

                it('filterPrivateRoutes should 404 for tag rss requests', function () {
                    var salt = Date.now().toString();
                    req.url = req.path = '/tag/welcome/rss/';

                    req.session = {
                        token: hash('rightpassword', salt),
                        salt: salt
                    };

                    res.isPrivateBlog = true;
                    res.redirect = sandbox.spy();

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    next.called.should.be.true();
                    (next.firstCall.args[0] instanceof common.errors.NotFoundError).should.eql(true);
                });

                it('filterPrivateRoutes should 404 for tag rss with pagination requests', function () {
                    var salt = Date.now().toString();
                    req.url = req.path = '/tag/welcome/rss/2';

                    req.session = {
                        token: hash('rightpassword', salt),
                        salt: salt
                    };

                    res.isPrivateBlog = true;
                    res.redirect = sandbox.spy();

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    next.called.should.be.true();
                    (next.firstCall.args[0] instanceof common.errors.NotFoundError).should.eql(true);
                });

                it('filterPrivateRoutes should return next if tag contains rss', function () {
                    var salt = Date.now().toString();
                    req.url = req.path = '/tag/rss-test/';

                    req.session = {
                        token: hash('rightpassword', salt),
                        salt: salt
                    };

                    res.isPrivateBlog = true;
                    res.redirect = sandbox.spy();

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    next.called.should.be.true();
                    next.firstCall.args.length.should.equal(0);
                });

                it('filterPrivateRoutes should not 404 for very short post url', function () {
                    var salt = Date.now().toString();
                    req.url = req.path = '/ab/';

                    req.session = {
                        token: hash('rightpassword', salt),
                        salt: salt
                    };

                    res.isPrivateBlog = true;
                    res.redirect = sandbox.spy();

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    next.called.should.be.true();
                    next.firstCall.args.length.should.equal(0);
                });

                it('filterPrivateRoutes: allow private /rss/ feed', function () {
                    settingsStub.withArgs('public_hash').returns('777aaa');

                    req.url = req.originalUrl = req.path = '/777aaa/rss/';
                    req.params = {};

                    res.isPrivateBlog = true;
                    res.locals = {};

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    next.called.should.be.true();
                    req.url.should.eql('/rss/');
                });

                it('filterPrivateRoutes: allow private /rss feed', function () {
                    settingsStub.withArgs('public_hash').returns('777aaa');

                    req.url = req.originalUrl = req.path = '/777aaa/rss';
                    req.params = {};

                    res.isPrivateBlog = true;
                    res.locals = {};

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    next.called.should.be.true();
                    req.url.should.eql('/rss');
                });

                it('filterPrivateRoutes: allow private rss feed e.g. tags', function () {
                    settingsStub.withArgs('public_hash').returns('777aaa');

                    req.url = req.originalUrl = req.path = '/tag/getting-started/777aaa/rss/';
                    req.params = {};

                    res.isPrivateBlog = true;
                    res.locals = {};

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    next.called.should.be.true();
                    req.url.should.eql('/tag/getting-started/rss/');
                });

                it('[failure] filterPrivateRoutes: allow private rss feed e.g. tags', function () {
                    settingsStub.withArgs('public_hash').returns('777aaa');

                    req.url = req.originalUrl = req.path = '/tag/getting-started/rss/';
                    req.params = {};

                    res.isPrivateBlog = true;
                    res.locals = {};

                    res.redirect = sandbox.spy();

                    privateBlogging.filterPrivateRoutes(req, res, next);
                    res.redirect.called.should.be.true();
                });
            });
        });
    });
});
