import path from 'path'
import fs from 'fs/promises'
import socket from 'socket.io-serveronly'
import { Flaska, FileResponse } from 'flaska'
import coremonitor from './core/coremonitor.mjs'
import * as routes from './core/routes.mjs'

import onConnection from './routerio.mjs'

export function run(http, port, orgCtx) {
  let localUtil = new orgCtx.sc.Util(import.meta.url)
  const staticRoot = localUtil.getPathFromRoot('../public')

  const flaska = new Flaska({
    log: orgCtx.log,
  }, http)

  flaska.before(function(ctx) {
    ctx.core = orgCtx.core
    ctx.state.started = new Date().getTime()
  })

  flaska.onreqerror(function(err, ctx) {
    if (ctx.aborted) {
      flaska.log.info('Request aborted')
    } else {
      flaska.log.error(err)
    }
  })

  flaska.after(function(ctx) {
    let ended = new Date().getTime()
    var requestTime = ended - ctx.state.started

    let status = ''
    let level = 'debug'
    if (ctx.status >= 400) {
      status = ctx.status + ' '
      level = 'warn'
    }
    if (ctx.status >= 500) {
      level = 'error'
    }

    ctx.log[level]({
      duration: requestTime,
      status: ctx.status,
    }, `<-- ${status}${ctx.method} ${ctx.url}`)
  })

  flaska.on404(function(ctx) {
    if (ctx.method !== 'GET' && ctx.method !== 'HEAD') {
      ctx.status = 404
      ctx.body = {
        status: 404,
        message: 'Not Found',
      }
      return
    }

    let file = path.resolve(path.join(staticRoot, ctx.url === '/' ? 'index.html' : ctx.url))
    if (!file.startsWith(staticRoot)) {
      ctx.status = 404
      ctx.body = 'HTTP 404 Error'
      return
    }

    return fs.stat(file).then(function(stat) {
      if (!stat) {
        ctx.status = 404
        ctx.body = 'HTTP 404 Error'
        return
      }
      ctx.body = new FileResponse(file, stat)
    })
  })

  flaska.post('/update/:name', routes.updateApp)

  return flaska.listenAsync(port).then(function() {
    orgCtx.log.info('Server is listening on port ' + port)

    const io = new socket(flaska.server)
    io.on('connection', onConnection.bind(this, io, orgCtx))

    coremonitor(io, orgCtx)
  })
}