import { performance } from 'perf_hooks'
import { Flaska, QueryHandler, JsonHandler, HttpError } from 'flaska'

import TestRoutes from './test/routes.mjs'
import MediaRoutes from './media/routes.mjs'

import config from './config.mjs'

export default class Server {
  constructor(http, port, core, opts = {}) {
    Object.assign(this, opts)
    this.http = http
    this.port = port
    this.core = core

    this.jsonHandler = JsonHandler
    this.queryHandler = QueryHandler

    this.flaskOptions = {
      log: this.core.log,
    }

    this.routes = {
      test: new TestRoutes(),
      media: new MediaRoutes(),
    }
  }

  run() {
    // Create our server
    this.flaska = new Flaska(this.flaskOptions, this.http)
    
    // configure our server
    if (config.get('NODE_ENV') === 'development') {
      this.flaska.devMode()
    }
    
    this.flaska.before(function(ctx) {
      ctx.__started = performance.now()
      ctx.log = ctx.log.child({
        ip: ctx.req.headers['x-forwarded-for'] || ctx.req.connection.remoteAddress,
      })
    })

    let healthChecks = 0
    let healthCollectLimit = 60 * 60 * 12
    
    this.flaska.after(function(ctx) {
      let ended = performance.now() - ctx.__started
    
      let status = ''
      let level = 'info'
      if (ctx.status >= 400) {
        status = ctx.status + ' '
        level = 'warn'
      }
      if (ctx.status >= 500) {
        level = 'error'
      }

      if (ctx.url === '/health') {
        healthChecks++
        if (healthChecks >= healthCollectLimit) {
          ctx.log[level]({
            duration: Math.round(ended),
            status: ctx.status,
          }, `<-- ${status}${ctx.method} ${ctx.url} {has happened ${healthChecks} times}`)
          healthChecks = 0
        }
        return
      }
    
      ctx.log[level]({
        duration: Math.round(ended),
        status: ctx.status,
      }, `<-- ${status}${ctx.method} ${ctx.url}`)
    })
    
    this.flaska.onerror(function(err, ctx) {
      if (err.status && err.status >= 400 && err.status < 500) {
        if (err.body && err.body.request) {
          ctx.log.warn({ request: err.body.request}, err.message)
        } else {
          ctx.log.warn(err.message)
        }
      } else {
        ctx.log.error(err)
      }
      ctx.status = err.status || 500
    
      if (err instanceof HttpError) {
        ctx.body = err.body || {
          status: ctx.status,
          message: err.message,
        }
      } else {
        ctx.body = {
          status: ctx.status,
          message: err.message,
        }
      }
    })

    // Register our routes
    let keys = Object.keys(this.routes)
    for (let key of keys) {
      this.routes[key].register(this)
    }

    // Start listening

    return this.flaska.listenAsync(this.port).then(() => {
      this.core.log.info({ sites: Object.keys(config.get('sites')) }, `Server is listening on port ${this.port}, uploading to ${config.get('uploadFolder')} for following sites`)
    })
  }
}
