/*
	Licensed Material - Property of IBM

	(C) Copyright 2016 IBM Corp.

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/


'use strict';

var moduleName = 'get-auth-token';

var Q = require('q');
var errors = require('mdo-errors');
var strings = require('ibm-strings');
var log = require('npmlog');

var http = require('http');
var https = require('https');
var querystring = require('querystring');
var getCertificate = require('./get-certificate').getCertificate;

var REQUEST_TOKEN_PATH = '/api/az/v1/token';
var REQUEST_TOKEN_GRANT_TYPE = 'client_credentials';
var REQUEST_TOKEN_METHOD = 'POST';

/**
 * Obtains a OAuth token from the server for an adapter
 *
 * Parameters:
 *
 * options = {
 *    host: <host-dns-name>,          // Optional String. defaults to localhost
 *    port: <host-port>               // Optional Numeric. defaults to 9080
 *    runtime: <project-runtime>      // Mandatory String. Project name (aka runtime, project context root)
 * }
 *
 *  scope               // Optional String.  Confidential client scope; defaults to ''
 *  id                  // Optional String.  Confidential client id; defaults to 'test'
 *  secret              // Optional String.  Confidential client secret; defaults to 'test'
 *
 * Returns:
 *  Q.deferred.promise.
 *   On success, a JSON output with the following structure:
 *   { access_token: 'eyJhb.....'
 *     token_type: 'Bearer'
 *     expires_in: 3600,
 *     scope: 'test'
 *    }
 *
 *   On failure, an error is returned
 *
 */
function getAuthToken(options, scope, id, secret) {
  var deferred = Q.defer();

  log.level = global.MDO_CLI_LOG_LEVEL ? global.MDO_CLI_LOG_LEVEL : 'info';

  // Runtime is needed for full path
  if (!options.runtime) {
    var error = new Error('Missing argument \'runtime\'');
    error.name = 'InvalidArgumentError';
    log.verbose('getAuthToken', error);
    deferred.reject(error);
    return deferred.promise;
  }

  // Default values for dev/test Confidential Client
  var scope   = !scope  ? ''      : scope;
  var id      = !id     ? 'test' : id;
  var secret  = !secret ? 'test' : secret;

  var token;

  // Form parametes (x-www-form-urlencoded)
  var token_request_form = querystring.stringify({
    'grant_type' : REQUEST_TOKEN_GRANT_TYPE,
    'scope' : scope
  });

  // Create http request for OAuth token
  var token_request_options = {
    hostname: !options.hostname ? 'localhost' : options.hostname,
    protocol: !options.protocol ? 'http:' : options.protocol,
    port: !options.port ? 9080 : options.port,
    path: '/' + options.runtime + REQUEST_TOKEN_PATH,
    method: REQUEST_TOKEN_METHOD,
    headers: {
      'Authorization' : 'Basic ' + new Buffer(id + ':' + secret).toString('base64'),
      'Content-Type': 'application/x-www-form-urlencoded',
      'Content-Length' : token_request_form.length
    }
  };

  getCertificate(token_request_options, moduleName);

  var request;
  if (token_request_options.protocol === 'https:') {
    log.silly(moduleName, 'performing https request');
    request = https.request(token_request_options, callback);
  } else {
    log.silly(moduleName, 'performing http request');
    request = http.request(token_request_options, callback);
  }

  function callback(response) {
    var body = '';
    response.on('data', function(data) {
      body += data;
    });
    response.on('end', function() {
      if (body !== '') {
        token = JSON.parse(body);
        deferred.resolve(token);
      } else {
        log.verbose("got response but no token");
        deferred.reject(errors.getError('NOTOKEN', token_request_options.hostname,
                                                   token_request_options.port));
      }
    })
  }

  request.on('error', function(error) {
    log.verbose(error.message);
    error.description = error.message;
    deferred.reject(error);
  });

  // Write data to request token
  request.write(token_request_form);
  request.end();

  return deferred.promise;
};

module.exports = {
  getAuthToken: getAuthToken
};
