var error = require("http-error");

var MAX_EXPIRE_INTERVAL = 5000;

/**
 * In memory rate limiter as connect middleware
 */
module.exports = ratelimit;

function ratelimit(key, duration, max) {
    var requests = Object.create(null); // in case there handles like 'constructor'
    var rootKey = "params"; 
    if (/^req\./.test(key)) {
        rootKey = null;
        key = key.replace(/^req\./, "");
    } 
    
    setInterval(function() {
        Object.keys(requests).forEach(expireRequests);
    }, Math.min(duration * 0.75, MAX_EXPIRE_INTERVAL));
    
    function expireRequests(handle)  {
        var requestsForHandle = requests[handle]; 
        var totalToSplice = 0;
        var expireTime = Date.now() - duration;  
        /* Requests are already sorted by date as they are appended, so we just loop 
            until we find one that shouldn't have expired and splice them from the list */
        for (totalToSplice = 0; totalToSplice < requestsForHandle.length; totalToSplice++)  {
            if (requestsForHandle[totalToSplice] >= expireTime) break;
        }
        requests[handle].splice(0, totalToSplice);
        if (requests[handle].length == 0) {
            delete requests[handle];
        }
        
        return true;
    }
    
    // Returns a deep value from an object. E.g. resolveValue({user: {id: 5}}, "user.id") === 5
    function resolveValue(obj, path) {
        return path.split('.').reduce(function(prev, curr) {
            return prev ? prev[curr] : undefined;
        }, obj);
    }
    
    return function(req, res, next) {
        var root = rootKey ? req[rootKey] : req;
        var handle = resolveValue(root, key);

        requests[handle] = requests[handle] || [];
        if (requests[handle].length >= max) {
            var err = new error.TooManyRequests("Rate limit exceeded");
            err.retryIn = Math.min(duration, 5000);
            return next(err);
        }
        
        requests[handle].push(Date.now());
        return next();
    };
}
