/*globals describe, beforeEach, afterEach, it*/
/*jshint expr:true*/
var should      = require('should'),
    sinon       = require('sinon'),
    middleware  = require('../../../server/middleware').middleware;

describe('Middleware: spamPrevention', function () {
    var sandbox,
        req,
        next,
        error,
        spyNext;

    beforeEach(function () {
        sandbox = sinon.sandbox.create();
        error = null;

        next = sinon.spy();

        spyNext = sinon.spy(function (param) {
            error = param;
        });
    });

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

    describe('signin', function () {
        beforeEach(function () {
            req = {
                connection: {
                    remoteAddress: '10.0.0.0'
                },
                body: {
                    username: 'tester',
                    grant_type: 'password'
                }
            };
        });

        it('calls next if refreshing the token', function (done) {
            req.body.grant_type = 'refresh_token';
            middleware.spamPrevention.signin(req, null, next);

            next.calledOnce.should.be.true;
            done();
        });

        it ('creates a BadRequestError when there\'s no username', function (done) {
            req.body = {};

            middleware.spamPrevention.signin(req, null, spyNext);

            should.exist(error);
            error.should.be.a.BadRequestError;
            done();
        });

        it ('rate limits after 10 attempts', function (done) {
            for (var ndx = 0; ndx < 10; ndx = ndx + 1) {
                middleware.spamPrevention.signin(req, null, spyNext);
            }

            middleware.spamPrevention.signin(req, null, spyNext);
            should.exist(error);
            error.should.be.a.UnauthorizedError;

            done();
        });

        it ('allows more attempts after an hour', function (done) {
            var ndx,
                stub = sinon.stub(process, 'hrtime', function () {
                    return [10, 10];
                });

            for (ndx = 0; ndx < 10; ndx = ndx + 1) {
                middleware.spamPrevention.signin(req, null, spyNext);
            }

            middleware.spamPrevention.signin(req, null, spyNext);
            error.should.be.a.UnauthorizedError;
            error = null;

            // fast forward 1 hour
            process.hrtime.restore();
            stub = sinon.stub(process, 'hrtime', function () {
                return [3700000, 10];
            });

            middleware.spamPrevention.signin(req, null, spyNext);
            should(error).equal(undefined);
            spyNext.should.be.calledOnce;

            process.hrtime.restore();
            done();
        });
    });

    describe('forgotten', function () {
        beforeEach(function () {
            req = {
                connection: {
                    remoteAddress: '10.0.0.0'
                },
                body: {
                    passwordreset: [
                        {email:'test@ghost.org'}
                    ]
                }
            };
        });

        it ('send a bad request if no email is specified', function (done) {
            req.body = {
                passwordreset: [{}]
            };

            middleware.spamPrevention.forgotten(req, null, spyNext);
            error.should.be.a.BadRequestError;

            done();
        });

        it ('creates an unauthorized error after 5 attempts with same email', function (done) {
            for (var ndx = 0; ndx < 6; ndx = ndx + 1) {
                middleware.spamPrevention.forgotten(req, null, spyNext);
            }

            middleware.spamPrevention.forgotten(req, null, spyNext);
            error.should.be.a.UnauthorizedError;

            done();
        });

        it ('creates an unauthorized error after 5 attempts from the same ip', function (done) {
            var ndx, email;

            for (ndx = 0; ndx < 6; ndx = ndx + 1) {
                email = 'test' + String(ndx) + '@ghost.org';
                req.body.passwordreset = [
                    {email: email}
                ];

                middleware.spamPrevention.forgotten(req, null, spyNext);
            }

            middleware.spamPrevention.forgotten(req, null, spyNext);
            error.should.be.a.UnauthorizedError;

            done();
        });
    });

    describe('protected', function () {
        var res;

        beforeEach(function () {
            res = sinon.spy();
            req = {
                connection: {
                    remoteAddress: '10.0.0.0'
                },
                body: {
                    password: 'password'
                }
            };
        });

        it ('sets an error when there is no password', function (done) {
            req.body = {};

            middleware.spamPrevention.protected(req, res, spyNext);
            res.error.message.should.equal('No password entered');
            spyNext.should.be.calledOnce;

            done();
        });

        it ('sets and error message after 10 tries', function (done) {
            var ndx;

            for (ndx = 0; ndx < 10; ndx = ndx + 1) {
                middleware.spamPrevention.protected(req, res, spyNext);
            }

            should.not.exist(res.error);
            middleware.spamPrevention.protected(req, res, spyNext);
            should.exist(res.error);
            should.exist(res.error.message);

            done();
        });

        it ('allows more tries after an hour', function (done) {
            var ndx,
                stub = sinon.stub(process, 'hrtime', function () {
                    return [10, 10];
                });

            for (ndx = 0; ndx < 11; ndx = ndx + 1) {
                middleware.spamPrevention.protected(req, res, spyNext);
            }

            should.exist(res.error);
            process.hrtime.restore();
            stub = sinon.stub(process, 'hrtime', function () {
                return [3610000, 10];
            });

            res = sinon.spy();

            middleware.spamPrevention.protected(req, res, spyNext);
            should.not.exist(res.error);

            process.hrtime.restore();
            done();
        });
    });
});