/*
 * decaffeinate suggestions:
 * DS101: Remove unnecessary use of Array.from
 * DS102: Remove unnecessary code created because of implicit returns
 * DS201: Simplify complex destructure assignments
 * DS207: Consider shorter variations of null checks
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */

module.exports = function (obj, methodName, prefix, logger) {
  let modifedMethodName
  const metrics = require('./index')

  if (typeof obj[methodName] !== 'function') {
    throw new Error(
      `[Metrics] expected object property '${methodName}' to be a function`
    )
  }

  const key = `${prefix}.${methodName}`

  const realMethod = obj[methodName]

  const splitPrefix = prefix.split('.')
  const startPrefix = splitPrefix[0]

  if (splitPrefix[1] != null) {
    modifedMethodName = `${splitPrefix[1]}_${methodName}`
  } else {
    modifedMethodName = methodName
  }
  return (obj[methodName] = function (...originalArgs) {
    const adjustedLength = Math.max(originalArgs.length, 1)
    const firstArgs = originalArgs.slice(0, adjustedLength - 1)
    const callback = originalArgs[adjustedLength - 1]

    if (callback == null || typeof callback !== 'function') {
      if (logger != null) {
        logger.debug(
          `[Metrics] expected wrapped method '${methodName}' to be invoked with a callback`
        )
      }
      return realMethod.apply(this, originalArgs)
    }

    const timer = new metrics.Timer(startPrefix, 1, {
      method: modifedMethodName,
    })

    return realMethod.call(
      this,
      ...Array.from(firstArgs),
      function (...callbackArgs) {
        const elapsedTime = timer.done()
        const possibleError = callbackArgs[0]
        if (possibleError != null) {
          metrics.inc(`${startPrefix}_result`, 1, {
            status: 'failed',
            method: modifedMethodName,
          })
        } else {
          metrics.inc(`${startPrefix}_result`, 1, {
            status: 'success',
            method: modifedMethodName,
          })
        }
        if (logger != null) {
          const loggableArgs = {}
          try {
            for (let idx = 0; idx < firstArgs.length; idx++) {
              const arg = firstArgs[idx]
              if (arg.toString().match(/^[0-9a-f]{24}$/)) {
                loggableArgs[`${idx}`] = arg
              }
            }
          } catch (error) {}
          logger.debug(
            { key, args: loggableArgs, elapsedTime },
            '[Metrics] timed async method call'
          )
        }
        return callback.apply(this, callbackArgs)
      }
    )
  })
}
