import { NextApiRequest, NextApiResponse } from 'next'
import { promisify } from 'util'
import { pipeline } from 'stream'
import { getFunctionUrl } from 'common/api'
import fetch, { Headers, Response } from 'node-fetch'

function getProxiedRequestHeaders(req: NextApiRequest, whitelist: string[]) {
  const result = new Headers()
  for (const name of whitelist) {
    const v = req.headers[name.toLowerCase()]
    if (Array.isArray(v)) {
      for (const vv of v) {
        result.append(name, vv)
      }
    } else if (v != null) {
      result.append(name, v)
    }
  }
  result.append('X-Forwarded-For', req.socket.remoteAddress || '')
  result.append('Via', 'Vercel public API')
  return result
}

function getProxiedResponseHeaders(res: Response, whitelist: string[]) {
  const result: { [k: string]: string } = {}
  for (const name of whitelist) {
    const v = res.headers.get(name)
    if (v != null) {
      result[name] = v
    }
  }
  return result
}

export const fetchBackend = (req: NextApiRequest, name: string) => {
  const url = getFunctionUrl(name)
  const headers = getProxiedRequestHeaders(req, [
    'Authorization',
    'Content-Length',
    'Content-Type',
    'Origin',
  ])
  const hasBody = req.method != 'HEAD' && req.method != 'GET'
  const body = req.body ? JSON.stringify(req.body) : req
  const opts = {
    headers,
    method: req.method,
    body: hasBody ? body : undefined,
  }
  return fetch(url, opts)
}

export const forwardResponse = async (
  res: NextApiResponse,
  backendRes: Response
) => {
  const headers = getProxiedResponseHeaders(backendRes, [
    'Access-Control-Allow-Origin',
    'Content-Type',
    'Cache-Control',
    'ETag',
    'Vary',
  ])
  res.writeHead(backendRes.status, headers)
  if (backendRes.body != null) {
    return await promisify(pipeline)(backendRes.body, res)
  }
}