         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"

      "failed"
      "wait"
    ]
    #I could add stuck, but(callback)  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

    ideally = errify (message) -> callback {success: false, message}

    console.log validTypes.indexOf(type)
    unless type in validTypes
      return callback success: false, message: "Invalid type: #{type} not in list of supported types"

    await getStatus type, ideally defer allKeys
      multi = []
      allKeyObjects = Object.keys(allKeys.keys)

      i = 0
      ii = allKeyObjects.length
      while i < ii
        firstPartOfKey = "bull:" + allKeyObjects[i] + ":"
        k = 0
        kk = allKeys.keys[allKeyObjects[i]].length
        while k < kk
          item = allKeys.keys[allKeyObjects[i]][k]
          #Brute force remove from everything
          multi.push ["lrem", firstPartOfKey + "active", 0, item]
          multi.push ["srem", firstPartOfKey + "completed", item]
          multi.push ["srem", firstPartOfKey + "failed", item]
          #Add to pending
          multi.push ["rpush", firstPartOfKey + "wait", item]

      await (@redis.multi multi).exec ideally defer data
      callback success: true, message: "Successfully made all #{type} jobs pending."

  #Makes a job with a specific ID pending, requires the type of job as the first parameter and ID as second.
  makePendingById = (type, id, callback) ->
    unless id
      return callback success: false, message: "There was no ID provided."
    unless type
      return callback success: false, message: "There was no type provided."

    ideally = errify (message) -> callback {success: false, message}

    firstPartOfKey = "bull:#{type}:"

    multi = []
    multi.push ["lrem", firstPartOfKey + "active", 0, id]
    multi.push ["lrem", firstPartOfKey + "wait", 0, id]
    multi.push ["srem", firstPartOfKey + "completed", id]
    multi.push ["srem", firstPartOfKey + "failed", id]
    #Add to pending
    multi.push ["rpush", firstPartOfKey + "wait", id]
    await (@redis.multi multi).exec ideally defer data
    callback success: true, message: "Successfully made #{type} job ##{id} pending."

  #Deletes all jobs in a specific status
  deleteJobByStatus = (type, callback) ->
    type = type.toLowerCase()
    validTypes = [
      "active"

errify = require "errify"

capitalize = (str) -> str[0].toUpperCase() + str[1..]


class RedisModel
  constructor: (@redis) ->

  activeKeys: (queue = "*", callback) ->
    @redis.keys "bull:#{queue}:active", callback

  completeKeys: (queue = "*", callback) ->
    @redis.keys "bull:#{queue}:completed", callback

  failedKeys: (queue = "*", callback) ->
    @redis.keys "bull:#{queue}:failed", callback

  waitKeys: (queue = "*", callback) ->
    @redis.keys "bull:#{queue}:wait", callback

  stuckKeys: (queue = "*", callback) ->
    ideally = errify callback
    #TODO: Find better way to do this. Being lazy at the moment.
    await @allKeys queue, ideally defer keys
    await @formatKeys keys, ideally defer keyList

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

    callback null, {keys, count}

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

    await @["#{status}Keys"] ideally defer statusKeys

    keys  = {}
    multi = []

    for key in statusKeys
      [_, type]  = key.split ":"
      keys[type] = null

      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 arrayOfArrays
    statusKeyKeys = Object.keys(keys)
    # Get the keys from the object we created earlier...
    count = 0
    for array, i in arrayOfArrays
      keys[statusKeyKeys[i]] = array
      count += array.length

    callback null, {keys, count}

  #Returns all JOB keys in string form (ex: bull:video transcoding:101)
  allKeys: (queue = "*", callback) ->
    ideally = errify callback

    await @redis.keys "bull:#{queue}:[0-9]*" ideally defer keysWithLocks
    result = (keyWithLock for keyWithLock in keysWithLocks when keyWithLock[-5..] isnt ":lock")
    callback null, result

  fullKeyNamesFromIds: (list, callback) ->
    return callback() unless list and Array.isArray list
    ideally = errify callback

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

  #Returns the job data from a list of job ids
  jobsInList: (list, callback) ->
    return callback() unless list

    if allkeys = list.keys
      fullNames = "bull:#{type}:#{key}" for key in keys for type, keys of allkeys
      callback null, fullnames
    else
      #Old list type
      @fullKeyNamesFromIds list, callback

  #Returns counts for different statuses
  statusCounts: (callback) ->
    ideally = errify callback
    await @statusKeys "active",   ideally defer active
    await @statusKeys "complete", ideally defer completed
    await @statusKeys "failed",   ideally defer failed
    await @statusKeys "wait",     ideally defer pendingKeys
    await @allKeys                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 @statusKeys "failed",   ideally defer failedJobs
    await @statusKeys "complete", ideally defer completedJobs
    await @statusKeys "active",   ideally defer activeJobs
    await @statusKeys "wait",     ideally defer pendingJobs

    keyList = for key in keys
      [_, type..., id] = key.split ":"
      type = type.join ":"

      status = "stuck"
      if activeJobs.keys[type] and id not in activeJobs.keys[type]
        status = "active"
