const http = require('http')

module.exports = function serverless(app) {
  return async function handler(event) {
    const method = String(event.httpMethod || event.method || 'GET').toUpperCase()
    const path = event.path || '/'
    const headers = event.headers || {}
    const qs = event.queryStringParameters || {}
    const qsStr = Object.keys(qs).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(qs[k])).join('&')
    const url = path + (qsStr ? '?' + qsStr : '')

    let rawBody = event.body || ''
    if (event.isBase64Encoded && rawBody) {
      rawBody = Buffer.from(rawBody, 'base64')
    } else if (typeof rawBody === 'string') {
      rawBody = Buffer.from(rawBody, 'utf8')
    }

    return new Promise((resolve) => {
      const req = new http.IncomingMessage()
      req.method = method
      req.url = url
      req.headers = {}
      Object.keys(headers).forEach(k => { req.headers[k.toLowerCase()] = headers[k] })
      if (event.requestContext && event.requestContext.sourceIp) {
        req.headers['x-forwarded-for'] = req.headers['x-forwarded-for'] || event.requestContext.sourceIp
      }
      if (rawBody.length > 0) {
        req.headers['content-length'] = String(rawBody.length)
      }

      const res = new http.ServerResponse(req)
      let body = ''
      const resHeaders = {}

      res.writeHead = function (statusCode, reasonOrHeaders, maybeHeaders) {
        res.statusCode = statusCode
        const h = maybeHeaders || (typeof reasonOrHeaders === 'object' ? reasonOrHeaders : {})
        Object.assign(resHeaders, h)
      }

      const originalSetHeader = res.setHeader.bind(res)
      res.setHeader = function (name, value) {
        resHeaders[name.toLowerCase()] = value
        originalSetHeader(name, value)
      }

      res.end = function (chunk) {
        if (chunk) body += chunk
        resolve({
          isBase64Encoded: false,
          statusCode: res.statusCode || 200,
          headers: Object.assign({
            'content-type': 'application/json; charset=utf-8'
          }, resHeaders),
          body
        })
      }

      res.write = function (chunk) { body += chunk }

      if (rawBody.length > 0) {
        req.push(rawBody)
      }
      req.push(null)

      app(req, res)
    })
  }
}
