import redis from '../services/redis';
import * as crypto from 'crypto';

// Cookie interface
interface Ithrottle {
  id: string;
  requests: number[];
  ip: string;
}

// Cookie based middleware
export default async ({ip, cookies}, res, next): Promise<void> => {
  let throttle: Ithrottle = cookies._throttle;

  // If request cookie does not exists reject request
  if (!throttle) {
    res.status(400);
    res.send('Unauthorised access.');
  }

  // If cookie id does not exists assume new user
  if (!throttle.id) {
    throttle.id = `throttle_${crypto.randomBytes(20).toString('hex')}`;
    throttle.requests = [];
    throttle.ip = ip;
  }

  // Check redis for existing session
  let session = redis.hgetall(throttle.id);

  //If no requests have been made store session
  if (!session) {
    redis.hmset(throttle.id, 'id', throttle.id, 'requests', [], 'ip', ip);
    session = redis.hgetall(throttle.id);
  }

  // Get existing requests from session
  const requests = session.requests;

  // If cookie request length and session length don't match reject request
  if (throttle.requests.length !== requests.length) {
    res.status(400);
    res.send('Unauthorised access.');
  }

  // Add current request to session
  requests.push((new Date()).getTime());
  redis.hset(throttle.id, 'requests', requests);

  // Store cookie in response to user
  res.cookie.throttle = {id: throttle.id, requests, ip};

  // Reject request if user has reaches 50 requests in 10 seconds
  if (requests.length >= 50) {
    res.status(400);
    res.send('Unauthorised access.');
  }

  // If number of requests exceeds 5 in the last 10 seconds start throttling
  if (requests.length >= 5) {
    setTimeout(() => {
      next();
      }, (requests.length - 5) * 1000);
  }

  // Expire session after 10 seconds
  redis.expire(throttle.id, 10);

  // Move to next step in express
  next();

};