log = require 'winston'
async = require 'async'
errors = require '../../commons/errors'
scoringUtils = require './scoringUtils'
LevelSession = require '../../models/LevelSession'
TaskLog = require './../../models/ScoringTask'

module.exports = dispatchTaskToConsumer = (req, res) ->
  yetiGuru = {}
  async.waterfall [
    checkSimulationPermissions.bind(yetiGuru, req)
    receiveMessageFromSimulationQueue
    changeMessageVisibilityTimeout
    parseTaskQueueMessage
    constructTaskObject
    constructTaskLogObject.bind(yetiGuru, getUserIDFromRequest(req))
    processTaskObject
  ], (err, taskObjectToSend) ->
    if err?
      if typeof err is 'string' and err.indexOf 'No more games in the queue' isnt -1
        res.send(204, 'No games to score.')
        return res.end()
      else
        return errors.serverError res, "There was an error dispatching the task: #{err}"
    scoringUtils.sendResponseObject res, taskObjectToSend


checkSimulationPermissions = (req, cb) ->
  if req.user and not req.user.isAnonymous()
    cb null
  else
    cb 'You need to be logged in to simulate games'

receiveMessageFromSimulationQueue = (cb) ->
  scoringUtils.scoringTaskQueue.receiveMessage (err, message) ->
    if err? then return cb "No more games in the queue, error: #{err}"
    if not message? or message.isEmpty() then return cb 'Message received from queue is invalid'
    cb null, message

changeMessageVisibilityTimeout = (message, cb) ->
  message.changeMessageVisibilityTimeout scoringUtils.scoringTaskTimeoutInSeconds, (err) ->
    cb err, message

parseTaskQueueMessage = (message, cb) ->
  try
    messageBody = message.getBody()
    unless typeof messageBody is 'object'
      messageBody = JSON.parse messageBody
    cb null, messageBody, message
  catch e
    cb "There was an error parsing the task. Error: #{e}"

constructTaskObject = (taskMessageBody, message, callback) ->
  async.map taskMessageBody.sessions, getSessionInformation, (err, sessions) ->
    if err? then return callback err
    taskObject = messageGenerated: Date.now(), sessions: (scoringUtils.formatSessionInformation session for session in sessions)
    callback null, taskObject, message

getSessionInformation = (sessionIDString, callback) ->
  selectString = 'submitDate team submittedCode teamSpells levelID creator creatorName submittedCodeLanguage totalScore'
  LevelSession.findOne(_id: sessionIDString).select(selectString).lean().exec (err, session) ->
    if err? then return callback err, {'error': 'There was an error retrieving the session.'}
    callback null, scoringUtils.formatSessionInformation session

constructTaskLogObject = (calculatorUserID, taskObject, message, callback) ->
  taskLogObject = new TaskLog
    createdAt: new Date()
    calculator: calculatorUserID
    sentDate: Date.now()
    messageIdentifierString: message.getReceiptHandle()
  taskLogObject.save (err) ->
    callback err, taskObject, taskLogObject, message

getUserIDFromRequest = (req) ->
  if req.user? then return req.user._id else return null

processTaskObject = (taskObject, taskLogObject, message, cb) ->
  taskObject.taskID = taskLogObject._id
  taskObject.receiptHandle = message.getReceiptHandle()
  cb null, taskObject
