#!/usr/bin/env node
import { json } from 'body-parser'
import * as cors from 'cors'
import * as express from 'express'
import { ensureDir, writeFile } from 'fs-extra'
import * as http from 'http'
import * as createError from 'http-errors'
import * as os from 'os'
import { buildBundle } from '../buildBundle'
import { PATH_PUBLIC, PATH_UNIT } from '../path'
import { PORT } from './port'
import compression = require('compression')
import path = require('path')

process.on('uncaughtException', function (err) {
  console.error(new Date().toUTCString() + ' uncaughtException:', err.message)
  console.error(err.stack)
  process.exit(1)
})

const LOCAL_IP_ADDRESS = Object.values(os.networkInterfaces()).reduce(
  (r, list) =>
    r.concat(
      list.reduce(
        (rr, i) =>
          rr.concat((i.family === 'IPv4' && !i.internal && i.address) || []),
        []
      )
    ),
  []
)[0]

export let server: http.Server | null = null

const app = express()

app.use((req, res, next) => {
  const { url, method } = req
  console.log(method, url)
  next()
})

const corsOptions = {
  origin: '*',
  credentials: true,
  optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
}

app.set('port', PORT)

app.use(cors(corsOptions))
app.use(compression())
app.use(express.static(PATH_PUBLIC))
app.use(json())
app.use(async (req, res, next) => {
  if (req.method === 'POST') {
    if (req.path === '/') {
      const userPath = path.join(PATH_PUBLIC, '_')
      const appPath = path.join(userPath, req.path)
      const bundlePath = path.join(appPath, 'bundle.json')

      let bundleJson: string

      try {
        bundleJson = JSON.stringify(req.body, null, 2)
      } catch (err) {
        return res.sendStatus(400)
      }

      await ensureDir(appPath)
      await writeFile(bundlePath, bundleJson)
      await buildBundle(PATH_UNIT, bundlePath, appPath)

      res.send()
    }
  }

  next()
})
app.use(function (req, res, next) {
  next(createError(404))
})

function onListening() {
  console.log(`http://localhost:${PORT}`)
  console.log(`http://${LOCAL_IP_ADDRESS}:${PORT}`)
}

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error
  }

  const bind = typeof PORT === 'string' ? 'Pipe ' + PORT : 'Port ' + PORT

  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges')
      process.exit(1)
      break
    case 'EADDRINUSE':
      console.error(bind + ' is already in use')
      process.exit(1)
      break
    default:
      throw error
  }
}

server = app.listen(PORT, onListening)

server.on('error', onError)
