/**
 * @fileOverview The Usage model.
 */
var terminus = require('terminus');
var Promise = require('bluebird');
var middlewarify = require('middlewarify');

// var log = require('../util/logger').getLogger('kansas.model.usage');

var Model = require('./model-redis');
var period = require('./period-bucket.model');
var kansasError = require('../util/error');

var Usage = module.exports = Model.extend(function() {
  // wire set/del/get
  middlewarify.make(this, 'set', this.hmset.bind(this, []));
  middlewarify.make(this, 'get', this.hdel.bind(this));
  middlewarify.make(this, 'del', this.hget.bind(this, []));

  this.namespace = 'usage';
  this.idProp = 'token';
});

/**
 * Will consume a unit from a token.
 *
 * @param {string} token then token.
 * @return {Promise(number)} A promise with the usage units remaining, negative
 *   values mean the usage limit has been reached a rejection of Error type
 *   kansas.error.TokenNotExists
 */
Usage.prototype.consume = Promise.method(function(token) {
  // stub to month for now.
  var bucket = period.get(period.Period.MONTH);
  var key = this.getPrefix() + bucket + ':' + token;
  return this.decr(key).then(this._checkConsumeResult.bind(this, token));
});

/**
 * Will check the result of DECR, if negative will check if token even exists.
 *
 * @param {string} token then token.
 * @param {number} remaining usage units remaining.
 * @return {Promise(number)} A promise with the usage units remaining, negative
 *   values mean the usage limit has been reached.
 */
Usage.prototype._checkConsumeResult = Promise.method(function(token, remaining) {
  if (remaining > 0) {
    return remaining;
  }

  // not there, need to check if token exists
  var tokenKey = this.prefix + 'token:' + token;

  return this.exists(tokenKey).then(function(result) {
    if (result) {
      return remaining;
    } else {
      throw(new kansasError.TokenNotExists());
    }
  });
});

/**
 * Perform pre-population of existing tokens.
 *
 * This can be a rather intensive operation, it will create new records
 * equal to the stored tokens. As this method will get invoked each time
 * Kansas starts it may become a bottleneck to your applications boot time.
 * If that's your case it's preferably to run this operation manualy using a
 * worker and a cronned job.
 *
 *
 * @return {Promise} A promise when the operation is complete.
 */
Usage.prototype.prePopulate = function() {
  var self = this;
  return new Promise(function(resolve, reject) {
    var pattern = self.prefix + 'token*';
    var streamWriter = terminus({objectMode: true}, self._populateChunk.bind(self));
    streamWriter.on('error', reject);
    streamWriter.on('finish', resolve);

    self.client.scan({pattern: pattern, count: 1})
      .pipe(streamWriter);
  });
};

/**
 * Handle a single chunk (key) from the master scanning operation.
 *
 * @param {string} chunk The key.
 * @param {string} encoding Encoding.
 * @param {Function} next Callback.
 * @private
 */
Usage.prototype._populateChunk = function (chunk, encoding, next) {
  var self = this;
  this.client.hmget(chunk, 'token', 'limit', 'period', function(err, res) {
    if (err) {return next(err);}
    var token = res[0];
    var limit = res[1];
    var periodName = res[2];

    // check if key not there
    if (!token) { return next();}

    Promise.all([
      self._populateCurrentPeriod(periodName, token, limit),
      self._populateNextPeriod(periodName, token, limit),
    ]).then(next.bind(null, null), next);
  });
};

/**
 * Check if current period's key exists, if not create it.
 *
 * @param {kansas.model.period.Period} periodName The periodname.
 * @param {string} token The token.
 * @param {string} limit The limit.
 * @return {Promise} A promise.
 */
Usage.prototype._populateCurrentPeriod = Promise.method(function(periodName,
  token, limit) {
  var self = this;
  var bucket = period.get(periodName);
  var usageKey = this.getPrefix() + bucket + ':' + token;
  this.exists(usageKey).then(function(result) {
    if (result) {
      return;
    }
    return self.client.set(usageKey, limit);
  });
});

