var util = require('../util');
var JsonBuilder = require('../json/builder');
var JsonParser = require('../json/parser');
var populateHostPrefix = require('./helpers').populateHostPrefix;

function buildRequest(req) {
  var httpRequest = req.httpRequest;
  var api = req.service.api;
  var target = api.targetPrefix + '.' + api.operations[req.operation].name;
  var version = api.jsonVersion || '1.0';
  var input = api.operations[req.operation].input;
  var builder = new JsonBuilder();

  if (version === 1) version = '1.0';

  if (api.awsQueryCompatible) {
    if (!httpRequest.params) {
      httpRequest.params = {};
    }
    // because Query protocol does this.
    Object.assign(httpRequest.params, req.params);
  }

  httpRequest.body = builder.build(req.params || {}, input);
  httpRequest.headers['Content-Type'] = 'application/x-amz-json-' + version;
  httpRequest.headers['X-Amz-Target'] = target;

  populateHostPrefix(req);
}

function extractError(resp) {
  var error = {};
  var httpResponse = resp.httpResponse;

  error.code = httpResponse.headers['x-amzn-errortype'] || 'UnknownError';
  if (typeof error.code === 'string') {
    error.code = error.code.split(':')[0];
  }

  if (httpResponse.body.length > 0) {
    try {
      var e = JSON.parse(httpResponse.body.toString());

      var code = e.__type || e.code || e.Code;
      if (code) {
        error.code = code.split('#').pop();
      }
      if (error.code === 'RequestEntityTooLarge') {
        error.message = 'Request body must be less than 1 MB';
      } else {
        error.message = (e.message || e.Message || null);
      }

      // The minimized models do not have error shapes, so
      // without expanding the model size, it's not possible
      // to validate the response shape (members) or
      // check if any are sensitive to logging.

      // Assign the fields as non-enumerable, allowing specific access only.
      for (var key in e || {}) {
        if (key === 'code' || key === 'message') {
          continue;
        }
        error['[' + key + ']'] = 'See error.' + key + ' for details.';
        Object.defineProperty(error, key, {
          value: e[key],
          enumerable: false,
          writable: true
        });
      }
    } catch (e) {
      error.statusCode = httpResponse.statusCode;
      error.message = httpResponse.statusMessage;
    }
  } else {
    error.statusCode = httpResponse.statusCode;
    error.message = httpResponse.statusCode.toString();
  }

  resp.error = util.error(new Error(), error);
}

function extractData(resp) {
  var body = resp.httpResponse.body.toString() || '{}';
  if (resp.request.service.config.convertResponseTypes === false) {
    resp.data = JSON.parse(body);
  } else {
    var operation = resp.request.service.api.operations[resp.request.operation];
    var shape = operation.output || {};
    var parser = new JsonParser();
    resp.data = parser.parse(body, shape);
  }
}

/**
 * @api private
 */
module.exports = {
  buildRequest: buildRequest,
  extractError: extractError,
  extractData: extractData
};
