var querystring = require('querystring');
var request = require('request');

var endpoints = require('./endpoints');

/**
 * Encodes object as a querystring, to be used as the suffix of request URLs.
 * @param  {Object} obj
 * @return {String}
 */
exports.makeQueryString = function (obj) {
  var qs = querystring.stringify(obj)
  qs = qs.replace(/\!/g, "%21")
         .replace(/\'/g, "%27")
         .replace(/\(/g, "%28")
         .replace(/\)/g, "%29")
         .replace(/\*/g, "%2A");
  return qs
}

/**
 * For each `/:param` fragment in path, move the value in params
 * at that key to path. If the key is not found in params, throw.
 * Modifies both params and path values.
 *
 * @param  {Objet} params  Object used to build path.
 * @param  {String} path   String to transform.
 * @return {Undefined}
 *
 */
exports.moveParamsIntoPath = function (params, path) {
  var rgxParam = /\/:(\w+)/g
  var missingParamErr = null

  path = path.replace(rgxParam, function (hit) {
    var paramName = hit.slice(2)
    var suppliedVal = params[paramName]
    if (!suppliedVal) {
      throw new Error('Twit: Params object is missing a required parameter for this request: `'+paramName+'`')
    }
    var retVal = '/' + suppliedVal
    delete params[paramName]
    return retVal
  })
  return path
}

/**
 * When Twitter returns a response that looks like an error response,
 * use this function to attach the error info in the response body to `err`.
 *
 * @param  {Error} err   Error instance to which body info will be attached
 * @param  {Object} body JSON object that is the deserialized HTTP response body received from Twitter
 * @return {Undefined}
 */
exports.attachBodyInfoToError = function (err, body) {
  err.twitterReply = body;
  if (!body) {
    return
  }
  if (body.error) {
    // the body itself is an error object
    err.message = body.error
    err.allErrors = err.allErrors.concat([body])
  } else if (body.errors && body.errors.length) {
    // body contains multiple error objects
    err.message = body.errors[0].message;
    err.code = body.errors[0].code;
    err.allErrors = err.allErrors.concat(body.errors)
  }
}

exports.makeTwitError = function (message) {
  var err = new Error()
  if (message) {
    err.message = message
  }
  err.code = null
  err.allErrors = []
  err.twitterReply = null
  return err
}

/**
 * Get a bearer token for OAuth2
 * @param  {String}   consumer_key
 * @param  {String}   consumer_secret
 * @param  {Function} cb
 *
 * Calls `cb` with Error, String
 *
 * Error (if it exists) is guaranteed to be Twit error-formatted.
 * String (if it exists) is the bearer token received from Twitter.
 */
exports.getBearerToken = function (consumer_key, consumer_secret, cb) {
  // use OAuth 2 for app-only auth (Twitter requires this)
  // get a bearer token using our app's credentials
  var b64Credentials = new Buffer(consumer_key + ':' + consumer_secret).toString('base64');
  request.post({
    url: endpoints.API_HOST + 'oauth2/token',
    headers: {
      'Authorization': 'Basic ' + b64Credentials,
      'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
    },
    body: 'grant_type=client_credentials',
    json: true,
  }, function (err, res, body) {
    if (err) {
      var error = exports.makeTwitError(err.toString());
      exports.attachBodyInfoToError(error, body);
      return cb(error, body, res);
    }

    if ( !body ) {
      var error = exports.makeTwitError('Not valid reply from Twitter upon obtaining bearer token');
      exports.attachBodyInfoToError(error, body);
      return cb(error, body, res);
    }

    if (body.token_type !== 'bearer') {
      var error = exports.makeTwitError('Unexpected reply from Twitter upon obtaining bearer token');
      exports.attachBodyInfoToError(error, body);
      return cb(error, body, res);
    }

    return cb(err, body.access_token);
  })
}
