const q = require('q')
const _ = require('lodash')

function removeHidden (obj) {
  for (let i in obj) {
    if (i[0] === '_') {
      delete obj[i]
      continue
    }
    if (_.isArray(obj[i])) {
      obj[i].forEach(removeHidden)
      continue
    }
    if (_.isObject(obj[i])) {
      removeHidden(obj[i])
    }
  }
}

module.exports = function (config) {
  const { common } = config
  return function (collectionName) {
    let bulk = []
    let opsCnt = 0
    let updates = {}

    return {
      update (id, data) {
        if (!id) {
          return
        }
        if (_.isObject(id) && id.toString().length === 24) { id = id.toString() }
        opsCnt++
        data = _.cloneDeep(data)

        _.forEach(data, (value, key) => {
          if (_.isObject(value)) {
            if (!_.isObject(id)) {
              throw new Error(`can not update an object diff property '${key}' without object reference`)
            }
            let originalValue = id[key] || {}
            _.merge(originalValue, value)
            data[key] = originalValue
          }
        })
        if (_.isObject(id)) {
          _.merge(id, data)
          id = id._id
        }

        removeHidden(data)

        updates[id] = updates[id] || {}
        _.extend(updates[id], data)
      },
      insert (data, id) {
        data = _.cloneDeep(data)
        removeHidden(data)

        if (id) {
          data._id = id
        }

        opsCnt++
        bulk.push({op: 'insert', data})
      },
      remove (id) {
        if (!id) {
          return
        }
        opsCnt++
        bulk.push({op: 'remove', id})
      },
      inc (id, key, amount) {
        if (!id) {
          return
        }
        if (_.isObject(id)) {
          id = id._id
        }
        opsCnt++
        bulk.push({op: 'inc', id, key, amount})
      },
      execute () {
        if (!opsCnt) return q.when({})
        for (let id in updates) {
          bulk.push({op: 'update', id, $set: updates[id]})
        }
        return common.storage.db[collectionName].bulk(bulk)
      }
    }
  }
}

@@ -1801,29 +1801,530 @@
 p: '
-inc', id, key, amount
+update', id, update: %7B$inc: %7B%5Bkey%5D: amount%7D%7D%7D)%0A      %7D,%0A      addToSet (id, key, value) %7B%0A        if (!id) %7B%0A          return%0A        %7D%0A        if (_.isObject(id)) %7B%0A          id = id._id%0A        %7D%0A        opsCnt++%0A        bulk.push(%7Bop: 'update', id, update: %7B$addToSet: %7B%5Bkey%5D: value%7D%7D%7D)%0A      %7D,%0A      pull (id, key, value) %7B%0A        if (!id) %7B%0A          return%0A        %7D%0A        if (_.isObject(id)) %7B%0A          id = id._id%0A        %7D%0A        opsCnt++%0A        bulk.push(%7Bop: 'update', id, update: %7B$pull: %7B%5Bkey%5D: value%7D%7D
 %7D)%0A 
@@ -2462,16 +2462,25 @@
 e', id, 
+update: %7B
 $set: up
@@ -2489,17 +2489,19 @@
 tes%5Bid%5D%7D
-)
+%7D);
 %0A       

Patch bulk to be compatible with recent engine updates