"use strict";
/**
 * @module context_utils
 */
var cls = require('cls-hooked/context');
var logger = require('./logger');
var Segment = require('./segments/segment');
var Subsegment = require('./segments/attributes/subsegment');
var cls_mode = true;
var NAMESPACE = 'AWSXRay';
var SEGMENT = 'segment';
var contextOverride = false;
var contextUtils = {
    CONTEXT_MISSING_STRATEGY: {
        RUNTIME_ERROR: {
            contextMissing: function contextMissingRuntimeError(message) {
                throw new Error(message);
            }
        },
        LOG_ERROR: {
            contextMissing: function contextMissingLogError(message) {
                var err = new Error(message);
                logger.getLogger().error(err.stack);
            }
        },
        IGNORE_ERROR: {
            contextMissing: function contextMissingIgnoreError() {
            }
        }
    },
    contextMissingStrategy: {},
    /**
     * Resolves the segment or subsegment given manual mode and params on the call required.
     * @param [Segment|Subsegment] segment - The segment manually provided via params.XraySegment, if provided.
     * @returns {Segment|Subsegment}
     * @alias module:context_utils.resolveManualSegmentParams
     */
    resolveManualSegmentParams: function resolveManualSegmentParams(params) {
        if (params && !contextUtils.isAutomaticMode()) {
            var xraySegment = params.XRaySegment || params.XraySegment;
            var segment = params.Segment;
            var found = null;
            if (xraySegment && (xraySegment instanceof Segment || xraySegment instanceof Subsegment)) {
                found = xraySegment;
                delete params.XRaySegment;
                delete params.XraySegment;
            }
            else if (segment && (segment instanceof Segment || segment instanceof Subsegment)) {
                found = segment;
                delete params.Segment;
            }
            return found;
        }
    },
    /**
     * Gets current CLS namespace for X-Ray SDK or creates one if absent.
     * @returns {Namespace}
     * @alias module:context_utils.getNamespace
     */
    getNamespace: function getNamespace() {
        return cls.getNamespace(NAMESPACE) || cls.createNamespace(NAMESPACE);
    },
    /**
     * Resolves the segment or subsegment given manual or automatic mode.
     * @param [Segment|Subsegment] segment - The segment manually provided, if provided.
     * @returns {Segment|Subsegment}
     * @alias module:context_utils.resolveSegment
     */
    resolveSegment: function resolveSegment(segment) {
        if (cls_mode) {
            return this.getSegment();
        }
        else if (segment && !cls_mode) {
            return segment;
        }
        else if (!segment && !cls_mode) {
            contextUtils.contextMissingStrategy.contextMissing('No sub/segment specified. A sub/segment must be provided for manual mode.');
        }
    },
    /**
     * Returns the current segment or subsegment.  For use with in automatic mode only.
     * @returns {Segment|Subsegment}
     * @alias module:context_utils.getSegment
     */
    getSegment: function getSegment() {
        if (cls_mode) {
            var segment = contextUtils.getNamespace(NAMESPACE).get(SEGMENT);
            if (!segment) {
                contextUtils.contextMissingStrategy.contextMissing('Failed to get the current sub/segment from the context.');
            }
            else if (segment instanceof Segment && process.env.LAMBDA_TASK_ROOT && segment.facade == true) {
                segment.resolveLambdaTraceData();
            }
            return segment;
        }
        else {
            contextUtils.contextMissingStrategy.contextMissing('Cannot get sub/segment from context. Not supported in manual mode.');
        }
    },
    /**
     * Sets the current segment or subsegment.  For use with in automatic mode only.
     * @param [Segment|Subsegment] segment - The sub/segment to set.
     * @returns {Segment|Subsegment}
     * @alias module:context_utils.setSegment
     */
    setSegment: function setSegment(segment) {
        if (cls_mode) {
            if (!contextUtils.getNamespace(NAMESPACE).set(SEGMENT, segment)) {
                logger.getLogger().warn('Failed to set the current sub/segment on the context.');
            }
        }
        else {
            contextUtils.contextMissingStrategy.contextMissing('Cannot set sub/segment on context. Not supported in manual mode.');
        }
    },
    /**
     * Returns true if in automatic mode, otherwise false.
     * @returns {Segment|Subsegment}
     * @alias module:context_utils.isAutomaticMode
     */
    isAutomaticMode: function isAutomaticMode() {
        return cls_mode;
    },
    /**
     * Enables automatic mode. Automatic mode uses 'cls-hooked'.
     * @see https://github.com/jeff-lewis/cls-hooked
     * @alias module:context_utils.enableAutomaticMode
     */
    enableAutomaticMode: function enableAutomaticMode() {
        cls_mode = true;
        contextUtils.getNamespace(NAMESPACE);
        logger.getLogger().debug('Overriding AWS X-Ray SDK mode. Set to automatic mode.');
    },
    /**
     * Disables automatic mode. Current segment or subsegment then must be passed manually
     * via the parent optional on captureFunc, captureAsyncFunc etc.
     * @alias module:context_utils.enableManualMode
     */
    enableManualMode: function enableManualMode() {
        cls_mode = false;
        if (cls.getNamespace(NAMESPACE)) {
            cls.destroyNamespace(NAMESPACE);
        }
        logger.getLogger().debug('Overriding AWS X-Ray SDK mode. Set to manual mode.');
    },
    /**
     * Sets the context missing strategy if no context missing strategy is set using the environment variable with
     * key AWS_XRAY_CONTEXT_MISSING. The context missing strategy's contextMissing function will be called whenever
     * trace context is not found.
     * @param {string|function} strategy - The strategy to set. Valid string values are 'LOG_ERROR' and 'RUNTIME_ERROR'.
     *                                     Alternatively, a custom function can be supplied, which takes a error message string.
     */
    setContextMissingStrategy: function setContextMissingStrategy(strategy) {
        if (!contextOverride) {
            if (typeof strategy === 'string') {
                var lookupStrategy = contextUtils.CONTEXT_MISSING_STRATEGY[strategy.toUpperCase()];
                if (lookupStrategy) {
                    contextUtils.contextMissingStrategy.contextMissing = lookupStrategy.contextMissing;
                    if (process.env.AWS_XRAY_CONTEXT_MISSING) {
                        logger.getLogger().debug('AWS_XRAY_CONTEXT_MISSING is set. Configured context missing strategy to ' +
                            process.env.AWS_XRAY_CONTEXT_MISSING + '.');
                    }
                    else {
                        logger.getLogger().debug('Configured context missing strategy to: ' + strategy);
                    }
                }
                else {
                    throw new Error('Invalid context missing strategy: ' + strategy + '. Valid values are ' +
                        Object.keys(contextUtils.CONTEXT_MISSING_STRATEGY) + '.');
                }
            }
            else if (typeof strategy === 'function') {
                contextUtils.contextMissingStrategy.contextMissing = strategy;
                logger.getLogger().info('Configured custom context missing strategy to function: ' + strategy.name);
            }
            else {
                throw new Error('Context missing strategy must be either a string or a custom function.');
            }
        }
        else {
            logger.getLogger().warn('Ignoring call to setContextMissingStrategy as AWS_XRAY_CONTEXT_MISSING is set. ' +
                'The current context missing strategy will not be changed.');
        }
    }
};
if (process.env.AWS_XRAY_MANUAL_MODE) {
    cls_mode = false;
    logger.getLogger().debug('Starting the AWS X-Ray SDK in manual mode.');
}
else {
    cls.createNamespace(NAMESPACE);
    logger.getLogger().debug('Starting the AWS X-Ray SDK in automatic mode (default).');
}
if (process.env.AWS_XRAY_CONTEXT_MISSING) {
    contextUtils.setContextMissingStrategy(process.env.AWS_XRAY_CONTEXT_MISSING);
    contextOverride = true;
}
else {
    contextUtils.contextMissingStrategy.contextMissing = contextUtils.CONTEXT_MISSING_STRATEGY.LOG_ERROR.contextMissing;
    logger.getLogger().debug('Using default context missing strategy: LOG_ERROR');
}
module.exports = contextUtils;
