_ = require "lodash"
Flat = require "flat"
flatten = Flat.flatten
unflatten = Flat.unflatten

# Convert Dynamo AttributeValue map object(s) to plain javascript object(s)
#
# @param {Object|Array.<Object>} obj Dynamo AttributeValue map object(s)
# @return {Object|Array.<Object>} plain javascript object(s)
unpackObjectOrArray = (object) ->
  if _.isNull object then return
  if object.Item then object = object.Item
  if object.Items then object = object.Items
  if object.Attributes then object = object.Attributes
  if _.isArray object then return object.map unpackObjectOrArray

  result = {}
  for key, value of object
    if value.S then result[key] = value.S
    if value.N then result[key] = parseFloat value.N
    if value.B
      value.B = new Buffer(value.B, "base64").toString "ascii"
      result[key] = switch
        when value.B is "true" then true
        when value.B is "false" then false
        else value.B

    if value.SS then result[key] = value.SS
    if value.NS then result[key] = _.map value.NS, (i) -> parseFloat i

  unflatten result

# Convert plain javascript object(s) to Dynamo AttributeValue map object(s)
#
# @param {Object|Array.<Object>} plain javascript object(s)
# @return {Object|Array.<Object>} obj Dynamo AttributeValue map object(s)
packObjectOrArray = (object, keepEmpty) ->
  object = flatten object

  if _.isNull object then return
  if _.isArray object then return object.map packObjectOrArray

  result = {}
  for key, value of object
    if _.isBoolean value
      result[key] = {B: new Buffer(do value.toString).toString "base64"}
    if _.isNumber value then result[key] = {N: do value.toString}
    if !keepEmpty and _.isEmpty value then continue
    if _.isString value then result[key] = {S: do value.toString}
    if _.isArray value
      isNumberArray = _.every value, (n) ->
        _.isNumber n

      value = _.map value, (i) -> do i.toString
      if isNumberArray then result[key] = {NS: value}
      else result[key] = {SS: value}

  result

# Format an plain object to DynamoDB update request format
#
# @param {Object} object - plain javascript object
# @return {Object} Dynamo AttributeValue map object(s)
formatForUpdate = (object) ->
  object = packObjectOrArray object, true

  result = {}
  for key, value of object
    if _.isEmpty(_.toArray(value)[0])
      result[key] =
        Action: "DELETE"
    else
      result[key] =
        Value: value
        Action: "PUT"

  result

# Format an plain object to DynamoDB query request format
#
# @param {Object} object - plain javascript object
# @return {Object} Dynamo AttributeValue map object(s)
objectToQuery = (object) ->
  object = packObjectOrArray object
  result = {}

  for key, value of object
    result[key] =
      ComparisonOperator: "EQ"
      AttributeValueList: [value]

  result

# Sort data by `_createTimeStamp` attribute
# and delete the private attribute
#
# @param {Object|Array} data
# @return {Object|Array|null} result
sortAndClean = (data) ->
  if _.isArray data
    result = _.sortBy data, "_createTimeStamp"
    _.forEach result, (item) ->
      delete item._createTimeStamp
  else if _.isPlainObject data
    result = data
    delete result._createTimeStamp

  result

module.exports =
  formatForUpdate: formatForUpdate
  objectToQuery: objectToQuery
  packObjectOrArray: packObjectOrArray
  unpackObjectOrArray: unpackObjectOrArray
  sortAndClean: sortAndClean
