/* eslint-disable
    camelcase,
    handle-callback-err,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
 * decaffeinate suggestions:
 * DS101: Remove unnecessary use of Array.from
 * DS102: Remove unnecessary code created because of implicit returns
 * DS207: Consider shorter variations of null checks
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
let ProjectPersistenceManager
const UrlCache = require('./UrlCache')
const CompileManager = require('./CompileManager')
const db = require('./db')
const dbQueue = require('./DbQueue')
const async = require('async')
const logger = require('logger-sharelatex')
const oneDay = 24 * 60 * 60 * 1000
const Settings = require('settings-sharelatex')
const diskusage = require('diskusage')

module.exports = ProjectPersistenceManager = {
  EXPIRY_TIMEOUT: Settings.project_cache_length_ms || oneDay * 2.5,

  refreshExpiryTimeout(callback) {
    if (callback == null) {
      callback = function (error) {}
    }
    diskusage.check('/', function (err, stats) {
      if (err) {
        logger.err({ err: err }, 'error getting disk usage')
        return callback(err)
      }
      const lowDisk = stats.available / stats.total < 0.1
      const lowerExpiry = ProjectPersistenceManager.EXPIRY_TIMEOUT * 0.9
      if (lowDisk && Settings.project_cache_length_ms / 2 < lowerExpiry) {
        logger.warn(
          {
            stats: stats,
            newExpiryTimeoutInDays: (lowerExpiry / oneDay).toFixed(2)
          },
          'disk running low on space, modifying EXPIRY_TIMEOUT'
        )
        ProjectPersistenceManager.EXPIRY_TIMEOUT = lowerExpiry
      }
      callback()
    })
  },
  markProjectAsJustAccessed(project_id, callback) {
    if (callback == null) {
      callback = function (error) {}
    }
    const job = (cb) =>
      db.Project.findOrCreate({ where: { project_id } })
        .spread((project, created) =>
          project
            .update({ lastAccessed: new Date() })
            .then(() => cb())
            .error(cb)
        )
        .error(cb)
    return dbQueue.queue.push(job, callback)
  },

  clearExpiredProjects(callback) {
    if (callback == null) {
      callback = function (error) {}
    }
    return ProjectPersistenceManager._findExpiredProjectIds(function (
      error,
      project_ids
    ) {
      if (error != null) {
        return callback(error)
      }
      logger.log({ project_ids }, 'clearing expired projects')
      const jobs = Array.from(project_ids || []).map((project_id) =>
        ((project_id) => (callback) =>
          ProjectPersistenceManager.clearProjectFromCache(project_id, function (
            err
          ) {
            if (err != null) {
              logger.error({ err, project_id }, 'error clearing project')
            }
            return callback()
          }))(project_id)
      )
      return async.series(jobs, function (error) {
        if (error != null) {
          return callback(error)
        }
        return CompileManager.clearExpiredProjects(
          ProjectPersistenceManager.EXPIRY_TIMEOUT,
          (error) => callback()
        )
      })
    })
  }, // ignore any errors from deleting directories

  clearProject(project_id, user_id, callback) {
    if (callback == null) {
      callback = function (error) {}
    }
    logger.log({ project_id, user_id }, 'clearing project for user')
    return CompileManager.clearProject(project_id, user_id, function (error) {
      if (error != null) {
        return callback(error)
      }
      return ProjectPersistenceManager.clearProjectFromCache(
        project_id,
        function (error) {
          if (error != null) {
            return callback(error)
          }
          return callback()
        }
      )
    })
  },

  clearProjectFromCache(project_id, callback) {
    if (callback == null) {
      callback = function (error) {}
    }
    logger.log({ project_id }, 'clearing project from cache')
    return UrlCache.clearProject(project_id, function (error) {
      if (error != null) {
        logger.err({ error, project_id }, 'error clearing project from cache')
        return callback(error)
      }
      return ProjectPersistenceManager._clearProjectFromDatabase(
        project_id,
        function (error) {
          if (error != null) {
            logger.err(
              { error, project_id },
              'error clearing project from database'
            )
          }
          return callback(error)
        }
      )
    })
  },

  _clearProjectFromDatabase(project_id, callback) {
    if (callback == null) {
      callback = function (error) {}
    }
    logger.log({ project_id }, 'clearing project from database')
    const job = (cb) =>
      db.Project.destroy({ where: { project_id } })
        .then(() => cb())
        .error(cb)
    return dbQueue.queue.push(job, callback)
  },

  _findExpiredProjectIds(callback) {
    if (callback == null) {
      callback = function (error, project_ids) {}
    }
    const job = function (cb) {
      const keepProjectsFrom = new Date(
        Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT
      )
      const q = {}
      q[db.op.lt] = keepProjectsFrom
      return db.Project.findAll({ where: { lastAccessed: q } })
        .then((projects) =>
          cb(
            null,
            projects.map((project) => project.project_id)
          )
        )
        .error(cb)
    }

    return dbQueue.queue.push(job, callback)
  }
}

logger.log(
  { EXPIRY_TIMEOUT: ProjectPersistenceManager.EXPIRY_TIMEOUT },
  'project assets kept timeout'
)
