const logger = require('@overleaf/logger')
const Settings = require('@overleaf/settings')
const { plainTextResponse } = require('../../infrastructure/Response')

function renderJSONError(res, message, info = {}) {
  if (info.message) {
    logger.warn(
      info,
      `http error info shouldn't contain a 'message' field, will be overridden`
    )
  }
  if (message != null) {
    res.json({ ...info, message })
  } else {
    res.json(info)
  }
}

function handleGeneric500Error(req, res, statusCode, message) {
  res.status(statusCode)
  switch (req.accepts(['html', 'json'])) {
    case 'html':
      return res.render('general/500', { title: 'Server Error' })
    case 'json':
      return renderJSONError(res, message)
    default:
      return plainTextResponse(res, 'internal server error')
  }
}

function handleGeneric400Error(req, res, statusCode, message, info = {}) {
  res.status(statusCode)
  switch (req.accepts(['html', 'json'])) {
    case 'html':
      return res.render('general/400', {
        title: 'Client Error',
        message,
      })
    case 'json':
      return renderJSONError(res, message, info)
    default:
      return plainTextResponse(res, 'client error')
  }
}

let HttpErrorHandler
module.exports = HttpErrorHandler = {
  handleErrorByStatusCode(req, res, err, statusCode) {
    const is400Error = statusCode >= 400 && statusCode < 500
    const is500Error = statusCode >= 500 && statusCode < 600

    req.logger.addFields({ err })
    if (is400Error) {
      req.logger.setLevel('warn')
    } else if (is500Error) {
      req.logger.setLevel('error')
    }

    if (statusCode === 403) {
      HttpErrorHandler.forbidden(req, res)
    } else if (statusCode === 404) {
      HttpErrorHandler.notFound(req, res)
    } else if (statusCode === 409) {
      HttpErrorHandler.conflict(req, res, '')
    } else if (statusCode === 422) {
      HttpErrorHandler.unprocessableEntity(req, res)
    } else if (is400Error) {
      handleGeneric400Error(req, res, statusCode)
    } else if (is500Error) {
      handleGeneric500Error(req, res, statusCode)
    } else {
      res.sendStatus(500)
    }
  },

  badRequest(req, res, message, info = {}) {
    handleGeneric400Error(req, res, 400, message, info)
  },

  conflict(req, res, message, info = {}) {
    res.status(409)
    switch (req.accepts(['html', 'json'])) {
      case 'html':
        return res.render('general/400', {
          title: 'Client Error',
          message,
        })
      case 'json':
        return renderJSONError(res, message, info)
      default:
        return plainTextResponse(res, 'conflict')
    }
  },

  forbidden(req, res, message = 'restricted', info = {}) {
    res.status(403)
    switch (req.accepts(['html', 'json'])) {
      case 'html':
        return res.render('user/restricted', { title: 'restricted' })
      case 'json':
        return renderJSONError(res, message, info)
      default:
        return plainTextResponse(res, 'restricted')
    }
  },

  notFound(req, res, message = 'not found', info = {}) {
    res.status(404)
    switch (req.accepts(['html', 'json'])) {
      case 'html':
        return res.render('general/404', { title: 'page_not_found' })
      case 'json':
        return renderJSONError(res, message, info)
      default:
        return plainTextResponse(res, 'not found')
    }
  },

  unprocessableEntity(req, res, message = 'unprocessable entity', info = {}) {
    res.status(422)
    switch (req.accepts(['html', 'json'])) {
      case 'html':
        return res.render('general/400', {
          title: 'Client Error',
          message,
        })
      case 'json':
        return renderJSONError(res, message, info)
      default:
        return plainTextResponse(res, 'unprocessable entity')
    }
  },

  legacyInternal(req, res, message, err) {
    req.logger.addFields({ err })
    req.logger.setLevel('error')
    handleGeneric500Error(req, res, 500, message)
  },

  maintenance(req, res) {
    // load balancer health checks require a success response for /
    if (req.url === '/') {
      res.status(200)
    } else {
      res.status(503)
    }
    let message = `${Settings.appName} is currently down for maintenance.`
    if (Settings.statusPageUrl) {
      message += ` Please check https://${Settings.statusPageUrl} for updates.`
    }
    switch (req.accepts(['html', 'json'])) {
      case 'html':
        return res.render('general/closed', { title: 'maintenance' })
      case 'json':
        return renderJSONError(res, message, {})
      default:
        return plainTextResponse(res, message)
    }
  },
}
