text: 'use strict'
  
  const Tags = require('opentracing').Tags
  const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
  const awsHelpers = require('./helpers')
  
  function createWrapRequest (tracer, config) {
    config = normalizeConfig(config)
  
    return function wrapRequest (send) {
      return function requestWithTrace (cb) {
        if (!this.service) return send.apply(this, arguments)
  
        const serviceIdentifier = this.service.serviceIdentifier
  
        if (!awsHelpers.isEnabled(serviceIdentifier, config[serviceIdentifier], this)) {
          return send.apply(this, arguments)
        }
  
        const serviceName = getServiceName(serviceIdentifier, tracer, config)
        const childOf = tracer.scope().active()
        const tags = {
          [Tags.SPAN_KIND]: 'client',
          'service.name': serviceName,
          'aws.operation': this.operation,
          'aws.region': this.service.config && this.service.config.region,
          'aws.service': this.service.api && this.service.api.className,
          'component': 'aws-sdk'
        }
  
        const span = tracer.startSpan('aws.request', {
          childOf,
          tags
        })
  
        this.on('complete', response => {
          if (!span) return
  
          awsHelpers.addResponseTags(span, response, serviceIdentifier, config, tracer)
          awsHelpers.finish(span, response.error)
        })
  
        analyticsSampler.sample(span, config.analytics)
  
        awsHelpers.requestInject(span, this, serviceIdentifier, tracer)
  
        const request = this
  
        return tracer.scope().activate(span, () => {
          let boundCb
          if (typeof cb === 'function') {
            boundCb = awsHelpers.wrapCb(cb, serviceIdentifier, tags, request, tracer, childOf)
          } else {
            boundCb = cb
          }
          return send.call(this, boundCb)
        })
      }
    }
  }
  
  function createWrapSetPromisesDependency (tracer, config, instrumenter, AWS) {
    return function wrapSetPromisesDependency (setPromisesDependency) {
      return function setPromisesDependencyWithTrace (dep) {
        const result = setPromisesDependency.apply(this, arguments)
  
        instrumenter.wrap(AWS.Request.prototype, 'promise', createWrapRequest(tracer, config))
  
        return result
      }
    }
  }
  
  function normalizeConfig (config) {
    const hooks = getHooks(config)
  
    return Object.assign({}, config, {
      splitByAwsService: config.splitByAwsService !== false,
      hooks
    })
  }
  
  function getHooks (config) {
    const noop = () => {}
    const request = (config.hooks && config.hooks.request) || noop
  
    return { request }
  }
  
  // TODO: test splitByAwsService when the test suite is fixed
  function getServiceName (serviceIdentifier, tracer, config) {
    const service = config.service || tracer._service
  
    return config.splitByAwsService
      ? `${service}-aws-${serviceIdentifier}`
      : service
  }
  
  // <2.1.35 has breaking changes for instrumentation
  // https://github.com/aws/aws-sdk-js/pull/629
  module.exports = [
    {
      name: 'aws-sdk',
      versions: ['>=2.3.0'],
      patch (AWS, tracer, config) {
        this.wrap(AWS.Request.prototype, 'promise', createWrapRequest(tracer, config))
        this.wrap(AWS.config, 'setPromisesDependency', createWrapSetPromisesDependency(tracer, config, this, AWS))
      },
      unpatch (AWS) {
        this.unwrap(AWS.Request.prototype, 'promise')
        this.unwrap(AWS.config, 'setPromisesDependency')
      }
    },
    {
      name: 'aws-sdk',
      versions: ['>=2.1.35'],
      patch (AWS, tracer, config) {
        this.wrap(AWS.Request.prototype, 'send', createWrapRequest(tracer, config))
      },
      unpatch (AWS) {
        this.wrap(AWS.Request.prototype, 'send')
      }
    }
  ]
  