"use strict";
/**
 * Capture module.
 * @module aws_p
 */
var semver = require('semver');
var Aws = require('../segments/attributes/aws');
var contextUtils = require('../context_utils');
var Utils = require('../utils');
var logger = require('../logger');
var minVersion = '2.7.15';
var throttledErrorDefault = function throttledErrorDefault() {
    return false; // If the customer doesn't provide an aws-sdk with a throttled error function, we can't make assumptions.
};
/**
 * Configures the AWS SDK to automatically capture information for the segment.
 * All created clients will automatically be captured.  See 'captureAWSClient'
 * for additional details.
 * @param {AWS} awssdk - The Javascript AWS SDK.
 * @alias module:aws_p.captureAWS
 * @returns {AWS}
 * @see https://github.com/aws/aws-sdk-js
 */
var captureAWS = function captureAWS(awssdk) {
    if (!semver.gte(awssdk.VERSION, minVersion)) {
        throw new Error('AWS SDK version ' + minVersion + ' or greater required.');
    }
    for (var prop in awssdk) {
        if (awssdk[prop].serviceIdentifier) {
            var Service = awssdk[prop];
            Service.prototype.customizeRequests(captureAWSRequest);
        }
    }
    return awssdk;
};
/**
 * Configures any AWS Client instance to automatically capture information for the segment.
 * For manual mode, a param with key called 'Segment' is required as a part of the AWS
 * call paramaters, and must reference a Segment or Subsegment object.
 * @param {AWS.Service} service - An instance of a AWS service to wrap.
 * @alias module:aws_p.captureAWSClient
 * @returns {AWS.Service}
 * @see https://github.com/aws/aws-sdk-js
 */
var captureAWSClient = function captureAWSClient(service) {
    service.customizeRequests(captureAWSRequest);
    return service;
};
function captureAWSRequest(req) {
    var parent = contextUtils.resolveSegment(contextUtils.resolveManualSegmentParams(req.params));
    if (!parent) {
        var output = this.serviceIdentifier + '.' + req.operation;
        if (!contextUtils.isAutomaticMode()) {
            logger.getLogger().info('Call ' + output + ' requires a segment object' +
                ' on the request params as "XRaySegment" for tracing in manual mode. Ignoring.');
        }
        else {
            logger.getLogger().info('Call ' + output +
                ' is missing the sub/segment context for automatic mode. Ignoring.');
        }
        return req;
    }
    var throttledError = this.throttledError || throttledErrorDefault;
    var stack = (new Error()).stack;
    let subsegment;
    if (parent.notTraced) {
        subsegment = parent.addNewSubsegmentWithoutSampling(this.serviceIdentifier);
    }
    else {
        subsegment = parent.addNewSubsegment(this.serviceIdentifier);
    }
    var traceId = parent.segment ? parent.segment.trace_id : parent.trace_id;
    const data = parent.segment ? parent.segment.additionalTraceData : parent.additionalTraceData;
    var buildListener = function (req) {
        if (parent.noOp) {
            return;
        }
        let traceHeader = 'Root=' + traceId + ';Parent=' + subsegment.id +
            ';Sampled=' + (subsegment.notTraced ? '0' : '1');
        if (data != null) {
            for (const [key, value] of Object.entries(data)) {
                traceHeader += ';' + key + '=' + value;
            }
        }
        req.httpRequest.headers['X-Amzn-Trace-Id'] = traceHeader;
    };
    var completeListener = function (res) {
        subsegment.addAttribute('namespace', 'aws');
        subsegment.addAttribute('aws', new Aws(res, subsegment.name));
        var httpRes = res.httpResponse;
        if (httpRes) {
            subsegment.addAttribute('http', new HttpResponse(httpRes));
            if (httpRes.statusCode === 429 || (res.error && throttledError(res.error))) {
                subsegment.addThrottleFlag();
            }
        }
        if (res.error) {
            var err = { message: res.error.message, name: res.error.code, stack: stack };
            if (httpRes && httpRes.statusCode) {
                if (Utils.getCauseTypeFromHttpStatus(httpRes.statusCode) == 'error') {
                    subsegment.addErrorFlag();
                }
                subsegment.close(err, true);
            }
            else {
                subsegment.close(err);
            }
        }
        else {
            if (httpRes && httpRes.statusCode) {
                var cause = Utils.getCauseTypeFromHttpStatus(httpRes.statusCode);
                if (cause) {
                    subsegment[cause] = true;
                }
            }
            subsegment.close();
        }
    };
    req.on('beforePresign', function (req) {
        // Only the AWS Presigner triggers this event,
        // so we can rely on this event to notify us when
        // a request is for a presigned url
        parent.removeSubsegment(subsegment);
        parent.decrementCounter();
        req.removeListener('build', buildListener);
        req.removeListener('complete', completeListener);
    });
    req.on('build', buildListener).on('complete', completeListener);
    if (!req.__send) {
        req.__send = req.send;
        req.send = function (callback) {
            if (contextUtils.isAutomaticMode()) {
                var session = contextUtils.getNamespace();
                session.run(function () {
                    contextUtils.setSegment(subsegment);
                    req.__send(callback);
                });
            }
            else {
                req.__send(callback);
            }
        };
    }
}
function HttpResponse(res) {
    this.init(res);
}
HttpResponse.prototype.init = function init(res) {
    this.response = {
        status: res.statusCode || '',
    };
    if (res.headers && res.headers['content-length']) {
        this.response.content_length = res.headers['content-length'];
    }
};
module.exports.captureAWSClient = captureAWSClient;
module.exports.captureAWS = captureAWS;
