/*
 * Application Insights JavaScript SDK - Core, 3.2.0
 * Copyright (c) Microsoft and contributors. All rights reserved.
 */
import { isArray, isString, strLeft, strTrim } from "@nevware21/ts-utils";
import { _DYN_LENGTH, _DYN_SPAN_ID, _DYN_SPLIT, _DYN_TO_LOWER_CASE, _DYN_TRACE_FLAGS, _DYN_TRACE_ID, _DYN_VERSION } from "../__DynamicConstants";
import { generateW3CId } from "./CoreUtils";
import { findMetaTag, findNamedServerTiming } from "./EnvUtils";
import { STR_EMPTY } from "./InternalConstants";
// using {0,16} for leading and trailing whitespace just to constrain the possible runtime of a random string
var TRACE_PARENT_REGEX = /^([\da-f]{2})-([\da-f]{32})-([\da-f]{16})-([\da-f]{2})(-[^\s]{1,64})?$/i;
var DEFAULT_VERSION = "00";
var INVALID_VERSION = "ff";
var INVALID_TRACE_ID = "00000000000000000000000000000000";
var INVALID_SPAN_ID = "0000000000000000";
var SAMPLED_FLAG = 0x01;
function _isValid(value, len, invalidValue) {
    if (value && value[_DYN_LENGTH /* @min:%2elength */] === len && value !== invalidValue) {
        return !!value.match(/^[\da-f]*$/i);
    }
    return false;
}
function _formatValue(value, len, defValue) {
    if (_isValid(value, len)) {
        return value;
    }
    return defValue;
}
function _formatFlags(value) {
    if (isNaN(value) || value < 0 || value > 255) {
        value = 0x01;
    }
    var result = value.toString(16);
    while (result[_DYN_LENGTH /* @min:%2elength */] < 2) {
        result = "0" + result;
    }
    return result;
}
/**
 * Create a new ITraceParent instance using the provided values.
 * @param traceId - The traceId to use, when invalid a new random W3C id will be generated.
 * @param spanId - The parent/span id to use, a new random value will be generated if it is invalid.
 * @param flags - The traceFlags to use, defaults to zero (0) if not supplied or invalid
 * @param version - The version to used, defaults to version "01" if not supplied or invalid.
 * @returns
 */
export function createTraceParent(traceId, spanId, flags, version) {
    var _a;
    return _a = {},
        _a[_DYN_VERSION /* @min:version */] = _isValid(version, 2, INVALID_VERSION) ? version : DEFAULT_VERSION,
        _a[_DYN_TRACE_ID /* @min:traceId */] = isValidTraceId(traceId) ? traceId : generateW3CId(),
        _a[_DYN_SPAN_ID /* @min:spanId */] = isValidSpanId(spanId) ? spanId : strLeft(generateW3CId(), 16),
        _a.traceFlags = flags >= 0 && flags <= 0xFF ? flags : 1,
        _a;
}
/**
 * Attempt to parse the provided string as a W3C TraceParent header value (https://www.w3.org/TR/trace-context/#traceparent-header)
 *
 * @param value - The value to be parsed
 * @param selectIdx - If the found value is comma separated which is the preferred entry to select, defaults to the first
 * @returns
 */
export function parseTraceParent(value, selectIdx) {
    var _a;
    if (!value) {
        // Don't pass a null/undefined or empty string
        return null;
    }
    if (isArray(value)) {
        // The value may have been encoded on the page into an array so handle this automatically
        value = value[0] || "";
    }
    if (!value || !isString(value) || value[_DYN_LENGTH /* @min:%2elength */] > 8192) {
        // limit potential processing based on total length
        return null;
    }
    if (value.indexOf(",") !== -1) {
        var values = value[_DYN_SPLIT /* @min:%2esplit */](",");
        value = values[selectIdx > 0 && values[_DYN_LENGTH /* @min:%2elength */] > selectIdx ? selectIdx : 0];
    }
    // See https://www.w3.org/TR/trace-context/#versioning-of-traceparent
    var match = TRACE_PARENT_REGEX.exec(strTrim(value));
    if (!match || // No match
        match[1] === INVALID_VERSION || // version ff is forbidden
        match[2] === INVALID_TRACE_ID || // All zeros is considered to be invalid
        match[3] === INVALID_SPAN_ID) { // All zeros is considered to be invalid
        return null;
    }
    return _a = {
            version: (match[1] || STR_EMPTY)[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */](),
            traceId: (match[2] || STR_EMPTY)[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */](),
            spanId: (match[3] || STR_EMPTY)[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */]()
        },
        _a[_DYN_TRACE_FLAGS /* @min:traceFlags */] = parseInt(match[4], 16),
        _a;
}
/**
 * Is the provided W3c Trace Id a valid string representation, it must be a 32-character string
 * of lowercase hexadecimal characters for example, 4bf92f3577b34da6a3ce929d0e0e4736.
 * If all characters as zero (00000000000000000000000000000000) it will be considered an invalid value.
 * @param value - The W3c trace Id to be validated
 * @returns true if valid otherwise false
 */
