text: const cds = require('../../../cds')
  const LOG = cds.log('rest')
  
  const createHandler = require('./handlers/create')
  const readHandler = require('./handlers/read')
  const updateHandler = require('./handlers/update')
  const deleteHandler = require('./handlers/delete')
  const operationHandler = require('./handlers/operation')
  
  const { contentTypeCheck } = require('./utils/header-checks')
  const parse = require('./utils/parse-url')
  const { base64toBuffer } = require('./utils/binary')
  
  const { UNAUTHORIZED, FORBIDDEN, getRequiresAsArray } = require('../../../common/utils/auth')
  
  const PPP = { POST: 1, PUT: 1, PATCH: 1 }
  
  const i18n = require('../../../common/i18n')
  const { normalizeError } = require('../../../common/error/frontend')
  
  const _getErrorForLogging = obj => {
    // Error.message and Error.stack are not enumerable
    const toLog = Object.assign({ message: obj.message, stack: obj.stack }, obj)
  
    // replace messages in toLog with developer texts (i.e., undefined locale)
    toLog.message = i18n(obj.message || obj.code, undefined, obj.args) || obj.message
    if (obj.details) {
      toLog.details = []
      for (const d of obj.details) {
        toLog.details.push(Object.assign({}, d, { message: i18n(d.message || d.code, undefined, d.args) || d.message }))
      }
    }
  
    return toLog
  }
  
  class Rest {
    constructor(cdsService) {
      this._cdsService = cdsService
      this._createRouter()
      this._addDispatcher()
    }
  
    get _express() {
      const express = require('express')
      Object.defineProperty(this, '_express', { value: express })
      return express
    }
  
    _createRouter() {
      this.router = this._express.Router()
      this.router.use(this._express.json())
    }
  
    _addDispatcher() {
      const service = this._cdsService
  
      const create = createHandler(service)
      const read = readHandler(service)
      const update = updateHandler(service)
      const deleet = deleteHandler(service)
      const operation = operationHandler(service)
  
      const requires = getRequiresAsArray(service.definition)
  
      /*
       * pre handler stuff
       */
      this.router.use('/', (req, res, next) => {
        // check @requires as soon as possible (DoS)
        if (req.path !== '/' && requires.length > 0 && !requires.some(r => req.user.is(r))) {
          // > unauthorized or forbidden?
          if (req.user._is_anonymous) {
            if (req.user._challenges) res.set('WWW-Authenticate', req.user._challenges.join(';'))
            return res.status(401).json(UNAUTHORIZED)
          }
          return res.status(403).json(FORBIDDEN)
        }
  
        // content-type check, parse url, and base64 to buffer
        try {
          if (PPP[req.method]) contentTypeCheck(req)
          req._parsed = parse[req.method](service, req)
          base64toBuffer(req.body, req._parsed.segments[0])
        } catch (e) {
          return next(e)
        }
  
        next()
      })
  
      // POST
      this.router.post('/*', (req, res, next) => {
        // CREATE or custom operation?
        if (req._parsed.event === 'CREATE') {
          create(req, res, next)
        } else {
          operation(req, res, next)
        }
      })
  
      // GET
      this.router.get('/*', (req, res, next) => {
        // READ or custom operation?
        if (req._parsed.event === 'READ') {
          read(req, res, next)
        } else {
          operation(req, res, next)
        }
      })
  
      // PUT, PATCH, DELETE
      this.router.put('/*', update)
      this.router.patch('/*', update)
      this.router.delete('/*', deleet)
  
      /*
       * error handling (the express way)
       */
      this.router.use((err, req, res, next) => {
        // log the error (4xx -> warn)
        const numericCode = err.statusCode || Number(err.code)
        if (numericCode >= 400 && numericCode < 500) {
          LOG._warn && LOG.warn(_getErrorForLogging(err))
        } else {
          LOG._error && LOG.error(_getErrorForLogging(err))
        }
  
        const { error, statusCode } = normalizeError(err, req)
  
        if (res.statusCode === 200) {
          // > i.e., not set in custom handler
          res.status(statusCode)
        }
        res.send({ error })
      })
    }
  }
  
  module.exports = Rest
  