         status = "pending"
       {id, type, status}
 
-    keyList = _.sortBy keyList, (key) -> parseInt key.id
+    keyList = keyList.sort (a, b) ->
+      aid = parseInt a.id
+      bid = parseInt b.id
+      if aid < bid then -1
+      else if aid > bid then 1
+      else 0
+
     callback null, keyList
 
-  #Removes one or  more jobs by ID, also removes the job from any state list it"s in
-  removeJobs = (list) ->
+  commandRemoveFromStateLists: (prefix, id) -> [
+    ["lrem", "#{prefix}active",     0, id]
+    ["lrem", "#{prefix}wait",       0, id]
+    ["srem", "#{prefix}completed",  id]
+    ["srem", "#{prefix}failed",     id]
+  ]
+
+  #Removes one or  more jobs by ID, also removes the job from any state list it's in
+  remove: (list) ->
     return unless list
     #Expects {id: 123, type: "video transcoding"}
     multi = []
     for item in list
-      firstPartOfKey = "bull:#{item.type}:"
-      multi.push ["del",  firstPartOfKey + item.id]
-      multi.push ["lrem", firstPartOfKey + "active", 0, .id]
-      multi.push ["lrem", firstPartOfKey + "wait", 0, .id]
-      multi.push ["srem", firstPartOfKey + "completed", item.id]
-      multi.push ["srem", firstPartOfKey + "failed", item.id]
+      prefix = "bull:#{item.type}:"
+      multi.push ["del", "#{prefix}#{item.id}"]
+      multi.concat @commandRemoveFromStateLists prefix, item.id
 
     (@redis.multi multi).exec()
 
   #Makes all jobs in a specific status pending
