// --- SETUP FASTIFY
global.config = require('./config')
var fastifyOpt = {
  ignoreTrailingSlash: true,
  maxParamLength: 1048576,
  bodyLimit: 1048576 * 15,
  jsonBodyLimit: 1048576 * 15,
  trustProxy: true
}
// if (config.env === 'production') { SSL handled through CF
//   fastifyOpt.https = {
//     http2: true,
//     key: require('fs').readFileSync('./fastify-wago.key'),
//     cert: require('fs').readFileSync('./fastify-wago.crt')
//   }
// }
const fastify = require('fastify')(fastifyOpt)

// --- GLOBAL MODULES
const Redis = require("ioredis")
global.RedisConnect = new Redis(config.redis)
const { Queue, Worker, QueueScheduler, QueueEvents } = require('bullmq')
const Profiler = require('./api/models/Profiler')
const discordBot = require('./discordBot')
const GameVersion = require('./api/models/GameVersion')
global.taskQueue = new Queue('taskQueue', { connection: RedisConnect })
global.taskQueueDiscordBot = new Queue('taskQueueDiscordBot', { connection: RedisConnect })
global.async = require('async')
global.axios = require('axios')
global.bluebird = require('bluebird')
global.commonRegex = require('./commonRegex')
global.elastic = require('./api/helpers/elasticsearch')
global.redis = new Redis(config.redis)
redis = require('./middlewares/decorateRedis')(redis)
global.redis2 = new Redis(config.redis2)
global.fs = require('fs').promises
global.mongoose = require('mongoose')
global.ENUM = require('./middlewares/enum')

// --- FASTIFY PLUGINS
fastify.register(require('fastify-cookie'))
fastify.register(require('fastify-compress'))
fastify.register(require('fastify-file-upload'), {
  limits: { fileSize: 1048576 * 15 },
})
fastify.register(require('fastify-websocket'), {
  options: { maxPayload: 1048576, perMessageDeflate: true }
})

// --- DECORATORS
fastify.decorate('enum', require('./middlewares/enum'))
fastify.decorateRequest('track', require('./middlewares/matomo'))
fastify.decorateRequest('trackError', require('./middlewares/matomoErrors'))
fastify.decorateReply('cache', require('./middlewares/cache'))
fastify.setErrorHandler(require('./middlewares/errors'))

// --- HOOKS & MIDDLEWARES
fastify.addHook('onRequest', async (req) => {
  await Profiler.startRequest(req)
})
fastify.addHook('onResponse', (req, res) => {
  Profiler.logEvent(req.profiler, 'Response', res.statusCode)
})
fastify.addHook('preValidation', require('./middlewares/rateLimit'))
fastify.addHook('preValidation', require('./middlewares/cors'))
fastify.addHook('preHandler', require('./middlewares/auth'))
fastify.addHook('preHandler', require('./middlewares/setDefaults'))
fastify.addHook('preHandler', require('./middlewares/analytics'))
fastify.addHook('preHandler', require('./middlewares/getRegion'))

// --- ROUTES
fastify.get('/ws', { websocket: true }, require('./api/services/websocket'))
fastify.get('/logout', (req, res) => { res.redirect('/auth/logout') })
fastify.get('/ping', (req, res) => { res.send({ pong: true, host: config.base_url, you: req.raw.ip }) })
fastify.get('/favicon.ico', (req, res) => { res.redirect('https://media.wago.io/favicon/favicon.ico') })
fastify.get('/loaderio-ea4a5150f4d42634b2499beaf72f04a9.txt', (req, res) => { res.send('loaderio-ea4a5150f4d42634b2499beaf72f04a9') })
fastify.register(require('./api/services/account'), { prefix: '/account' })
fastify.register(require('./api/services/admin'), { prefix: '/admin' })
fastify.register(require('./api/services/api'), { prefix: '/api' })
fastify.register(require('./api/services/api-v2'), { prefix: '/api/v2' })
fastify.register(require('./api/services/auth'), { prefix: '/auth' })
fastify.register(require('./api/services/comments'), { prefix: '/comments' })
fastify.register(require('./api/services/data'), { prefix: '/data' })
fastify.register(require('./api/services/html'), { prefix: '/html' }) // pre-render content, embeds; called by proxy
fastify.register(require('./api/services/import'), { prefix: '/import' })
fastify.register(require('./api/services/lookup'), { prefix: '/lookup' })
fastify.register(require('./api/services/search'), { prefix: '/search' })
fastify.register(require('./api/services/wago'), { prefix: '/wago' })
fastify.register(require('./api/services/webhooks'))

