/* eslint-disable
    camelcase,
    handle-callback-err,
    no-unsafe-negation,
    no-unused-vars,
*/
// 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
 * DS205: Consider reworking code to avoid use of IIFEs
 * DS207: Consider shorter variations of null checks
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
let PersistenceManager
const Settings = require('settings-sharelatex')
const Errors = require('./Errors')
const Metrics = require('./Metrics')
const logger = require('logger-sharelatex')
const request = require('requestretry').defaults({
  maxAttempts: 2,
  retryDelay: 10
})

// We have to be quick with HTTP calls because we're holding a lock that
// expires after 30 seconds. We can't let any errors in the rest of the stack
// hold us up, and need to bail out quickly if there is a problem.
const MAX_HTTP_REQUEST_LENGTH = 5000 // 5 seconds

const updateMetric = function (method, error, response) {
  // find the status, with special handling for connection timeouts
  // https://github.com/request/request#timeouts
  const status = (() => {
    if ((error != null ? error.connect : undefined) === true) {
      return `${error.code} (connect)`
    } else if (error != null) {
      return error.code
    } else if (response != null) {
      return response.statusCode
    }
  })()
  Metrics.inc(method, 1, { status })
  if ((error != null ? error.attempts : undefined) > 1) {
    Metrics.inc(`${method}-retries`, 1, { status: 'error' })
  }
  if ((response != null ? response.attempts : undefined) > 1) {
    return Metrics.inc(`${method}-retries`, 1, { status: 'success' })
  }
}

module.exports = PersistenceManager = {
  getDoc(project_id, doc_id, _callback) {
    if (_callback == null) {
      _callback = function (
        error,
        lines,
        version,
        ranges,
        pathname,
        projectHistoryId,
        projectHistoryType
      ) {}
    }
    const timer = new Metrics.Timer('persistenceManager.getDoc')
    const callback = function (...args) {
      timer.done()
      return _callback(...Array.from(args || []))
    }

    const url = `${Settings.apis.web.url}/project/${project_id}/doc/${doc_id}`
    return request(
      {
        url,
        method: 'GET',
        headers: {
          accept: 'application/json'
        },
        auth: {
          user: Settings.apis.web.user,
          pass: Settings.apis.web.pass,
          sendImmediately: true
        },
        jar: false,
        timeout: MAX_HTTP_REQUEST_LENGTH
      },
      function (error, res, body) {
        updateMetric('getDoc', error, res)
        if (error != null) {
          return callback(error)
        }
        if (res.statusCode >= 200 && res.statusCode < 300) {
          try {
            body = JSON.parse(body)
          } catch (e) {
            return callback(e)
          }
          if (body.lines == null) {
            return callback(new Error('web API response had no doc lines'))
          }
          if (body.version == null || !body.version instanceof Number) {
            return callback(
              new Error('web API response had no valid doc version')
            )
          }
          if (body.pathname == null) {
            return callback(
              new Error('web API response had no valid doc pathname')
            )
          }
          return callback(
            null,
            body.lines,
            body.version,
            body.ranges,
            body.pathname,
            body.projectHistoryId,
            body.projectHistoryType
          )
        } else if (res.statusCode === 404) {
          return callback(new Errors.NotFoundError(`doc not not found: ${url}`))
        } else {
          return callback(
            new Error(`error accessing web API: ${url} ${res.statusCode}`)
          )
        }
      }
    )
  },

  setDoc(
    project_id,
    doc_id,
    lines,
    version,
    ranges,
    lastUpdatedAt,
    lastUpdatedBy,
    _callback
  ) {
    if (_callback == null) {
      _callback = function (error) {}
    }
    const timer = new Metrics.Timer('persistenceManager.setDoc')
    const callback = function (...args) {
      timer.done()
      return _callback(...Array.from(args || []))
    }

    const url = `${Settings.apis.web.url}/project/${project_id}/doc/${doc_id}`
    return request(
      {
        url,
        method: 'POST',
        json: {
          lines,
          ranges,
          version,
          lastUpdatedBy,
          lastUpdatedAt
        },
        auth: {
          user: Settings.apis.web.user,
          pass: Settings.apis.web.pass,
          sendImmediately: true
        },
        jar: false,
        timeout: MAX_HTTP_REQUEST_LENGTH
      },
      function (error, res, body) {
        updateMetric('setDoc', error, res)
        if (error != null) {
          return callback(error)
        }
        if (res.statusCode >= 200 && res.statusCode < 300) {
          return callback(null)
        } else if (res.statusCode === 404) {
          return callback(new Errors.NotFoundError(`doc not not found: ${url}`))
        } else {
          return callback(
            new Error(`error accessing web API: ${url} ${res.statusCode}`)
          )
        }
      }
    )
  }
}
