text: import angular from 'angular'
  import objectKeys from 'object-keys'
  import arrayMap from 'array-map'
  
  export default httpEtagHttpDecorator
  
  httpEtagHttpDecorator.$inject = ['$delegate', 'httpEtag']
  
  function httpEtagHttpDecorator ($delegate, httpEtag) {
    var $http = $delegate
    var cachableHttpMethods = [
      'GET',
      'JSONP'
    ]
  
    function $httpDecorator (httpConfig) {
      var useLegacyPromiseExtensions = httpEtagHttpDecorator.useLegacyPromiseExtensions()
      var hasConfig = !!httpConfig.etagCache
      var isCacheableMethod = cachableHttpMethods.indexOf(httpConfig.method) >= 0
      var isCachable = hasConfig && isCacheableMethod
      var httpPromise
  
      if (isCachable) {
        var etagCacheConfig = processHttpConfigEtagValue(httpConfig)
        if (etagCacheConfig) {
          var itemCache = httpEtag.getItemCache(etagCacheConfig.id, etagCacheConfig.itemKey)
          if (!itemCache) throw new Error('No defined ETag caches match specified cache ID')
  
          var cacheInfo = itemCache.info()
          var rawCacheData = itemCache.$get()
          var cachedEtag = rawCacheData && rawCacheData.etagHeader
          var cachedResponse = cachedEtag && rawCacheData.responseData
  
          // Allow easy access to cache in interceptor
          httpConfig.$$_itemCache = itemCache
  
          if (cachedEtag) {
            httpConfig.headers = angular.extend({}, httpConfig.headers, {
              'If-None-Match': cachedEtag
            })
          }
        }
      }
  
      httpPromise = $http.apply($http, arguments)
      httpEtagPromiseFactory(httpPromise)
  
      function httpEtagPromiseFactory (httpPromise) {
        var then = httpPromise.then
        var success = httpPromise.success
  
        if (useLegacyPromiseExtensions) {
          httpPromise.cached = function httpEtagPromiseCached (callback) {
            if (rawCacheData && cacheInfo.cacheResponseData) {
              callback(cachedResponse, 'cached', undefined, httpConfig, itemCache)
            }
            return httpPromise
          }
        } else {
          httpPromise.cached = function deprecatedEtagPromiseCached () {
            throw new Error('The method `cached` on the promise returned from `$http` has been disabled in favor of `ifCached`.')
          }
        }
  
        httpPromise.ifCached = function httpEtagPromiseIfCached (callback) {
          if (rawCacheData && cacheInfo.cacheResponseData) {
            // We're simply mimicking the Angular API here, so... sorry, Standard.
            // eslint-disable-next-line standard/no-callback-literal
            callback({
              data: cachedResponse,
              status: 'cached',
              headers: undefined,
              config: httpConfig
            }, itemCache)
          }
          return httpPromise
        }
  
        httpPromise.then = function httpEtagThenWrapper (successCallback, errorCallback, progressBackCallback) {
          var thenPromise = then.apply(httpPromise, [
            successCallback ? httpEtagSuccessWrapper : undefined,
            errorCallback ? httpEtagErrorWrapper : undefined,
            progressBackCallback
          ])
  
          function httpEtagSuccessWrapper (response) {
            return successCallback(response, itemCache)
          }
  
          function httpEtagErrorWrapper (response) {
            return errorCallback(response, itemCache)
          }
  
          return httpEtagPromiseFactory(thenPromise)
        }
  
        if (useLegacyPromiseExtensions && itemCache) {
          httpPromise.success = function httpEtagPromiseSuccess (callback) {
            var partializedCallback = partial(callback, undefined, undefined, undefined, undefined, itemCache)
            return success.apply(httpPromise, [partializedCallback])
          }
        }
  
        return httpPromise
      }
  
      return httpPromise
    }
  
    // Decorate the cachable shortcut methods, too
    angular.forEach(cachableHttpMethods, function (httpMethod) {
      var method = httpMethod.toLowerCase()
      $httpDecorator[method] = function httpEtagHttpShortcutWrapper (url, config) {
        config = angular.extend({}, config, {
          method: httpMethod,
          url: url
        })
  
        return $httpDecorator.call($http, config)
      }
    })
  
    // Copy over all other properties and methods
    angular.forEach($http, function copyHttpPropertyToDectorator (value, key) {
      if (!$httpDecorator[key]) $httpDecorator[key] = value
    })
  
    /**
     * HELPERS
     */
  
    function processHttpConfigEtagValue (httpConfig) {
      var etagValue = httpConfig.etagCache
      var etagValueType = typeof etagValue
      var etagCacheConfig = {}
  
      // Evaluate function first
      if (etagValueType === 'function') {
        etagValue = etagValue(httpConfig)
        etagValueType = typeof etagValue
      }
  
      // Plain, cache, or itemCache objects
      if (etagValueType === 'object') {
        var id, itemKey
  
        if (etagValue.isCache) {
          id = etagValue.info().id
          itemKey = generateCacheItemKey(httpConfig)
        } else if (etagValue.isItemCache) {
          id = etagValue.info().id
          itemKey = etagValue.info().itemKey
        } else {
          id = etagValue.id
          itemKey = etagValue.itemKey || generateCacheItemKey(httpConfig)
        }
  
        etagCacheConfig.id = id
        etagCacheConfig.itemKey = itemKey
      } else if (etagValueType === 'string') {
        etagCacheConfig.id = etagValue
        etagCacheConfig.itemKey = generateCacheItemKey(httpConfig)
      } else if (etagValue === true) {
        // Undefined cacheId will use the default cacheId as defined in provider
        etagCacheConfig.itemKey = generateCacheItemKey(httpConfig)
      } else return
      return etagCacheConfig
    }
  
    function generateCacheItemKey (httpConfig) {
      var url = httpConfig.url
      var params = stringifyParams(httpConfig.params)
      var joiner = ((params && url.indexOf('?') > 0) ? '&' : '?')
      var queryString = (params && joiner + params) || ''
      return url + queryString
    }
  
    // Based on npm package "query-string"
    function stringifyParams (obj) {
      return obj ? arrayMap(objectKeys(obj).sort(), function (key) {
        var val = obj[key]
  
        if (angular.isArray(val)) {
          return arrayMap(val.sort(), function (val2) {
            return encodeURIComponent(key) + '=' + encodeURIComponent(val2)
          }).join('&')
        }
  
        return encodeURIComponent(key) + '=' + encodeURIComponent(val)
      }).join('&') : ''
    }
  
    // http://ejohn.org/blog/partial-functions-in-javascript/
    function partial (fn) {
      var args = Array.prototype.slice.call(arguments, 1)
      return function () {
        var arg = 0
        for (var i = 0; i < args.length && arg < arguments.length; i++) {
          if (args[i] === undefined) args[i] = arguments[arg++]
        }
        return fn.apply(this, args)
      }
    }
  
    return $httpDecorator
  }
  