text: import express from 'express'
  import path from 'path'
  import {merge} from 'ramda'
  import compression from 'compression'
  import appConfig from './config'
  import crypto from 'crypto'
  import Promise from 'bluebird'
  import bodyParser from 'body-parser'
  import DB from './db'
  import CourseRoutes from './routes/course'
  import ApiRoutes from './routes/api'
  import errorLoggerRoutes from './routes/errorLogger'
  import {appState} from './store/lukkariStore'
  import forceSSL from 'express-force-ssl'
  import {renderFullPage} from './pages/initPage'
  import {frameOptions, hsts} from './httpUtils'
  import Logger from './logger'
  import UniversalRouter from 'universal-router'
  import {routes} from './routes'
  
  const fs = Promise.promisifyAll(require('fs'))
  
  process.on('unhandledRejection', (reason, p) => {
    Logger.error('Unhandled Rejection at: Promise', p, 'reason:', reason)
  })
  
  const server = express()
  
  server.use(compression({threshold: 512}))
  server.use(hsts)
  server.use(bodyParser.json())
  server.use(bodyParser.urlencoded({extended: true}))
  server.disable('x-powered-by')
  server.use('/course', CourseRoutes)
  server.use('/api', ApiRoutes)
  
  server.use('/favicon.png', express.static(`${__dirname}/img/favicon.png`))
  server.use('/favicon.ico', express.static(`${__dirname}/img/favicon.png`))
  server.use('/github.png', express.static(`${__dirname}/img/github.png`))
  server.use('/spinner.gif', express.static(`${__dirname}/img/spinner.gif`))
  
  const cssFilePath = path.resolve(`${__dirname}/../.generated/styles.css`)
  const bundleJsFilePath = path.resolve(`${__dirname}/../.generated/bundle.js`)
  
  if (process.env.NODE_ENV !== 'production') {
    server.use('/test/', express.static(path.resolve(`${__dirname}'/../test`)))
    server.use('/node_modules/', express.static(path.resolve(`${__dirname}'/../node_modules`)))
  } else {
    server.use(forceSSL)
    server.use(frameOptions)
    server.set('forceSSLOptions', {
      enable301Redirects: true,
      trustXFPHeader: true,
      httpsPort: 443,
      sslRequiredMessage: 'SSL Required.'
    })
  }
  
  async function checksumPromise(filePath) {
    const fileContent = await fs.readFileAsync(filePath)
    return await crypto.createHash('md5').update(fileContent).digest('hex')
  }
  
  function setHeaders(req, res, checksum, filePath) {
    if (req.params.checksum === checksum) {
      const twoHoursInSeconds = 60 * 60 * 2
      res.setHeader('Cache-Control', `must-revalidate, max-age=${twoHoursInSeconds}`)
      res.setHeader('ETag', checksum)
      res.sendFile(filePath)
    } else {
      res.status(404).send()
    }
  }
  
  const serveStaticResource = filePath => (req, res, next) =>
    checksumPromise(filePath)
      .then(checksum => setHeaders(req, res, checksum, filePath))
      .catch(next)
  
  server.get('/static/:checksum/styles.css', serveStaticResource(cssFilePath))
  server.get('/static/:checksum/bundle.js', serveStaticResource(bundleJsFilePath))
  
  function buildInitialState(displayName) {
    const pageState = () => {
      switch (displayName) {
        case 'LukkariPage':
          return {}
        case 'CatalogPage':
          return {}
        case 'NotFoundPage':
          return {}
        default:
          return null
      }
    }
  
    return {
      ...pageState, ...{
        selectedCourses: [],
        searchResults: [],
        currentDate: new Date(),
        isModalOpen: false,
        selectedIndex: -1,
        waitingAjax: false,
        departmentCourses: [],
        department: 'TITE'
      }
    }
  }
  
  server.get('/.well-known/acme-challenge/:content', (req, res) => res.send(appConfig.letsEncryptResponse))
  server.use('/errors', errorLoggerRoutes)
  
  const router = new UniversalRouter(routes)
  
  server.get('*', (req, res) => {
    router.resolve({pathname: req.path, query: req.query})
      .then(routeData => Promise
        .all([checksumPromise(cssFilePath), checksumPromise(bundleJsFilePath)])
        .then(([cssChecksum, bundleJsChecksum]) => {
          const initialState = merge(appState.currentState, buildInitialState(routeData.path))
          return Promise.resolve(renderFullPage(
            routeData.component,
            initialState,
            {cssChecksum, bundleJsChecksum}
          ))
            .then(page => res.send(page))
        })
        .catch(err => {
          res.status(500).send('Server error')
          Logger.error('Server error', err.stack)
        }))
  })
  
  export async function start(port) {
    const env = process.env.NODE_ENV ? process.env.NODE_ENV : 'development'
    const reportPages = () => {
      Logger.info(`Page available at http://localhost:${port} in ${env}`)
    }
    async function isDbInitialized(exists) {
      return exists ? null : DB.initializeDb()
    }
  
    const exists = await DB.isTableInitialized('course')
    await isDbInitialized(exists)
    await server.listen(port)
    await reportPages()
  }
  