/**
 * Lookahead one period unit and create the usage key.
 *
 * @param {kansas.model.period.Period} periodName The periodname.
 * @param {string} token The token.
 * @param {string} limit The limit.
 * @return {Promise} A promise.
 */
Usage.prototype._populateNextPeriod = Promise.method(function(periodName,
  token, limit) {
  var bucket = period.getFuture(periodName);
  var usageKey = this.getPrefix() + bucket + ':' + token;
  return this.client.set(usageKey, limit);
});

@@ -1923,2660 +1923,4 @@
 %7D);%0A
-%0A/**%0A * Perform pre-population of existing tokens.%0A *%0A * This can be a rather intensive operation, it will create new records%0A * equal to the stored tokens. As this method will get invoked each time%0A * Kansas starts it may become a bottleneck to your applications boot time.%0A * If that's your case it's preferably to run this operation manualy using a%0A * worker and a cronned job.%0A *%0A *%0A * @return %7BPromise%7D A promise when the operation is complete.%0A */%0AUsage.prototype.prePopulate = function() %7B%0A  var self = this;%0A  return new Promise(function(resolve, reject) %7B%0A    var pattern = self.prefix + 'token*';%0A    var streamWriter = terminus(%7BobjectMode: true%7D, self._populateChunk.bind(self));%0A    streamWriter.on('error', reject);%0A    streamWriter.on('finish', resolve);%0A%0A    self.client.scan(%7Bpattern: pattern, count: 1%7D)%0A      .pipe(streamWriter);%0A  %7D);%0A%7D;%0A%0A/**%0A * Handle a single chunk (key) from the master scanning operation.%0A *%0A * @param %7Bstring%7D chunk The key.%0A * @param %7Bstring%7D encoding Encoding.%0A * @param %7BFunction%7D next Callback.%0A * @private%0A */%0AUsage.prototype._populateChunk = function (chunk, encoding, next) %7B%0A  var self = this;%0A  this.client.hmget(chunk, 'token', 'limit', 'period', function(err, res) %7B%0A    if (err) %7Breturn next(err);%7D%0A    var token = res%5B0%5D;%0A    var limit = res%5B1%5D;%0A    var periodName = res%5B2%5D;%0A%0A    // check if key not there%0A    if (!token) %7B return next();%7D%0A%0A    Promise.all(%5B%0A      self._populateCurrentPeriod(periodName, token, limit),%0A      self._populateNextPeriod(periodName, token, limit),%0A    %5D).then(next.bind(null, null), next);%0A  %7D);%0A%7D;%0A%0A/**%0A * Check if current period's key exists, if not create it.%0A *%0A * @param %7Bkansas.model.period.Period%7D periodName The periodname.%0A * @param %7Bstring%7D token The token.%0A * @param %7Bstring%7D limit The limit.%0A * @return %7BPromise%7D A promise.%0A */%0AUsage.prototype._populateCurrentPeriod = Promise.method(function(periodName,%0A  token, limit) %7B%0A  var self = this;%0A  var bucket = period.get(periodName);%0A  var usageKey = this.getPrefix() + bucket + ':' + token;%0A  this.exists(usageKey).then(function(result) %7B%0A    if (result) %7B%0A      return;%0A    %7D%0A    return self.client.set(usageKey, limit);%0A  %7D);%0A%7D);%0A%0A/**%0A * Lookahead one period unit and create the usage key.%0A *%0A * @param %7Bkansas.model.period.Period%7D periodName The periodname.%0A * @param %7Bstring%7D token The token.%0A * @param %7Bstring%7D limit The limit.%0A * @return %7BPromise%7D A promise.%0A */%0AUsage.prototype._populateNextPeriod = Promise.method(function(periodName,%0A  token, limit) %7B%0A  var bucket = period.getFuture(periodName);%0A  var usageKey = this.getPrefix() + bucket + ':' + token;%0A  return this.client.set(usageKey, limit);%0A%7D);%0A

remove populate function from usage model