const fs = require('fs')
const path = require('path')

const chalk = require('chalk')
const pify = require('pify')

const createStaticServer = require('./create-static-server')

const fsStat = pify(fs.stat)
const DEFAULT_PORT = 9080

const onResponse = (request, response) => {
  if (response.statusCode >= 400) {
    console.log(chalk`{gray '-->'} {red ${response.statusCode}} ${request.url}`)
  } else if (response.statusCode >= 200 && response.statusCode < 300) {
    console.log(chalk`{gray '-->'} {green ${response.statusCode}} ${request.url}`)
  } else {
    console.log(chalk`{gray '-->'} {green.dim ${response.statusCode}} ${request.url}`)
  }
}
const onRequest = (request, response) => {
  console.log(chalk`{gray '<--'} {blue [${request.method}]} ${request.url}`)
  response.on('finish', () => onResponse(request, response))
}

const startServer = ({ port, rootDirectory }) => {
  const server = createStaticServer(rootDirectory)

  server.on('request', onRequest)

  server.listen(port, () => {
    console.log(`Running at http://localhost:${port}`)
  })
}

const parsePort = (portString) => {
  const port = Number(portString)
  if (!Number.isInteger(port)) {
    throw new Error(`Port '${portString}' is invalid; must be an integer`)
  } else if (port < 0 || port > 65535) {
    throw new Error(`Port '${portString}' is out of range; must be between 0 and 65535 inclusive`)
  }
  return port
}

const parseDirectoryArgument = async (pathString) => {
  const resolvedPath = path.resolve(pathString)
  const directoryStats = await fsStat(resolvedPath)
  if (!directoryStats.isDirectory()) {
    throw new Error(`Invalid path '${pathString}'; must be a directory`)
  }
  return resolvedPath
}

const main = async () => {
  const args = process.argv.slice(2)

  const options = {
    port: process.env.port || DEFAULT_PORT,
    rootDirectory: path.resolve('.'),
  }

  while (args.length) {
    if ((/^(--port|-p)$/u).test(args[0])) {
      if (args[1] === undefined) {
        throw new Error('Missing port argument')
      }
      options.port = parsePort(args[1])
      args.splice(0, 2)
    } else {
      options.rootDirectory = await parseDirectoryArgument(args[0])
      args.splice(0, 1)
    }
  }

  startServer(options)
}

main()
  .catch((error) => {
    console.error(error)
    process.exit(1)
  })