// --- START SERVER AND CONNECT TO MONGO
const startServer = async () => {
  try {
    await fastify.listen(config.port, '0.0.0.0')
    console.log(`Fastify listening on ${fastify.server.address().port}`)
    await mongoose.connect(config.db.uri, { useNewUrlParser: true, useCreateIndex: true, useFindAndModify: false, useUnifiedTopology: true })
    const models = await fs.readdir('./api/models')
    models.forEach((model) => {
      if (model.indexOf('.js') < 0) return
      model = model.split('.')[0]
      global[model] = require('./api/models/' + model)
    })
    global.Categories = require('../frontend/src/components/libs/categories2')

    const encodeDecodeAddons = await fs.readdir('./api/helpers/encode-decode')
    global.Addons = {}
    encodeDecodeAddons.forEach((addon) => {
      if (addon.indexOf('.js') < 0) return
      addon = addon.split('.')[0]
      global.Addons[addon] = require('./api/helpers/encode-decode/' + addon)
    })

    const profilerTasks = {}

    const runTask = require('./api/helpers/tasks')
    // runTask('ProcessAllCode')
    if (config.env === 'processing' || config.env === 'development' || config.env === 'staging' || require('os').hostname().match(/wago-processing/)) {
      // setup queues and workers
      new QueueScheduler('taskQueue', { connection: RedisConnect })
      const worker = new Worker('taskQueue', async (job) => {
        console.log('run task', job.name, job.data)
        await runTask(job.name, job.data, profilerTasks[job.id])
      }, {
        concurrency: 2,
        connection: RedisConnect,
        removeOnComplete: {
          age: 3600, // keep up to 1 hour
          count: 1000, // keep up to 100 jobs
        },
        removeOnFail: {
          age: 24 * 3600, // keep up to 24 hours
        }
      })
      worker.on('active', async (job) => {
        profilerTasks[job.id] = await Profiler.startTask(job)
      })
      worker.on('completed', async (job) => {
        if (profilerTasks[job.id]) {
          await Profiler.logEvent(profilerTasks[job.id], 'Complete', 200)
          delete profilerTasks[job.id]
        }
      })
      worker.on('failed', async (job) => {
        if (profilerTasks[job.id]) {
          await Profiler.logEvent(profilerTasks[job.id], 'Failed', 500)
          delete profilerTasks[job.id]
        }
      })
      worker.on('error', async (job) => {
        if (profilerTasks[job.id]) {
          await Profiler.logEvent(profilerTasks[job.id], 'Failed', 500)
          delete profilerTasks[job.id]
        }
      })
    }

    if (config.env === 'development' || config.env === 'staging' || require('os').hostname().match(/wago-processing-01$/)) {
      const cleanup = await taskQueue.getRepeatableJobs()
      for (let i = 0; i < cleanup.length; i++) {
        await taskQueue.removeRepeatableByKey(cleanup[i].key)
      }
      await taskQueue.add('CleanTaskQueue', null, { repeat: { cron: '*/10 * * * *' }, priority: 10 })
      // await taskQueue.add('UpdateWagoOfTheMoment', null, {repeat: {cron: '* * * * *'}, priority: 3})
      // await taskQueue.add('UpdateTwitchStatus', null, {repeat: {cron: '* * * * *'}, priority: 3})
      await taskQueue.add('UpdatePatreonAccounts', null, { repeat: { cron: '0 * * * *' }, priority: 3 })
      await taskQueue.add('UpdateTopLists', null, { repeat: { cron: '*/5 * * * *' }, priority: 3 })
      await taskQueue.add('UpdateValidCharacters', null, { repeat: { cron: '10 * * * *' }, priority: 3 })
      await taskQueue.add('UpdateGuildMembership', null, { repeat: { cron: '15 * * * *' }, priority: 3 })
      await taskQueue.add('UpdateSupportedAddons', null, { repeat: { cron: '45 * * * *' }, priority: 3 })
      await taskQueue.add('ComputeStatistics', null, { repeat: { cron: '0 * * * *' }, priority: 4 })
      await taskQueue.add('UpdateGameVersions', null, { repeat: { cron: '0 */4 * * *' }, priority: 4 })

      await taskQueue.add('SyncElastic', { table: 'User' }, { repeat: { cron: '0 10 5 * *' }, priority: 10 })
      await taskQueue.add('SyncElastic', { table: 'imports' }, { repeat: { cron: '0 10 15 * *' }, priority: 10 })
      await taskQueue.add('SyncElastic', { table: 'comments' }, { repeat: { cron: '0 10 25 * *' }, priority: 10 })

      // run once at startup (1 host only)
      await elastic.ensureIndexes(true)
      // await runTask('SyncElastic', { table: 'comments' })

      if (config.env === 'development' || require('os').hostname().match(/wago-processing-01$/)) {
        global.discordBot = require('./discordBot')
        discordBot.start()

        new QueueScheduler('taskQueueDiscordBot', { connection: RedisConnect })
        new Worker('taskQueueDiscordBot', async (job) => {
          await runTask(job.name, job.data, profilerTasks[job.id])
        }, {
          concurrency: 3,
          connection: RedisConnect
        })
      }
    }

    if (config.env === 'development') {
      // await GameVersion.updatePatches()
      // await taskQueue.add('ProcessAllCode')
      // await taskQueue.add('SyncElastic', { table: 'imports' })
    }
  } catch (err) {
    console.log('FASTIFY ERROR', err)
    process.exit(1)
  }
}
startServer()
