var sinon = require('sinon');
var identity_check = require('../../src/lib/identity_check');
var exceptions = require('../../src/lib/exceptions');
var assert = require('assert');
var winston = require('winston');
var Promise = require('bluebird');

describe('test identity check process', function() {
  var req, res, app, icheck_interface;
  var user_data_store;
  var notifier;

  beforeEach(function() {
    req = {};
    res = {};

    app = {};
    icheck_interface = {};
    icheck_interface.pre_check_callback = sinon.stub();

    user_data_store = {};
    user_data_store.issue_identity_check_token = sinon.stub();
    user_data_store.issue_identity_check_token.returns(Promise.resolve());
    user_data_store.consume_identity_check_token = sinon.stub();
    user_data_store.consume_identity_check_token.returns(Promise.resolve({ userid: 'user' }));

    notifier = {};
    notifier.notify = sinon.stub().returns(Promise.resolve());

    req.headers = {};
    req.session = {};
    req.session.auth_session = {};

    req.query = {};
    req.app = {};
    req.app.get = sinon.stub();
    req.app.get.withArgs('logger').returns(winston);
    req.app.get.withArgs('user data store').returns(user_data_store);
    req.app.get.withArgs('notifier').returns(notifier);

    res.status = sinon.spy();
    res.send = sinon.spy();
    res.redirect = sinon.spy();
    res.render = sinon.spy();

    app.get = sinon.spy();
    app.post = sinon.spy();
  });

  it('should register a POST and GET endpoint', function() {
    var app = {};
    app.get = sinon.spy();
    app.post = sinon.spy();
    var endpoint = '/test';
    var icheck_interface = {};

    identity_check(app, endpoint, icheck_interface);

    assert(app.get.calledOnce);
    assert(app.get.calledWith(endpoint));

    assert(app.post.calledOnce);
    assert(app.post.calledWith(endpoint));
  });

  describe('test POST', test_post_handler);
  describe('test GET', test_get_handler);

  function test_post_handler() {
    it('should send 403 if pre check rejects', function(done) {
      var endpoint = '/protected';

      icheck_interface.pre_check_callback.returns(Promise.reject('No access'));
      identity_check(app, endpoint, icheck_interface);

      res.send = sinon.spy(function() {
        assert.equal(res.status.getCall(0).args[0], 403);
        done();
      });

      var handler = app.post.getCall(0).args[1];
      handler(req, res);
    });

    it('should send 400 if email is missing in provided identity', function(done) {
      var endpoint = '/protected';
      var identity = { userid: 'abc' };

      icheck_interface.pre_check_callback.returns(Promise.resolve(identity));
      identity_check(app, endpoint, icheck_interface);

      res.send = sinon.spy(function() {
        assert.equal(res.status.getCall(0).args[0], 400);
        done();
      });

      var handler = app.post.getCall(0).args[1];
      handler(req, res);
    });

    it('should send 400 if userid is missing in provided identity', function(done) {
      var endpoint = '/protected';
      var identity = { email: 'abc@example.com' };

      icheck_interface.pre_check_callback.returns(Promise.resolve(identity));
      identity_check(app, endpoint, icheck_interface);

      res.send = sinon.spy(function() {
        assert.equal(res.status.getCall(0).args[0], 400);
        done();
      });
      var handler = app.post.getCall(0).args[1];
      handler(req, res);
    });

    it('should issue a token, send an email and return 204', function(done) {
      var endpoint = '/protected';
      var identity = { userid: 'user', email: 'abc@example.com' };
      req.headers.host = 'localhost';
      req.headers['x-original-uri'] = '/auth/test';

      icheck_interface.pre_check_callback.returns(Promise.resolve(identity));
      identity_check(app, endpoint, icheck_interface);

      res.send = sinon.spy(function() {
        assert.equal(res.status.getCall(0).args[0], 204);
        assert(notifier.notify.calledOnce);
        assert(user_data_store.issue_identity_check_token.calledOnce);
        assert.equal(user_data_store.issue_identity_check_token.getCall(0).args[0], 'user');
        assert.equal(user_data_store.issue_identity_check_token.getCall(0).args[3], 240000);
        done();
      });
      var handler = app.post.getCall(0).args[1];
      handler(req, res);
    });
  }

  function test_get_handler() {
    it('should send 403 if no identity_token is provided', function(done) {
      var endpoint = '/protected';

      identity_check(app, endpoint, icheck_interface);

      res.send = sinon.spy(function() {
        assert.equal(res.status.getCall(0).args[0], 403);
        done();
      });
      var handler = app.get.getCall(0).args[1];
      handler(req, res);
    });

    it('should render template if identity_token is provided and still valid', function(done) {
      req.query.identity_token = 'token';
      var endpoint = '/protected';

      icheck_interface.render_template = 'template';

      identity_check(app, endpoint, icheck_interface);

      res.render = sinon.spy(function(template) {
        assert.equal(template, 'template');
        done();
      });
      var handler = app.get.getCall(0).args[1];
      handler(req, res);
    });

    it('should return 403 if identity_token is provided but invalid', function(done) {
      req.query.identity_token = 'token';
      var endpoint = '/protected';

      icheck_interface.render_template = 'template';
      user_data_store.consume_identity_check_token
        .returns(Promise.reject('Invalid token'));

      identity_check(app, endpoint, icheck_interface);

      res.send = sinon.spy(function(template) {
        assert.equal(res.status.getCall(0).args[0], 403);
        done();
      });
      var handler = app.get.getCall(0).args[1];
      handler(req, res);
    });

    it('should set the identity_check session object even if session does not exist yet', function(done) {
      req.query.identity_token = 'token';
      var endpoint = '/protected';

      req.session = {};
      icheck_interface.render_template = 'template';

      identity_check(app, endpoint, icheck_interface);

      res.render = sinon.spy(function(template) {
        assert.equal(req.session.auth_session.identity_check.userid, 'user');
        assert.equal(template, 'template');
        done();
      });
      var handler = app.get.getCall(0).args[1];
      handler(req, res);
    });
  }
});