/* eslint-disable
    camelcase,
    handle-callback-err,
    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
 * DS207: Consider shorter variations of null checks
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
let CompileController
const RequestParser = require('./RequestParser')
const CompileManager = require('./CompileManager')
const Settings = require('settings-sharelatex')
const Metrics = require('./Metrics')
const ProjectPersistenceManager = require('./ProjectPersistenceManager')
const logger = require('logger-sharelatex')
const Errors = require('./Errors')

function isImageNameAllowed(imageName) {
  const ALLOWED_IMAGES =
    Settings.clsi && Settings.clsi.docker && Settings.clsi.docker.allowedImages
  return !ALLOWED_IMAGES || ALLOWED_IMAGES.includes(imageName)
}

module.exports = CompileController = {
  compile(req, res, next) {
    if (next == null) {
      next = function (error) {}
    }
    const timer = new Metrics.Timer('compile-request')
    return RequestParser.parse(req.body, function (error, request) {
      if (error != null) {
        return next(error)
      }
      request.project_id = req.params.project_id
      if (req.params.user_id != null) {
        request.user_id = req.params.user_id
      }
      return ProjectPersistenceManager.markProjectAsJustAccessed(
        request.project_id,
        function (error) {
          if (error != null) {
            return next(error)
          }
          return CompileManager.doCompileWithLock(request, function (
            error,
            outputFiles,
            stats,
            timings
          ) {
            let code, status
            if (outputFiles == null) {
              outputFiles = []
            }
            if (error instanceof Errors.AlreadyCompilingError) {
              code = 423 // Http 423 Locked
              status = 'compile-in-progress'
            } else if (error instanceof Errors.FilesOutOfSyncError) {
              code = 409 // Http 409 Conflict
              status = 'retry'
            } else if (error && error.code === 'EPIPE') {
              // docker returns EPIPE when shutting down
              code = 503 // send 503 Unavailable response
              status = 'unavailable'
            } else if (error != null ? error.terminated : undefined) {
              status = 'terminated'
            } else if (error != null ? error.validate : undefined) {
              status = `validation-${error.validate}`
            } else if (error != null ? error.timedout : undefined) {
              status = 'timedout'
              logger.log(
                { err: error, project_id: request.project_id },
                'timeout running compile'
              )
            } else if (error != null) {
              status = 'error'
              code = 500
              logger.warn(
                { err: error, project_id: request.project_id },
                'error running compile'
              )
            } else {
              let file
              status = 'failure'
              for (file of Array.from(outputFiles)) {
                if (file.path === 'output.pdf') {
                  status = 'success'
                }
              }

              if (status === 'failure') {
                logger.warn(
                  { project_id: request.project_id, outputFiles },
                  'project failed to compile successfully, no output.pdf generated'
                )
              }

              // log an error if any core files are found
              for (file of Array.from(outputFiles)) {
                if (file.path === 'core') {
                  logger.error(
                    { project_id: request.project_id, req, outputFiles },
                    'core file found in output'
                  )
                }
              }
            }

            if (error != null) {
              outputFiles = error.outputFiles || []
            }

            timer.done()
            return res.status(code || 200).send({
              compile: {
                status,
                error: (error != null ? error.message : undefined) || error,
                stats,
                timings,
                outputFiles: outputFiles.map((file) => {
                  const record = {
                    url:
                      `${Settings.apis.clsi.url}/project/${request.project_id}` +
                      (request.user_id != null
                        ? `/user/${request.user_id}`
                        : '') +
                      (file.build != null ? `/build/${file.build}` : '') +
                      `/output/${file.path}`,
                    path: file.path,
                    type: file.type,
                    build: file.build,
                    contentId: file.contentId
                  }
                  if (file.ranges != null) {
                    record.ranges = file.ranges
                  }
                  if (file.size != null) {
                    record.size = file.size
                  }
                  return record
                })
              }
            })
          })
        }
      )
    })
  },

  stopCompile(req, res, next) {
    const { project_id, user_id } = req.params
    return CompileManager.stopCompile(project_id, user_id, function (error) {
      if (error != null) {
        return next(error)
      }
      return res.sendStatus(204)
    })
  },

  clearCache(req, res, next) {
    if (next == null) {
      next = function (error) {}
    }
    return ProjectPersistenceManager.clearProject(
      req.params.project_id,
      req.params.user_id,
      function (error) {
        if (error != null) {
          return next(error)
        }
        return res.sendStatus(204)
      }
    )
  }, // No content

  syncFromCode(req, res, next) {
    if (next == null) {
      next = function (error) {}
    }
    const { file } = req.query
    const line = parseInt(req.query.line, 10)
    const column = parseInt(req.query.column, 10)
    const { imageName } = req.query
    const { project_id } = req.params
    const { user_id } = req.params

    if (imageName && !isImageNameAllowed(imageName)) {
      return res.status(400).send('invalid image')
    }

    return CompileManager.syncFromCode(
      project_id,
      user_id,
      file,
      line,
      column,
      imageName,
      function (error, pdfPositions) {
        if (error != null) {
          return next(error)
        }
        return res.json({
          pdf: pdfPositions
        })
      }
    )
  },

  syncFromPdf(req, res, next) {
    if (next == null) {
      next = function (error) {}
    }
    const page = parseInt(req.query.page, 10)
    const h = parseFloat(req.query.h)
    const v = parseFloat(req.query.v)
    const { imageName } = req.query
    const { project_id } = req.params
    const { user_id } = req.params

    if (imageName && !isImageNameAllowed(imageName)) {
      return res.status(400).send('invalid image')
    }
    return CompileManager.syncFromPdf(
      project_id,
      user_id,
      page,
      h,
      v,
      imageName,
      function (error, codePositions) {
        if (error != null) {
          return next(error)
        }
        return res.json({
          code: codePositions
        })
      }
    )
  },

  wordcount(req, res, next) {
    if (next == null) {
      next = function (error) {}
    }
    const file = req.query.file || 'main.tex'
    const { project_id } = req.params
    const { user_id } = req.params
    const { image } = req.query
    if (image && !isImageNameAllowed(image)) {
      return res.status(400).send('invalid image')
    }
    logger.log({ image, file, project_id }, 'word count request')

    return CompileManager.wordcount(project_id, user_id, file, image, function (
      error,
      result
    ) {
      if (error != null) {
        return next(error)
      }
      return res.json({
        texcount: result
      })
    })
  },

  status(req, res, next) {
    if (next == null) {
      next = function (error) {}
    }
    return res.send('OK')
  }
}