-  makePendingByType = (type, callback) ->
+  makePendingByType: (type, callback) ->
     type = type.toLowerCase()
     validTypes = [
       "active"

# https://github.com/petkaantonov/bluebird/issues/63#issuecomment-34140791
_ = require("lodash")
q = require("bluebird")

class RedisModel
  constructor: (@redis) ->

  getActiveKeys: (callback) ->
    @redis.keys "bull:*:active", callback

  getCompletedKeys: (callback) ->
    @redis.keys "bull:*:completed", callback

  getFailedKeys: (callback) ->
    @redis.keys "bull:*:failed", callback

  getWaitingKeys: (callback) ->
    @redis.keys "bull:*:wait", callback

  getStuckKeys: (callback) ->
    ideally = errify callback
    #TODO: Find better way to do this. Being lazy at the moment.
    await getAllKeys ideally defer keys
    await formatKeys keys, ideally defer keyList

    count = 0
    keys  = {}
    for key in keyList when key.status is stuck
      results[keyList[i].type] or= []
      results[keyList[i].type].push keyList[i].id
      count++

    callback null, {keys, count}

  #Returns indexes of completed jobs
  getStatus: (status, callback) ->
    ideally = errify callback

    getStatusKeysFunction = switch status
    when "complete" then getCompletedKeys
    when "active"   then getActiveKeys
    when "failed"   then getFailedKeys
    when "wait"     then getWaitingKeys
    when "stuck"    then getStuckKeys()
    else
      return callback new Error "UNSUPPORTED STATUS: #{status}"

    await getStatusKeysFunction ideally defer statusKeys

    keys = []
    multi = []

    for key in statusKeys
      keys[(key.split ":")[1]] = []
      # This creates an array/object thing with keys of the job type
      if status is "active" or status is "wait"
        multi.push ["lrange", key, 0, -1]
      else
        multi.push ["smembers", key]

    await (@redis.multi multi).exec ideally defer data
    statusKeyKeys = Object.keys(keys)
    # Get the keys from the object we created earlier...
    count = 0
    for datum, i in data
      keys[statusKeyKeys[i]] = datum
      count += datum.length

    callback null, {keys, count}

  #Returns all JOB keys in string form (ex: bull:video transcoding:101)
  getAllKeys = (callback) ->
    ideally = errify callback
    await @redis.keys "bull:*:[0-9]*" ideally defer keysWithLocks
    (keyWithLock for keyWithLock in keysWithLocks when keyWithLock.substring(keyWithLock.length - 5, keyWithLock.length) isnt ":lock")

  getFullKeyNamesFromIds = (list, callback) ->
    return callback() unless list and Array.isArray list

    ideally = errify callback
    keys = (["keys", "bull:*:#{item}"] for item in list)
    await @redis.multi(keys).exec ideally defer arrayOfArrays
    callback null, (array[0] for array in arrayOfArrays when array.length is 1)

  #Returns the job data from a list of job ids
  getJobsInList = (list) ->
    return callback() unless list

    ideally = errify callback
    jobs = []
    if keys = list.keys
      objectKeys = Object.keys(keys)
      fullNames = []

      for key, val in keys
        for i in val
          fullNames.push "bull:" + key + ":" + keys[objectKeys[i]][k]
      callback null, fullnames
    else
      #Old list type
      getFullKeyNamesFromIds list, callback

  #Returns counts for different statuses
  getStatusCounts = (callback) ->
    ideally = errify callback
    await getStatus "active",   ideally defer active
    await getStatus "complete", ideally defer completed
    await getStatus "failed",   ideally defer failed
    await getStatus "wait",     ideally defer pendingKeys
    await getAllKeys ideally defer allKeys

    active: active.count
    complete: completed.count
    failed: failed.count
    pending: pendingKeys.count
    total: allKeys.length
    stuck: allKeys.length - (active.count + completed.count + failed.count + pendingKeys.count)

  #Returns all keys in object form, with status applied to object. Ex: {id: 101, type: "video transcoding", status: "pending"}
  formatKeys = (keys, callback) ->
    return unless keys

    ideally = errify callback

    await getStatus "failed",   ideally defer failedJobs
    await getStatus "complete", ideally defer completedJobs
    await getStatus "active",   ideally defer activeJobs
    await getStatus "wait",     ideally defer pendingJobs

    keyList = for key in keys
      [type, id] = key.split ":"
      status = "stuck"
      if activeJobs.keys[type] and id not in activeJobs.keys[type]
        status = "active"

      "failed"
      "wait"
    ]
    #I could add stuck, but I won't support mass modifying "stuck" jobs because it's very possible for things to be in a "stuck" state temporarily, while transitioning between states

    return callback "Invalid type: #{type} not in list of supported types" unless type in validTypes
    ideally = errify callback

    await @statusKeys type, ideally defer allKeys
    multi = []
    for type, keys of allKeys.keys
      prefix = "bull:#{type}:"
      for id in keys
        multi.push ["del", "#{prefix}#{id}"]
        multi.concat @commandRemoveFromStateLists prefix, id

    await (@redis.multi multi).exec ideally defer data
    callback null, "Successfully deleted all jobs of status #{type}."

  deleteById: (type, id, callback) ->
    return callback "id required"   unless id
    return callback "type required" unless type
    ideally = errify callback

    prefix = "bull:#{type}:"
    multi  = [["del", "#{prefix}#{id}"]]
    multi.concat @commandRemoveFromStateLists prefix, id
    await (@redis.multi multi).exec ideally defer data
    callback null, "Successfully deleted job #{type} ##{id}."

  dataById: (type, id, callback) ->
    return callback "id required"   unless id
    return callback "type required" unless type
    ideally = errify callback

    prefix = "bull:#{type}:"
    await redis.hgetall "#{prefix}id", ideally defer err, result
    callback null, result

  progressForKeys: (keys, callback) ->
    ideally = errify callback

    multi = for key in keys
      ["hget", "bull:#{key.type}:#{key.id}", "progress"]

    await (@redis.multi multi).exec ideally defer results
    key.progress = results[i] for key, i in keys
    callback null, keys


module.exports = RedisModel