export function isValidTraceId(value) {
    return _isValid(value, 32, INVALID_TRACE_ID);
}
/**
 * Is the provided W3c span id (aka. parent id) a valid string representation, it must be a 16-character
 * string of lowercase hexadecimal characters, for example, 00f067aa0ba902b7.
 * If all characters are zero (0000000000000000) this is considered an invalid value.
 * @param value - The W3c span id to be validated
 * @returns true if valid otherwise false
 */
export function isValidSpanId(value) {
    return _isValid(value, 16, INVALID_SPAN_ID);
}
/**
 * Validates that the provided ITraceParent instance conforms to the currently supported specifications
 * @param value
 * @returns
 */
export function isValidTraceParent(value) {
    if (!value ||
        !_isValid(value[_DYN_VERSION /* @min:%2eversion */], 2, INVALID_VERSION) ||
        !_isValid(value[_DYN_TRACE_ID /* @min:%2etraceId */], 32, INVALID_TRACE_ID) ||
        !_isValid(value[_DYN_SPAN_ID /* @min:%2espanId */], 16, INVALID_SPAN_ID) ||
        !_isValid(_formatFlags(value[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */]), 2)) {
        // Each known field must contain a valid value
        return false;
    }
    return true;
}
/**
 * Is the parsed traceParent indicating that the trace is currently sampled.
 * @param value - The parsed traceParent value
 * @returns
 */
export function isSampledFlag(value) {
    if (isValidTraceParent(value)) {
        return (value[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */] & SAMPLED_FLAG) === SAMPLED_FLAG;
    }
    return false;
}
/**
 * Format the ITraceParent value as a string using the supported and know version formats.
 * So even if the passed traceParent is a later version the string value returned from this
 * function will convert it to only the known version formats.
 * This currently only supports version "00" and invalid "ff"
 * @param value - The parsed traceParent value
 * @returns
 */
export function formatTraceParent(value) {
    if (value) {
        // Special Note: This only supports formatting as version 00, future versions should encode any known supported version
        // So parsing a future version will populate the correct version value but reformatting will reduce it to version 00.
        var flags = _formatFlags(value[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */]);
        if (!_isValid(flags, 2)) {
            flags = "01";
        }
        var version = value[_DYN_VERSION /* @min:%2eversion */] || DEFAULT_VERSION;
        if (version !== "00" && version !== "ff") {
            // Reduce version to "00"
            version = DEFAULT_VERSION;
        }
        // Format as version 00
        return "".concat(version.toLowerCase(), "-").concat(_formatValue(value.traceId, 32, INVALID_TRACE_ID).toLowerCase(), "-").concat(_formatValue(value.spanId, 16, INVALID_SPAN_ID).toLowerCase(), "-").concat(flags.toLowerCase());
    }
    return "";
}
/**
 * Helper function to fetch the passed traceparent from the page, looking for it as a meta-tag or a Server-Timing header.
 * @param selectIdx - If the found value is comma separated which is the preferred entry to select, defaults to the first
 * @returns
 */
export function findW3cTraceParent(selectIdx) {
    var name = "traceparent";
    var traceParent = parseTraceParent(findMetaTag(name), selectIdx);
    if (!traceParent) {
        traceParent = parseTraceParent(findNamedServerTiming(name), selectIdx);
    }
    return traceParent;
}
//# sourceMappingURL=W3cTraceParent.js.map