/**
 * Define inbound function for policy
 * @function policyInbound
 * @param {ClayPolicy} policy - Policy to bind
 * @param {ClayResource} resource - Resource of entities
 * @returns {function} Inbound function
 */
'use strict'

const {LogPrefixes} = require('clay-constants')
const clayResourceName = require('clay-resource-name')
const {PolicyError} = require('clay-errors')
const {flatten} = require('objnest')
const {UNIQUE_VIOLATION} = require('clay-policy').Reasons

/** @lends policyInbound */
function policyInbound (policy) {
  return async function inbound (resource, attributesArray, actionContext = {}) {
    const resourceName = clayResourceName(resource)
    const {action, errorNamespace} = actionContext
    const idFor = (index) => {
      const {id, ids} = actionContext
      if (id) {
        return id
      }
      return ids && ids[index]
    }

    async function hasUniqueViolation (attributes, uniqueFilter, {id}) {
      const exists = await resource.exists(uniqueFilter)
      if (!exists) {
        return false
      }
      if (id) {
        const first = await resource.first(uniqueFilter)
        const isSelf = String(id) === String(first.id)
        return !isSelf
      }
      return true
    }

    policy = await Promise.resolve(policy)
    for (let [index, attributes] of attributesArray.entries()) {
      if (!policy) {
        continue
      }
      if (policy.format) {
        attributes = policy.format(attributes)
      } else {
        console.warn('`policy.format` is not found. Perhaps you are using older version of clay-policy.')
      }

      switch (action) {
        case 'create':
        case 'createBulk': {
          const defaults = policy.defaultsFor(attributes)
          Object.assign(attributes, defaults)
          policy.validateToThrow(attributes, {
            prefix: resourceName,
            namespace: errorNamespace
          })
          break
        }
        default:
          policy.validateToThrow(attributes, {
            prefix: resourceName,
            ignoreMissing: true,
            namespace: errorNamespace
          })
          break
      }

      const id = idFor(index)
      const uniqueFilters = policy.uniqueFilters(attributes)
      for (const uniqueFilter of uniqueFilters) {
        const hasViolation = await hasUniqueViolation(attributes, uniqueFilter, {id})
        if (hasViolation) {
          const attributesNames = Object.keys(uniqueFilter)
          const error = new PolicyError(`[${resourceName}] Unique constraint violation on ${attributesNames.join(',')}`, {
            conflict: errorNamespace ? flatten({[errorNamespace]: uniqueFilter}) : uniqueFilter,
            reason: UNIQUE_VIOLATION
          })
          delete error.stack
          throw error
        }
      }
    }
    return attributesArray
  }
}

module.exports = policyInbound

@@ -231,16 +231,17 @@
 %0Aconst %7B
+ 
 LogPrefi
@@ -243,16 +243,17 @@
 Prefixes
+ 
 %7D = requ
@@ -336,16 +336,17 @@
 %0Aconst %7B
+ 
 PolicyEr
@@ -348,16 +348,17 @@
 icyError
+ 
 %7D = requ
@@ -383,23 +383,25 @@
 %0Aconst %7B
+ 
 flatten
+ 
 %7D = requ
@@ -422,16 +422,17 @@
 %0Aconst %7B
+ 
 UNIQUE_V
@@ -439,16 +439,17 @@
 IOLATION
+ 
 %7D = requ
@@ -683,16 +683,17 @@
  const %7B
+ 
 action, 
@@ -706,33 +706,121 @@
 amespace
-%7D = actionContext
+, skipPolicyCheck = false %7D = actionContext%0A    if (skipPolicyCheck) %7B%0A      return attributesArray%0A    %7D
 %0A    con
@@ -860,15 +860,17 @@
 st %7B
+ 
 id, ids
+ 
 %7D = 
@@ -1029,18 +1029,20 @@
 ilter, %7B
+ 
 id
+ 
 %7D) %7B%0A   
@@ -2454,18 +2454,20 @@
 ilter, %7B
+ 
 id
+ 
 %7D)%0A     
@@ -2720,16 +2720,17 @@
 latten(%7B
+ 
 %5BerrorNa
@@ -2751,16 +2751,17 @@
 ueFilter
+ 
 %7D) : uni

update deps