/**
 * @todo Switch from sha256 to bcrypt
 */

const crypto = require('crypto');
const {Sequelize, db} = require('../connection');

const hashData = function(data, salt = '') {
  var shasum = crypto.createHash('sha256');
  shasum.update(data + salt);
  return shasum.digest('hex');
};

const UserModel = db.define('user', {
  username: {
    type: Sequelize.STRING(40),
    allowNull: false,
    unique: true
  },
  password: {
    type: Sequelize.STRING(64),
    allowNull: false
  },
  salt: {
    type: Sequelize.STRING(64),
  }
}, {
  hooks: {
    beforeCreate: function(user) {
      user.salt = crypto.randomBytes(32).toString('hex');

      user.password = hashData(user.password, user.salt);
    }
  }
});

var User = {model: UserModel};

/**
 * Sanitize is a private helper function that sanitizes the output
 * of the model's CRUD functions. For user's table a whitelist strategy is
 * used to filter out password<hash> and salt<hash> data.
 * 
 * @param <object> - A rowData object
 */
const sanitize = function({id, username, createdAt, updatedAt}) {
  return {id, username, createdAt, updatedAt};
};

User.create = function({username, password}) {
  return UserModel.find({where: {username}})
    .then(function(user) {
      if (user) { throw new Error('User already exists'); }
      return UserModel.create({username, password});
    })
    .then(sanitize);
};

User.validate = function({username, password}) {
  return UserModel.findOne({where: {username}})
    .then(function(user) {
      if (user && user.password === hashData(password, user.salt)) {
        return sanitize(user);
      } else {
        return null;
      }
    });
};

User.findAll = function(query = {}) {
  return UserModel.findAll({where: query}).map(sanitize);
};

User.read = User.find = function({id, username}) {
  return UserModel.findOne({where: {id}})
    .then(sanitize);
};

/**
 * @todo test coverage for update!
 */
User.update = function(query, values) {
  return UserModel.update({values: values}, {where: query})
    .then(doc.map(sanitize));
};

User.delete = function(query) {
  return UserModel.destroy({where: query})
    .then(sanitize);
};

module.exports = User;
/**
 * @todo Switch from sha256 to bcrypt
 */

const crypto = require('crypto');
const {Sequelize, db} = require('../connection');

const hashData = function(data, salt = '') {
  var shasum = crypto.createHash('sha256');
  shasum.update(data + salt);
  return shasum.digest('hex');
};

const UserModel = db.define('user', {
  username: {
    type: Sequelize.STRING(40),
    allowNull: false,
    unique: true
  },
  password: {
    type: Sequelize.STRING(64),
    allowNull: false
  },
  salt: {
    type: Sequelize.STRING(64),
  }
}, {
  hooks: {
    beforeCreate: function(user) {
      user.salt = crypto.randomBytes(32).toString('hex');

      user.password = hashData(user.password, user.salt);
    }
  }
});

UserModel.prototype.toJSON = function() {
  var values = this.dataValues;

  delete values.password;
  delete values.salt;

  return values;
};

var User = {model: UserModel};

const privateData = ['password', 'salt'];

User.create = function({username, password}) {
  return UserModel.findOrCreate({
    where: {username},
    defaults: {password}
  })
    .spread(function(user, created) {
      if (!created) {
        throw new Error('User already exists');
      }

      return user.toJSON();
    });
};

User.validate = function({username, password}) {
  return UserModel.findOne({where: {username}})
    .then(function(user) {
      if (user && user.password === hashData(password, user.salt)) {
        return user.toJSON();
      } else {
        return null;
      }
    });
};

User.findAll = function(query = {}) {
  return UserModel.findAll({where: query})
    .map(user => user.toJSON());
};

User.read = User.find = function({id, username}) {
  return UserModel.findOne({where: {id}})
    .then(user => user.toJSON());
};

/**
 * @todo test coverage for update!
 * @todo generate new password hash?
 * Model.update returns an array with the affected row count and the rows affected.
 * It should be decided how that is returned later.
 */
User.update = function(query, values) {
  return UserModel.update({values: values}, {where: query});
};

/**
 * Model.destroy returns the rows affected and does not need to be sanitized.
 */
User.delete = function(query) {
  return UserModel.destroy({where: query});
};

module.exports = User;
Refactor user model to use toJSON override.