var crypto = require('crypto');
var async = require('async');
var util = require('util');

var mongoose = require('lib/mongoose'),
    Schema = mongoose.Schema;

var schema = new Schema({
  username: {
    type: String,
    unique: true,
    require: true
  },
  hashedPassword: {
    type: String,
    required: true
  },
  salt: {
    type: String,
    required: true
  },
  quotes: {
    type: Array
  },
  created: {
    type: Date,
    default: Date.now
  }
});
schema.methods.encryptPassword = function(password) {
  return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
};

schema.virtual('password',{type: String, required: true})
  .set(function(password) {
      this._plainPassword = password;
      this.salt = Math.random();
      this.hashedPassword = this.encryptPassword(password);
    })
  .get(function() {return this._plainPassword;});

schema.methods.checkPassword = function(password) {
  return this.encryptPassword(password) == this.hashedPassword;
};

schema.statics.authorize = function(username, password, callback) {
  var User = this;

  async.waterfall([
      function (callback) {
        User.findOne({username: username}, callback);
      },
      function (user, callback) {
        if (user) {
          if (user.checkPassword(password)) {
            callback(null, user);
          } else {
            callback(new AuthError("Wrong password"))
          }
        } else {
          var user = new User({username: username, password: password});
          user.save(function (err) {
            if (err) return callback(err);
            callback(null, user);
          });
        }
      }
    ], callback);
};

exports.User = mongoose.model('User', schema);

function AuthError(message) {
  Error.apply(this, arguments);
  Error.captureStackTrace(this, AuthError);

  this.message = message;
}

util.inherits(AuthError, Error);

AuthError.prototype.name = "AuthError";

module.exports.AuthError = AuthError;

var crypto = require('crypto');
var async = require('async');
var util = require('util');

var mongoose = require('lib/mongoose'),
    Schema = mongoose.Schema;

var schema = new Schema({
  username: {
    type: String,
    unique: true,
    require: 'Username is required'
  },
  quotes: {
    type: Array
  },
  hashedPassword: {
    type: String,
    required: 'Password is required',
    select: false
  },
  salt: {
    type: String,
    select: false
  }
}, { versionKey: false });

schema.methods.encryptPassword = function (password) {
  return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
};

schema.virtual('password')
    .set(function (password) {
      this._plainPassword = password;
      this.salt = Math.random();
      this.hashedPassword = this.encryptPassword(password);
    })
    .get(function () {
      return this._plainPassword;
    });

schema.methods.checkPassword = function (password) {
  if (!password) return false;
  return this.encryptPassword(password) == this.hashedPassword;
};

schema.statics.authorize = function (username, password, callback) {
  var User = this;

  async.waterfall([
    function (callback) {
      User.findOne({username: username}, callback);
    },
    function(user, callback) {
      if (user){
        if (user.checkPassword(password)) {
          callback(null, user);
        } else {
          callback(new AuthError("Wrong password"))
        }
      } else {
        var user = new User({username: username, password: password});
        user.save(function (err) {
          if (err) return callback(err);
          callback(null, user);
        });
      }
    }
  ], callback);
};

exports.User = mongoose.model('User', schema);

function AuthError(message) {
  Error.apply(this, arguments);
  Error.captureStackTrace(this, AuthError);

  this.message = message;
}

util.inherits(AuthError, Error);

AuthError.prototype.name = "AuthError";

module.exports.AuthError = AuthError;

Clean user model - now we have nice message for validation errors, save and validate only what we really need, fixed bug with checkPassword when password empty