'use strict';

var _lodash = require('lodash');

var _lodash2 = _interopRequireDefault(_lodash);

var _bodyParser = require('body-parser');

var _bodyParser2 = _interopRequireDefault(_bodyParser);

var _express = require('express');

var _queryString = require('query-string');

var _queryString2 = _interopRequireDefault(_queryString);

var _utilRoles = require('@botpress/util-roles');

var _anonymous = require('./anonymous');

var _anonymous2 = _interopRequireDefault(_anonymous);

var _nonSecured = require('./non-secured');

var _nonSecured2 = _interopRequireDefault(_nonSecured);

var _secured = require('./secured');

var _secured2 = _interopRequireDefault(_secured);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

const routersConditions = {};
const routers = {};

const API_RE = /\/api\/(botpress-[^\/]+).*$/i;

const maybeApply = (name, fn) => {
  return (req, res, next) => {
    const router = req.originalUrl.match(API_RE);
    if (!router) {
      return fn(req, res, next);
    }

    if (!routersConditions[router[1]]) {
      return fn(req, res, next);
    }

    const condition = routersConditions[router[1]][name];
    if (condition === false) {
      next();
    } else if (typeof condition === 'function' && condition(req) === false) {
      next();
    } else {
      return fn(req, res, next);
    }
  };
};

module.exports = bp => {
  const _authenticationMiddleware = (() => {
    var _ref = _asyncToGenerator(function* (req, res, next) {
      res.maybeSendRequireLogin = function () {
        if (!bp.botfile.login.enabled) {
          res.status(400).send({
            message: 'Login must be turned on for this API method'
          });

          return true;
        } else {
          return false;
        }
      };

      if (!bp.botfile.login.enabled) {
        return next();
      }

      const user = yield bp.security.authenticate(req.headers.authorization);
      if (!!user) {
        req.user = user;
        next();
      } else {
        res.status(401).location('/login').end();
      }
    });

    return function _authenticationMiddleware(_x, _x2, _x3) {
      return _ref.apply(this, arguments);
    };
  })();

  const getCloudRoles = (() => {
    var _ref2 = _asyncToGenerator(function* (req) {
      const { useCloud, enabled } = bp.botfile.login;
      const isUsingCloud = !!useCloud && (yield bp.cloud.isPaired());
      if (!isUsingCloud || !enabled) {
        // No cloud, skip check
        return false;
      }
      const { roles } = req.user || {};
      if (!roles) {
        return null;
      }
      return bp.cloud.getUserRoles(roles);
    });

    return function getCloudRoles(_x4) {
      return _ref2.apply(this, arguments);
    };
  })();

  const installProtector = app => {
    app.secure = (operation, resource) => {
      const wrap = method => (route, ...handlers) => {
        const secureMiddleware = (() => {
          var _ref3 = _asyncToGenerator(function* (req, res, next) {
            try {
              const roles = yield getCloudRoles(req);

              if (roles === false) {
                return next();
              }

              if (!(0, _utilRoles.checkMultipleRoles)(roles, operation, resource)) {
                return res.sendStatus(403); // Forbidden
              }

              return next();
            } catch (err) {
              return res.status(500).send({ message: err.message });
            }
          });

          return function secureMiddleware(_x5, _x6, _x7) {
            return _ref3.apply(this, arguments);
          };
        })();

        return app[method](route, secureMiddleware, ...handlers);
      };

      return {
        get: wrap('get'),
        post: wrap('post'),
        put: wrap('put'),
        patch: wrap('patch'),
        delete: wrap('delete')
      };
    };
  };

  const installRouter = app => {
    /**
     * Creates an HTTP [Express Router]{@link https://expressjs.com/} that is protected by authentication
     * The router routes are available at "http://bot_url/api/:name"
     * Where `name` is a string starting with `botpress-`
     * @func
     * @alias getRouter
     * @memberOf! Botpress
     * @param  {String} name       The name of the router. Must start with `botpress-`
     * @param  {object=} [conditions] See examples. Conditionally disables built-in Botpress middlewares.
     * @example
     * const securedRouter = bp.getRouter('botpress-custom')
     * const publicRouter = bp.getRouter('botpress-custom', { auth: false })
     *
     * // Conditions can also be used like below
     * const conditions = { 'auth': req => !/\/webhook/i.test(req.originalUrl) }
     * const conditionalAuthentication = bp.getRouter('botpress-custom', conditions)
     */
    bp.getRouter = (name, conditions) => {
      if (!/^botpress-/.test(name)) {
        throw new Error(`The name of a router must start with 'botpress-'. Received: ${name}`);
      }

      if (!routers[name]) {
        const router = (0, _express.Router)();
        routers[name] = router;
        app.use(`/api/${name}/`, router);
      }

      if (conditions) {
        routersConditions[name] = Object.assign(routersConditions[name] || {}, conditions);
      }

      installProtector(routers[name]);
      return routers[name];
    };

    const links = {};

    /**
     * Creates a short link for a specific API route, making it easier to share, more verbose and elegant.
     * Short links are available as `http://bot_url/s/{name}`
     * @alias  createShortlink
     * @param  {String} name        Unique, url-friendly name of the short link
     * @param  {String} destination The original route to redirect to
     * @param  {Object} params      Query parameters to pass the route. Will be serialized.
     * @memberof! Botpress
     * @example
     *   const config = {
    botName: 'Superbot',
    botConvoDescription: "Tell me something!",
    backgroundColor: '#ffffff'
    }
    // Visiting "http://bot_url/s/chat" will display the webchat in fullscreen
    bp.createShortlink('chat', '/lite', {
    m: 'channel-web',
    v: 'fullscreen',
    options: JSON.stringify({ config: config })
    })
     */
    bp.createShortlink = (name, destination, params) => {
      name = name.toLowerCase();

      if (links[name]) {
        throw new Error(`There's already a shortlink named "${name}"`);
      }

      const q = params ? '?' + _queryString2.default.stringify(params) : '';
      links[name] = `${destination}${q}`;
    };

    app.get(`/s/:name`, (req, res) => {
      const name = req.params.name.toLowerCase();
      const query = _queryString2.default.stringify(req.query);

      if (!links[name]) {
        return res.status(404).send({ error: `Shortlink "${name}" not registered` });
      }

      if (query) {
        const hasQuery = /\?/g.test(links[name]);
        links[name] = links[name].concat(`${hasQuery ? '&' : '?'}${query}`);
      }

      res.redirect(links[name]);
    });
  };

  const installMaybeUse = app => {
    app.maybeUse = function () {
      if (arguments.length === 3) {
        app.use(arguments[0], maybeApply(arguments[1], arguments[2]));
      } else if (arguments.length === 2) {
        app.use(maybeApply(arguments[0], arguments[1]));
      }
    };
  };

  const install = (() => {
    var _ref4 = _asyncToGenerator(function* (app) {
      installRouter(app);
      installProtector(app);
      installMaybeUse(app);

      app.maybeUse('bodyParser.json', _bodyParser2.default.json({ limit: _lodash2.default.get(bp.botfile, 'api.bodyMaxSize') || '1mb' }));
      app.maybeUse('bodyParser.urlencoded', _bodyParser2.default.urlencoded({ extended: true }));

      (0, _anonymous2.default)(bp, app);

      app.use('/api/*', maybeApply('auth', _authenticationMiddleware));

      (0, _nonSecured2.default)(bp, app);
      (0, _secured2.default)(bp, app);
    });

    return function install(_x8) {
      return _ref4.apply(this, arguments);
    };
  })();

  return { install };
};
//# sourceMappingURL=api.js.map