var AWS = require('../core');
var s3util = require('./s3util');
var regionUtil = require('../region_config');

AWS.util.update(AWS.S3Control.prototype, {
  /**
   * @api private
   */
  setupRequestListeners: function setupRequestListeners(request) {
    request.addListener('extractError', this.extractHostId);
    request.addListener('extractData', this.extractHostId);
    request.addListener('validate', this.validateAccountId);

    var isArnInBucket = s3util.isArnInParam(request, 'Bucket');
    var isArnInName = s3util.isArnInParam(request, 'Name');

    if (isArnInBucket) {
      request._parsedArn = AWS.util.ARN.parse(request.params['Bucket']);
      request.addListener('validate', this.validateOutpostsBucketArn);
      request.addListener('validate', s3util.validateOutpostsArn);
      request.addListener('afterBuild', this.addOutpostIdHeader);
    } else if (isArnInName) {
      request._parsedArn = AWS.util.ARN.parse(request.params['Name']);
      request.addListener('validate', s3util.validateOutpostsAccessPointArn);
      request.addListener('validate', s3util.validateOutpostsArn);
      request.addListener('afterBuild', this.addOutpostIdHeader);
    }

    if (isArnInBucket || isArnInName) {
      request.addListener('validate', s3util.validateArnRegion);
      request.addListener('validate', this.validateArnAccountWithParams, true);
      request.addListener('validate', s3util.validateArnAccount);
      request.addListener('validate', s3util.validateArnService);
      request.addListener('build', this.populateParamFromArn, true);
      request.addListener('build', this.populateUriFromArn);
      request.addListener('build', s3util.validatePopulateUriFromArn);
    }

    if (request.params.OutpostId &&
        (request.operation === 'createBucket' ||
          request.operation === 'listRegionalBuckets')) {
      request.addListener('build', this.populateEndpointForOutpostId);
    }
  },

  /**
   * Adds outpostId header
   */
  addOutpostIdHeader: function addOutpostIdHeader(req) {
    req.httpRequest.headers['x-amz-outpost-id'] = req._parsedArn.outpostId;
  },

  /**
   * Validate Outposts ARN supplied in Bucket parameter is a valid bucket name
   */
  validateOutpostsBucketArn: function validateOutpostsBucketArn(req) {
    var parsedArn = req._parsedArn;

    //can be ':' or '/'
    var delimiter = parsedArn.resource['outpost'.length];

    if (parsedArn.resource.split(delimiter).length !== 4) {
      throw AWS.util.error(new Error(), {
        code: 'InvalidARN',
        message: 'Bucket ARN should have two resources outpost/{outpostId}/bucket/{accesspointName}'
      });
    }

    var bucket = parsedArn.resource.split(delimiter)[3];
    if (!s3util.dnsCompatibleBucketName(bucket) || bucket.match(/\./)) {
      throw AWS.util.error(new Error(), {
        code: 'InvalidARN',
        message: 'Bucket ARN is not DNS compatible. Got ' + bucket
      });
    }

    //set parsed valid bucket
    req._parsedArn.bucket = bucket;
  },

  /**
   * @api private
   */
  populateParamFromArn: function populateParamFromArn(req) {
    var parsedArn = req._parsedArn;
    if (s3util.isArnInParam(req, 'Bucket')) {
      req.params.Bucket = parsedArn.bucket;
    } else if (s3util.isArnInParam(req, 'Name')) {
      req.params.Name = parsedArn.accessPoint;
    }
  },

  /**
   * Populate URI according to the ARN
   */
  populateUriFromArn: function populateUriFromArn(req) {
    var parsedArn = req._parsedArn;

    var endpoint = req.httpRequest.endpoint;
    var useArnRegion = req.service.config.s3UseArnRegion;

    endpoint.hostname = [
      's3-outposts',
      useArnRegion ? parsedArn.region : req.service.config.region,
      'amazonaws.com'
    ].join('.');
    endpoint.host = endpoint.hostname;
  },

  /**
   * @api private
   */
  populateEndpointForOutpostId: function populateEndpointForOutpostId(req) {
    var endpoint = req.httpRequest.endpoint;
    endpoint.hostname = [
      's3-outposts',
      req.service.config.region,
      'amazonaws.com'
    ].join('.');
    endpoint.host = endpoint.hostname;
  },

  /**
   * @api private
   */
  extractHostId: function(response) {
    var hostId = response.httpResponse.headers ? response.httpResponse.headers['x-amz-id-2'] : null;
    response.extendedRequestId = hostId;
    if (response.error) {
      response.error.extendedRequestId = hostId;
    }
  },

  /**
   * @api private
   */
  validateArnAccountWithParams: function validateArnAccountWithParams(req) {
    var params = req.params;
    var inputModel = req.service.api.operations[req.operation].input;
    if (inputModel.members.AccountId) {
      var parsedArn = req._parsedArn;
      if (parsedArn.accountId) {
        if (params.AccountId) {
          if (params.AccountId !== parsedArn.accountId) {
            throw AWS.util.error(
              new Error(),
              {code: 'ValidationError', message: 'AccountId in ARN and request params should be same.'}
            );
          }
        } else {
          // Store accountId from ARN in params
          params.AccountId = parsedArn.accountId;
        }
      }
    }
  },

  /**
   * @api private
   */
  validateAccountId: function(request) {
    var params = request.params;
    if (!Object.prototype.hasOwnProperty.call(params, 'AccountId')) return;
    var accountId = params.AccountId;
    //validate type
    if (typeof accountId !== 'string') {
      throw AWS.util.error(
        new Error(),
        {code: 'ValidationError', message: 'AccountId must be a string.'}
      );
    }
    //validate length
    if (accountId.length < 1 || accountId.length > 63) {
      throw AWS.util.error(
        new Error(),
        {code: 'ValidationError', message: 'AccountId length should be between 1 to 63 characters, inclusive.'}
      );
    }
    //validate pattern
    var hostPattern = /^[a-zA-Z0-9]{1}$|^[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]$/;
    if (!hostPattern.test(accountId)) {
      throw AWS.util.error(new Error(),
        {code: 'ValidationError', message: 'AccountId should be hostname compatible. AccountId: ' + accountId});
    }
  },

  /**
   * @api private
   */
  getSigningName: function getSigningName(req) {
    var _super = AWS.Service.prototype.getSigningName;
    if (req && req._parsedArn && req._parsedArn.service) {
      return req._parsedArn.service;
    } else if (req.params.OutpostId &&
      (req.operation === 'createBucket' ||
        req.operation === 'listRegionalBuckets')) {
      return 's3-outposts';
    } else {
      return _super.call(this, req);
    }
  },
});
