// limiter.js
// Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0

'use strict';

const path = require('path');

const { RateLimiterRedis } = require('rate-limiter-flexible');
const expressLimiter = require('express-limiter');

const { SiteService, SiteError } = require('../../lib/site-lib');

class LimiterService extends SiteService {

  constructor (dtp) {
    super(dtp, module.exports);
    this.config = require(path.resolve(dtp.config.root, 'config', 'limiter.js'));

    this.limiter = expressLimiter(this.dtp.app, this.dtp.redis);
    this.handlers = {
      lookup: this.limiterLookup.bind(this),
      whitelist: this.limiterWhitelist.bind(this),
    };

    this.rateLimiters = { };
  }

  createMiddleware (config) {
    const options = {
      total: config.total,
      expire: config.expire,
      lookup: this.handlers.lookup,
      whitelist: this.handlers.whitelist,
      onRateLimited: async (req, res, next) => {
        this.emit('limiter:block', req);
        next(new SiteError(config.status || 429, config.message || 'Rate limit exceeded'));
      },
    };
    const middleware = this.limiter(options);
    return async (req, res, next) => {
      return middleware(req, res, next);
    };
  }

  limiterLookup (req, res, options, next) {
    if (req.user) {
      options.lookup = 'user._id';  // req.user._id, populated by PassportJS session
    } else {
      options.lookup = 'ip';        // req.ip, populated by ExpressJS with trust_proxy=1
    }
    return next();
  }
  
  limiterWhitelist (req) {
    return req.user && req.user.flags.isAdmin;
  }

  createRateLimiter (id, config) {
    if (this.rateLimiters[id]) {
      return this.rateLimiters[id];
    }
    config = Object.assign({
      storeClient: this.dtp.redis,
    }, config);
    this.rateLimiters[id] = new RateLimiterRedis(config);
    return this.rateLimiters[id];
  }
}

module.exports = {
  slug: 'limiter',
  name: 'limiter',
  create: (dtp) => { return new LimiterService(dtp); },
};