var utils = require('./utils');

var epsilonDelta = function (configs) {
  configs = configs || {};
  configs.db = configs.db || require('./backends/memory-db')();
  configs.userKey = configs.userKey || 'connection.remoteAddress';
  configs.capacity = configs.capacity || 200;
  configs.expire = configs.expire || 360000;
  configs.autolimit = configs.autolimit || true;
  configs.limitResponse = configs.limitResponse || 'Your rate limit has been reached.';
  configs.limitHeader = configs.limitHeader || 'X-Rate-Limit-Limit';
  configs.remainingHeader = configs.remainingHeader || 'X-Rate-Limit-Remaining';
  configs.resetHeader = configs.resetHeader || 'X-Rate-Limit-Reset';

  var db = configs.db;

  // manually set user's data.
  var manualSet = function manualSet (userKey, capacity, expire) {
    var data = {};
    ((typeof capacity == 'object') ? 
      function () {
        capacity.capacity = capacity.capacity || configs.capacity;
        capacity.expire = capacity.expire || configs.expire;

        data.capacity = capacity.capacity;
        data.expire = capacity.expire;
      } :
      function () {
        capacity = capacity;

        data.capacity = capacity;
        data.expire = expire;
      })();
    db.hmset(userKey, data);
  };

  // general request update function
  var updateUser = function updateUser (userKey, cb) {
    db.hgetall(userKey, function (err, data) {
      var now = Date.now();

      // default value the data
      data = data || {};
      data.capacity = data.hasOwnProperty('capacity') ? 
        data.capacity : configs.capacity;
      data.expire = data.hasOwnProperty('expire') ? 
        data.expire : configs.expire;

      // refill bucket if should be refilled before now
      (data.expire <= now) && function(){
        data.capacity = configs.capacity;
        data.expire = now + configs.expire;
      }();

      // take one from bucket if user still has tokens
      (data.capacity > 0) && (data.capacity--);

      // write code here
      db.hmset(userKey, data);

      cb && cb(null, data);
      return data;
    });
  };

  var rate = function rate (userKey, cb) {
    updateUser(userKey, function (err, data) {
      var limitReached = data.capacity <= 0;
      cb && cb(err, limitReached);
    });
  };

  // the main limiter middleware.
  var limiter = function limiter (req, res, next) {
    var userKey = ((typeof configs.userKey == 'function') ?
      configs.userKey(req) :
      ((configs.userKey.indexOf(':') != -1) ? 
        utils.compileKey(configs.userKey, req) :
        utils.nestedGet(req, configs.userKey)));

    updateUser(userKey, function (err, data) {
      res.setHeader(configs.limitHeader, configs.capacity);
      res.setHeader(configs.remainingHeader, data.capacity);
      res.setHeader(configs.resetHeader, data.expire - Date.now());

      res.limitReached = false;
      ((configs.autolimit && parseInt(data.capacity) > 0) ? 
        next :
        function () {
          // if callback exists, call it
          res.limitReached = true;
          configs.limitCallback && configs.limitCallback(req, res);
          
          (typeof configs.limitResponse == 'object') &&
            res.set('Content-Type', 'application/json');
          res.status(429);
          res.end(JSON.stringify(configs.limitResponse));
        })();
    });
  };

  limiter.rate = rate;
  limiter.updateUser = updateUser;
  limiter.manualSet = manualSet;
  return limiter;
}

module.exports = epsilonDelta;
@@ -97,36 +97,35 @@
 %7B%7D;%0A
+%0A
   
-configs.db = configs.db %7C%7C
+var defaults = %7B%0A    db:
  req
@@ -158,48 +158,22 @@
 ')()
-;%0A  configs.userKey = configs.
+,%0A    
 userKey
- %7C%7C
+:
  'co
@@ -199,47 +199,22 @@
 ess'
-;%0A  configs.capacity = configs.
+,%0A    
 capacity
  %7C%7C 
@@ -213,92 +213,39 @@
 city
- %7C%7C
+:
  200
-;%0A  configs.expire = configs.
+,%0A    
 expire
- %7C%7C
+:
  360000
-;%0A  configs.autolimit = configs.
+,%0A    
 auto
@@ -253,52 +253,20 @@
 imit
- %7C%7C
+:
  true
-;%0A  configs.limitResponse = configs.
+,%0A    
 limi
@@ -274,19 +274,17 @@
 Response
- %7C%7C
+:
  'Your r
@@ -315,42 +315,14 @@
 ed.'
-;%0A  configs.limitHeader = configs.
+,%0A    
 limi
@@ -324,27 +324,25 @@
  limitHeader
- %7C%7C
+:
  'X-Rate-Lim
@@ -350,28 +350,22 @@
 t-Limit'
-;%0A  configs.
+,%0A    
 remainin
@@ -375,37 +375,52 @@
 ader
- = configs.remaining
+: 'X-Rate-Limit-Remaining',%0A    reset
 Header
- %7C%7C
+:
  'X-
@@ -436,85 +436,114 @@
 t-Re
-maining';%0A
+set'%0A  %7D;%0A%0A  Object.keys(defaults).forEach(function (k) %7B%0A 
   
+ 
 configs
-.resetHeader = configs.resetHeader %7C%7C 'X-Rate-Limit-Reset'
+%5Bk%5D = configs%5Bk%5D %7C%7C defaults%5Bk%5D;%0A  %7D)
 ;%0A%0A 

refactor default configs