typeUtil = require "./typeUtil"
AWS = require "aws-sdk"
_ = require "lodash"
Flat = require "flat"
flatten = Flat.flatten
unflatten = Flat.unflatten

class Dynoga
  constructor: (options) ->
    @dynamo = new AWS.DynamoDB options

  # Create an item
  #
  # @param {String} table
  # @param {Object} item - New item (with primary keys)
  # @return {Error}
  # @return {Object} new item
  create: (table, item, callback) ->
    # Generate an id if undefined
    item.id = do Date.now().toString unless item.id

    # Generate a private attribute for future array order
    item._createTimeStamp = do Date.now

    dynamoItem = typeUtil.packObjectOrArray item

    @dynamo.putItem
      TableName: table
      Item: dynamoItem
      (err, data) ->
        delete item._createTimeStamp
        callback? err, item

  # Read one or many item(s)
  #
  # @param {String} table
  # @param {Object} keys - ex: {key1: "plop", key2: "rafiki"}
  # @return {Error}
  # @return {Object|Array} one item or list of items
  read: (table, keys, callback) ->
    keysLength = _.toArray(keys).length
    keyConditions = typeUtil.objectToQuery keys

    @dynamo.query
      TableName: table
      KeyConditions: keyConditions
      (err, data) ->
        result = typeUtil.unpackObjectOrArray data
        if result? and keysLength is 2 then result = result[0]
        callback? err, typeUtil.sortAndClean result

  # Update one item
  #
  # @param {String} table
  # @param {Object} item to update (with primary keys)
  # @return {Error}
  # @return {Object} item after udpate
  update: (table, item, callback) ->
    # Extract keys from item
    @_findKeys table, (err, keys) =>
      if err
        callback? err
        return

      objectKey = {}
      objectKey[keys[0]] = item[keys[0]]
      objectKey[keys[1]] = item[keys[1]]
      dynamoKey = typeUtil.packObjectOrArray objectKey

      delete item[keys[0]]
      delete item[keys[1]]
      dynamoItem = typeUtil.formatForUpdate item

      @read table, objectKey, (err, data) =>

        # Extract old objects key into database
        oldKeys = [] # ex: toto.choco
        for key, value of flatten data
          if /\./.test key then oldKeys.push key

        # Extract objects to update
        updatedKeys = [] # ex: toto
        for key, value of dynamoItem
          if /\./.test key then updatedKeys.push key.split(".")[0]
        updatedKeys = _.uniq updatedKeys

        # Prepare with defaults objects to delete
        updateQuery = {}
        for i in updatedKeys
          for j in oldKeys
            regex = new RegExp "^" + i + "\." # string beginning by `{{key}}.`
            if regex.test j
              updateQuery[j] =
                Action: "DELETE"

        # Prepare final query
        for key, value of dynamoItem
          updateQuery[key] = value

        @dynamo.updateItem
          TableName: table
          Key: dynamoKey
          AttributeUpdates: updateQuery
          ReturnValues: "ALL_NEW"
          (err, data) =>
            if err then callback err
            callback? err, typeUtil.sortAndClean typeUtil.unpackObjectOrArray data

  # Delete one item
  #
  # @param {String} table
  # @param {Object} item or keys
  # @return {Error}
  # @return {Object} deleted item
  delete: (table, item, callback) ->
    # Extract keys from item
    @_findKeys table, (err, keys) =>
      if err
        callback? err
        return

      objectKey = {}
      objectKey[keys[0]] = item[keys[0]]
      objectKey[keys[1]] = item[keys[1]]
      dynamoKey = typeUtil.packObjectOrArray objectKey

      # Stock item and delete it
      @dynamo.deleteItem
        TableName: table
        Key: dynamoKey
        ReturnValues: "ALL_OLD"
        (err, data) ->
          callback? err, typeUtil.sortAndClean typeUtil.unpackObjectOrArray data

  # Extract primary keys of a given table
  #
  # @param {String} table
  # @return {Error}
  # @return {Array} list of primary keys
  _findKeys: (table, callback) ->
    @dynamo.describeTable
      TableName: table
      (err, data) ->
        if data?
          keySchema = data.Table.KeySchema
          keys = _.map keySchema, (key) ->
            key.AttributeName
        callback? err, keys

module.exports = Dynoga
