text: 'use strict';
  
  // var debug = require('debug')('sslayer:ScreenShotLayerHttpClient');
  var Promise = require('bluebird');
  var utils = require('./utils');
  var OPTIONAL_REGEX = /^optional!/;
  var _ = require('lodash');
  
  /**
   * Create an API method from the declared spec.
   *
   * @param [spec.method='GET'] Request Method (POST, GET, DELETE, PUT)
   * @param [spec.path=''] Path to be appended to the API BASE_PATH, joined with
   *  the instance's path (e.g. 'captureScreenshot')
   * @param [spec.required=[]] Array of required arguments in the order that they
   *  must be passed by the consumer of the API. Subsequent optional arguments are
   *  optionally passed through a hash (Object) as the penultimate argument
   *  (preceding the also-optional callback argument
   * @param [spec.encode] Function for mutating input parameters to a method.
   *  Usefully for applying transforms to data on a per-method basis.
   */
  
  module.exports = {
  
    sslayerMethod: function (spec) {
      var commandPath = typeof spec.path === 'function' ? spec.path
        : utils.makeURLInterpolator(spec.path || '');
      var requestMethod = (spec.method || 'GET').toUpperCase();
      var urlParams = spec.urlParams || [];
      var encode = spec.encode || function (data) {
        return data;
      };
      return function () {
        var self = this;
        var args = [].slice.call(arguments);
        var callback = typeof args[args.length - 1] === 'function' && args.pop();
        var urlData = this.createUrlData();
  
        return this.wrapTimeout(new Promise((function (resolve, reject) {
          for (var i = 0, l = urlParams.length; i < l; ++i) {
            // Note that we shift the args array after every iteration so this just
            // grabs the "next" argument for use as a URL parameter.
            var arg = args[0];
  
            var param = urlParams[i];
  
            var isOptional = OPTIONAL_REGEX.test(param);
            param = param.replace(OPTIONAL_REGEX, '');
  
            if (!arg) {
              if (isOptional) {
                urlData[param] = '';
                continue;
              }
  
              return reject(new Error(
                'sslayer: Argument "' + urlParams[i] + '" required, but got: ' + arg +
                ' (on API request to ' + requestMethod + ' ' + commandPath + ')'
              ));
            }
  
            urlData[param] = args.shift();
          }
  
          var data = encode(utils.getDataFromArgs(args));
          var opts = utils.getOptionsFromArgs(args);
  
          if (args.length) {
            var err = new Error('sslayer: Unknown arguments (' + args + ').');
            return reject(err);
          }
  
  
          var requestPath = this.createFullPath(commandPath, urlData);
  
          function requestCallback(err, response) {
            if (err) {
              reject(err);
            } else {
              resolve(
                spec.transformResponseData ?
                  spec.transformResponseData(response) :
                  response
              );
            }
          }
  
          var options = { headers: Object.assign(opts.headers, spec.headers) };
          options.useBody = spec.useBody || false;
          options.authorization = spec.authorization || '';
          options.contentType = spec.contentType || '';
          self._request(requestMethod, requestPath, data, opts.auth, options, requestCallback);
        }).bind(this)), callback);
      };
    },
  
    setMetadata: function (id, key, value, auth, cb) {
      var self = this;
      var data = key;
      var isObject = _.isPlainObject(key);
      // We assume null for an empty object
      var isNull = data === null || (isObject && !Object.keys(data).length);
  
      // Allow optional passing of auth & cb:
      if ((isNull || isObject) && typeof value === 'string') {
        auth = value;
      } else if (typeof auth !== 'string') {
        if (!cb && typeof auth === 'function') {
          cb = auth;
        }
        auth = null;
      }
  
      var urlData = this.createUrlData();
      var path = this.createFullPath('/' + id, urlData);
  
      return this.wrapTimeout(new Promise((function (resolve, reject) {
        if (isNull) {
          // Reset metadata:
          sendMetadata(null, auth);
        } else if (!isObject) {
          // Set individual metadata property:
          var metadata = {};
          metadata[key] = value;
          sendMetadata(metadata, auth);
        } else {
          // Set entire metadata object after resetting it:
          this._request('POST', path, {
            metadata: null
          }, auth, {}, function (err) {
            if (err) {
              return reject(err);
            }
            sendMetadata(data, auth);
          });
        }
  
        function sendMetadata(metadata, auth) {
          self._request('POST', path, {
            metadata: metadata
          }, auth, {}, function (err, response) {
            if (err) {
              reject(err);
            } else {
              resolve(response.metadata);
            }
          });
        }
      }).bind(this)), cb);
    },
  
    getMetadata: function (id, auth, cb) {
      if (!cb && typeof auth === 'function') {
        cb = auth;
        auth = null;
      }
  
      var urlData = this.createUrlData();
      var path = this.createFullPath('/' + id, urlData);
  
      return this.wrapTimeout(new Promise((function (resolve, reject) {
        this._request('GET', path, {}, auth, {}, function (err, response) {
          if (err) {
            reject(err);
          } else {
            resolve(response.metadata);
          }
        });
      }).bind(this)), cb);
    }
  
  };
  