const db = require('../db')
const state = require('./state')
const newSettingsLoader = require('../new-settings-loader')
const logger = require('../logger')

db.connect({ direct: true }).then(sco => {
  sco.client.on('notification', data => {
    const parsedData = JSON.parse(data.payload)
    return reload(parsedData.operatorId)
  })
  return sco.none('LISTEN $1:name', 'reload')
}).catch(console.error)

db.connect({ direct: true }).then(sco => {
  sco.client.on('notification', data => {
    const parsedData = JSON.parse(data.payload)
    return machineAction(parsedData.action, parsedData.value)
  })
  return sco.none('LISTEN $1:name', 'machineAction')
}).catch(console.error)

function machineAction (type, value) {
  const deviceId = value.deviceId
  const operatorId = value.operatorId
  const pid = state.pids?.[operatorId]?.[deviceId]?.pid

  switch (type) {
    case 'reboot':
      logger.debug(`Rebooting machine '${deviceId}' from operator ${operatorId}`)
      state.reboots[operatorId] = { [deviceId]: pid }
      break
    case 'shutdown':
      logger.debug(`Shutting down machine '${deviceId}' from operator ${operatorId}`)
      state.shutdowns[operatorId] = { [deviceId]: pid }
      break
    case 'restartServices':
      logger.debug(`Restarting services of machine '${deviceId}' from operator ${operatorId}`)
      state.restartServicesMap[operatorId] = { [deviceId]: pid }
      break
    case 'emptyUnit':
      logger.debug(`Emptying units from machine '${deviceId}' from operator ${operatorId}`)
      state.emptyUnit[operatorId] = { [deviceId]: pid }
      break
    case 'refillUnit':
      logger.debug(`Refilling recyclers from machine '${deviceId}' from operator ${operatorId}`)
      state.refillUnit[operatorId] = { [deviceId]: pid }
      break
    case 'diagnostics':
      logger.debug(`Running diagnostics on machine '${deviceId}' from operator ${operatorId}`)
      state.diagnostics[operatorId] = { [deviceId]: pid }
    default:
      break
  }
}

function reload (operatorId) {
  state.needsSettingsReload[operatorId] = true
}

const populateSettings = function (req, res, next) {
  const { needsSettingsReload, settingsCache } = state
  const operatorId = res.locals.operatorId
  const versionId = req.headers['config-version']
  if (versionId !== state.oldVersionId) {
    state.oldVersionId = versionId
  }

  try {
    // Priority of configs to retrieve
    // 1. Machine is in the middle of a transaction and has the config-version header set, fetch that config from cache or database, depending on whether it exists in cache
    // 2. The operator settings changed, so we must update the cache
    // 3. There's a cached config, send the cached value
    // 4. There's no cached config, cache and send the latest config

    if (versionId) {
      const cachedVersionedSettings = settingsCache.get(`${operatorId}-v${versionId}`)

      if (!cachedVersionedSettings) {
        logger.debug('Fetching a specific config version cached value')
        return newSettingsLoader.load(versionId)
          .then(settings => {
            settingsCache.set(`${operatorId}-v${versionId}`, settings)
            req.settings = settings
          })
          .then(() => next())
          .catch(next)
      }

      logger.debug('Fetching a cached specific config version')
      req.settings = cachedVersionedSettings
      return next()
    }

    const operatorSettings = settingsCache.get(`${operatorId}-latest`)

    if (!!needsSettingsReload[operatorId] || !operatorSettings) {
      !!needsSettingsReload[operatorId]
        ? logger.debug('Fetching and caching a new latest config value, as a reload was requested')
        : logger.debug('Fetching the latest config version because there\'s no cached value')

      return newSettingsLoader.loadLatest()
        .then(settings => {
          settingsCache.set(`${operatorId}-latest`, settings)
          if (!!needsSettingsReload[operatorId]) delete needsSettingsReload[operatorId]
          req.settings = settings
        })
        .then(() => next())
        .catch(next)
    }

    logger.debug('Fetching the latest config value from cache')
    req.settings = operatorSettings
    return next()
  } catch (e) {
    logger.error(e)
  }
}

module.exports = populateSettings
