/*
 * Application Insights JavaScript SDK - Core, 3.3.10
 * Copyright (c) Microsoft and contributors. All rights reserved.
 */
var _a, _b;
import { arrForEach, arrIndexOf, dumpObj, getDocument, getLazy, getNavigator, isArray, isFunction, isNullOrUndefined, isString, isTruthy, isUndefined, objForEachKey, strEndsWith, strIndexOf, strLeft, strSubstring, strTrim, utcNow } from "@nevware21/ts-utils";
import { cfgDfMerge } from "../Config/ConfigDefaultHelpers";
import { createDynamicConfig, onConfigChange } from "../Config/DynamicConfig";
import { _DYN_ENABLED, _DYN_LENGTH, _DYN_LOGGER, _DYN_PUSH, _DYN_SPLICE, _DYN_SPLIT, _DYN_USER_AGENT } from "../__DynamicConstants";
import { _throwInternal } from "./DiagnosticLogger";
import { getLocation, isIE } from "./EnvUtils";
import { getExceptionName, isNotNullOrUndefined, setValue, strContains } from "./HelperFuncs";
import { STR_DOMAIN, STR_EMPTY, STR_PATH, UNDEFINED_VALUE } from "./InternalConstants";
var strToGMTString = "toGMTString";
var strToUTCString = "toUTCString";
var strCookie = "cookie";
var strExpires = "expires";
var strIsCookieUseDisabled = "isCookieUseDisabled";
var strDisableCookiesUsage = "disableCookiesUsage";
var strConfigCookieMgr = "_ckMgr";
var _supportsCookies = null;
var _allowUaSameSite = null;
var _parsedCookieValue = null;
var _doc;
var _cookieCache = {};
var _globalCookieConfig = {};
// // `isCookieUseDisabled` is deprecated, so explicitly casting as a key of IConfiguration to avoid typing error
// // when both isCookieUseDisabled and disableCookiesUsage are used disableCookiesUsage will take precedent, which is
// // why its listed first
/**
 * Set the supported dynamic config values as undefined (or an empty object) so that
 * any listeners will be informed of any changes.
 * Explicitly NOT including the deprecated `isCookieUseDisabled` as we don't want to support
 * the v1 deprecated field as dynamic for updates
 */
var rootDefaultConfig = (_a = {
        cookieCfg: cfgDfMerge((_b = {},
            _b[STR_DOMAIN] = { fb: "cookieDomain", dfVal: isNotNullOrUndefined },
            _b.path = { fb: "cookiePath", dfVal: isNotNullOrUndefined },
            _b.enabled = UNDEFINED_VALUE,
            _b.ignoreCookies = UNDEFINED_VALUE,
            _b.blockedCookies = UNDEFINED_VALUE,
            _b.disableCookieDefer = false,
            _b)),
        cookieDomain: UNDEFINED_VALUE,
        cookiePath: UNDEFINED_VALUE
    },
    _a[strDisableCookiesUsage] = UNDEFINED_VALUE,
    _a);
function _getDoc() {
    !_doc && (_doc = getLazy(function () { return getDocument(); }));
}
/**
 * @ignore
 * DO NOT USE or export from the module, this is exposed as public to support backward compatibility of previous static utility methods only.
 * If you want to manager cookies either use the ICookieMgr available from the core instance via getCookieMgr() or create
 * your own instance of the CookieMgr and use that.
 * Using this directly for enabling / disabling cookie handling will not only affect your usage but EVERY user of cookies.
 * Example, if you are using a shared component that is also using Application Insights you will affect their cookie handling.
 * @param logger - The DiagnosticLogger to use for reporting errors.
 */
function _gblCookieMgr(config, logger) {
    // Stash the global instance against the BaseCookieMgr class
    var inst = createCookieMgr[strConfigCookieMgr] || _globalCookieConfig[strConfigCookieMgr];
    if (!inst) {
        // Note: not using the getSetValue() helper as that would require always creating a temporary cookieMgr
        // that ultimately is never used
        inst = createCookieMgr[strConfigCookieMgr] = createCookieMgr(config, logger);
        _globalCookieConfig[strConfigCookieMgr] = inst;
    }
    return inst;
}
function _isMgrEnabled(cookieMgr) {
    if (cookieMgr) {
        return cookieMgr.isEnabled();
    }
    return true;
}
function _isIgnoredCookie(cookieMgrCfg, name) {
    if (name && cookieMgrCfg && isArray(cookieMgrCfg.ignoreCookies)) {
        return arrIndexOf(cookieMgrCfg.ignoreCookies, name) !== -1;
    }
    return false;
}
function _isBlockedCookie(cookieMgrCfg, name) {
    if (name && cookieMgrCfg && isArray(cookieMgrCfg.blockedCookies)) {
        if (arrIndexOf(cookieMgrCfg.blockedCookies, name) !== -1) {
            return true;
        }
    }
    return _isIgnoredCookie(cookieMgrCfg, name);
}
function _isCfgEnabled(rootConfig, cookieMgrConfig) {
    var isCfgEnabled = cookieMgrConfig[_DYN_ENABLED /* @min:%2eenabled */];
    if (isNullOrUndefined(isCfgEnabled)) {
        // Set the enabled from the provided setting or the legacy root values
        var cookieEnabled = void 0;
        // This field is deprecated and dynamic updates will not be fully supported
        if (!isUndefined(rootConfig[strIsCookieUseDisabled])) {
            cookieEnabled = !rootConfig[strIsCookieUseDisabled];
        }
        // If this value is defined it takes precedent over the above
        if (!isUndefined(rootConfig[strDisableCookiesUsage])) {
            cookieEnabled = !rootConfig[strDisableCookiesUsage];
        }
        // Not setting the cookieMgrConfig.enabled as that will update (set) the global dynamic config
        // So future "updates" then may not be as expected
        isCfgEnabled = cookieEnabled;
    }
    return isCfgEnabled;
}
/**
 * Helper to return the ICookieMgr from the core (if not null/undefined) or a default implementation
 * associated with the configuration or a legacy default.
 * @param core - The AppInsightsCore instance to get the cookie manager from
 * @param config - The config to use if the core is not available
 * @returns
 */
export function safeGetCookieMgr(core, config) {
    var cookieMgr;
    if (core) {
        // Always returns an instance
        cookieMgr = core.getCookieMgr();
    }
    else if (config) {
        var cookieCfg = config.cookieCfg;
        if (cookieCfg && cookieCfg[strConfigCookieMgr]) {
            cookieMgr = cookieCfg[strConfigCookieMgr];
        }
        else {
            cookieMgr = createCookieMgr(config);
        }
    }
    if (!cookieMgr) {
        // Get or initialize the default global (legacy) cookie manager if we couldn't find one
        cookieMgr = _gblCookieMgr(config, (core || {})[_DYN_LOGGER /* @min:%2elogger */]);
    }
    return cookieMgr;
}
export function createCookieMgr(rootConfig, logger) {
    var cookieMgrConfig;
    var _path;
    var _domain;
    var unloadHandler;
    // Explicitly checking against false, so that setting to undefined will === true
    var _enabled;
    var _getCookieFn;
    var _setCookieFn;
    var _delCookieFn;
    // Buffer for storing cookie operations when cookies are disabled
    // null = deferral disabled, array = deferral enabled with pending operations
    var _pendingCookies = [];
    // Helper function to format deletion cookie value
    function _formatDeletionValue(path) {
        var _a;
        var values = (_a = {},
            _a[STR_PATH] = path ? path : "/",
            _a[strExpires] = "Thu, 01 Jan 1970 00:00:01 GMT",
            _a);
        if (!isIE()) {
            // Set max age to expire now
            values["max-age"] = "0";
        }
        return _formatCookieValue(STR_EMPTY, values);
    }
    // Helper function to format a cookie value with all attributes
    function _formatSetCookieValue(value, maxAgeSec, domain, path) {
        var values = {};
        var theValue = strTrim(value || STR_EMPTY);
        var idx = strIndexOf(theValue, ";");
        if (idx !== -1) {
            theValue = strTrim(strLeft(value, idx));
            values = _extractParts(strSubstring(value, idx + 1));
        }
        // Only update domain if not already present (isUndefined) and the value is truthy (not null, undefined or empty string)
        setValue(values, STR_DOMAIN, domain || _domain, isTruthy, isUndefined);
        if (!isNullOrUndefined(maxAgeSec)) {
            var _isIE = isIE();
            if (isUndefined(values[strExpires])) {
                var nowMs = utcNow();
                // Only add expires if not already present
                var expireMs = nowMs + (maxAgeSec * 1000);
                // Sanity check, if zero or -ve then ignore
                if (expireMs > 0) {
                    var expiry = new Date();
                    expiry.setTime(expireMs);
                    setValue(values, strExpires, _formatDate(expiry, !_isIE ? strToUTCString : strToGMTString) || _formatDate(expiry, _isIE ? strToGMTString : strToUTCString) || STR_EMPTY, isTruthy);
                }
            }
            if (!_isIE) {
                // Only replace if not already present
                setValue(values, "max-age", STR_EMPTY + maxAgeSec, null, isUndefined);
            }
        }
        var location = getLocation();
        if (location && location.protocol === "https:") {
            setValue(values, "secure", null, null, isUndefined);
            // Only set same site if not also secure
            if (_allowUaSameSite === null) {
                _allowUaSameSite = !uaDisallowsSameSiteNone((getNavigator() || {})[_DYN_USER_AGENT /* @min:%2euserAgent */]);
            }
            if (_allowUaSameSite) {
                setValue(values, "SameSite", "None", null, isUndefined);
            }
        }
        setValue(values, STR_PATH, path || _path, null, isUndefined);
        return _formatCookieValue(theValue, values);
    }
    // Helper function to remove any existing pending operations for a cookie name
    function _removePendingCookie(name) {
        if (_pendingCookies) {
            // Remove all existing entries for this cookie name (iterate backwards to handle multiple entries safely)
            for (var i = _pendingCookies[_DYN_LENGTH /* @min:%2elength */] - 1; i >= 0; i--) {
                if (_pendingCookies[i].n === name) {
                    _pendingCookies[_DYN_SPLICE /* @min:%2esplice */](i, 1);
                }
            }
        }
    }
    // Helper function to flush pending cookies when cookies become enabled
    function _flushPendingCookies() {
        if (areCookiesSupported(logger) && _pendingCookies) {
            // Process all pending cookie operations in order
            arrForEach(_pendingCookies, function (pendingData) {
                if (!_isBlockedCookie(cookieMgrConfig, pendingData.n)) {
                    if (pendingData.o === 0 /* ePendingOp.Set */) {
                        // Apply the cached cookie value directly
                        _setCookieFn(pendingData.n, pendingData.v);
                    }
                    else if (pendingData.o === 1 /* ePendingOp.Purge */) {
                        // Apply the cached deletion
                        _delCookieFn(pendingData.n, pendingData.v);
                    }
                }
            });
            // Clear the cache after flushing
            _pendingCookies = [];
        }
    }
    // Make sure the root config is dynamic as it may be the global config
    rootConfig = createDynamicConfig(rootConfig || _globalCookieConfig, null, logger).cfg;
    // Will get recalled if the referenced configuration is changed
    unloadHandler = onConfigChange(rootConfig, function (details) {
        // Make sure the root config has all of the the defaults to the root config to ensure they are dynamic
        details.setDf(details.cfg, rootDefaultConfig);
        // Create and apply the defaults to the cookieCfg element
        cookieMgrConfig = details.ref(details.cfg, "cookieCfg"); // details.setDf(details.cfg.cookieCfg, defaultConfig);
        _path = cookieMgrConfig[STR_PATH /* @min:%2epath */] || "/";
        _domain = cookieMgrConfig[STR_DOMAIN /* @min:%2edomain */];
        // Handle deferral state changes based on disableCookieDefer setting
        if (cookieMgrConfig.disableCookieDefer) {
            // When deferral is disabled, set to null to disable any deferral behavior
            // All pending operations are dropped
            _pendingCookies = null;
        }
        else if (_pendingCookies === null) {
            // When deferral is enabled and was previously disabled, initialize empty buffer
            _pendingCookies = [];
        }
        // Check if enabled state is changing
        var wasEnabled = _enabled;
        // Explicitly checking against false, so that setting to undefined will === true
        _enabled = _isCfgEnabled(rootConfig, cookieMgrConfig) !== false;
        _getCookieFn = cookieMgrConfig.getCookie || _getCookieValue;
        _setCookieFn = cookieMgrConfig.setCookie || _setCookieValue;
        _delCookieFn = cookieMgrConfig.delCookie || _setCookieValue;
        // If cookies were just enabled via config change and we have pending cookies, flush them
        if (!wasEnabled && _enabled && _pendingCookies) {
            _flushPendingCookies();
        }
    }, logger);
    var cookieMgr = {
        isEnabled: function () {
            var enabled = _isCfgEnabled(rootConfig, cookieMgrConfig) !== false && _enabled && areCookiesSupported(logger);
            // Using an indirect lookup for any global cookie manager to support tree shaking for SDK's
            // that don't use the "applicationinsights-core" version of the default cookie function
            var gblManager = _globalCookieConfig[strConfigCookieMgr];
            if (enabled && gblManager && cookieMgr !== gblManager) {
                // Make sure the GlobalCookie Manager instance (if not this instance) is also enabled.
                // As the global (deprecated) functions may have been called (for backward compatibility)
                enabled = _isMgrEnabled(gblManager);
            }
            return enabled;
        },
        setEnabled: function (value) {
            // Change the default config and allow the asynchronous dynamic config logic
            // to process all of the changes at once (like handling change to the enabled and
            // disabling caching at the same time)
            cookieMgrConfig[_DYN_ENABLED /* @min:%2eenabled */] = value;
            // If this value is defined someone else might be listening to it so also update it,
            // this also handles the edge case if the default above is not dynamic
            if (!isUndefined(rootConfig[strDisableCookiesUsage])) {
                rootConfig[strDisableCookiesUsage] = !value;
            }
        },
        set: function (name, value, maxAgeSec, domain, path) {
            var result = false;
            var isBlocked = _isBlockedCookie(cookieMgrConfig, name);
            if (!isBlocked) {
                var cookieValue = _formatSetCookieValue(value, maxAgeSec, domain, path);
                if (_isMgrEnabled(cookieMgr)) {
                    _setCookieFn(name, cookieValue);
                    result = true;
                }
                else if (_pendingCookies) {
                    // Defer the fully formatted cookie value if cookies are disabled and deferral is enabled
                    // Remove any previous operation for this cookie name (latest operation wins)
                    _removePendingCookie(name);
                    // Append new operation to the array
                    _pendingCookies[_DYN_PUSH /* @min:%2epush */]({
                        n: name,
                        o: 0 /* ePendingOp.Set */,
                        v: cookieValue
                    });
                    result = true; // Return true to indicate the operation was "successful" (deferred)
                }
            }
            return result;
        },
        get: function (name) {
            var value = STR_EMPTY;
            var isIgnored = _isIgnoredCookie(cookieMgrConfig, name);
            if (!isIgnored) {
                if (_isMgrEnabled(cookieMgr)) {
                    value = _getCookieFn(name);
                }
                else if (_pendingCookies) {
                    // Search for the most recent operation for this cookie (search backwards through array)
                    for (var i = _pendingCookies[_DYN_LENGTH /* @min:%2elength */] - 1; i >= 0; i--) {
                        var pendingData = _pendingCookies[i];
                        if (pendingData.n === name) {
                            // Found the most recent operation for this cookie name
                            if (pendingData.o === 0 /* ePendingOp.Set */) {
                                // Return deferred value if it was a set operation
                                // Extract the value part from the formatted cookie string (before first semicolon)
                                var cookieValue = pendingData.v;
                                var idx = strIndexOf(cookieValue, ";");
                                value = idx !== -1 ? strTrim(strLeft(cookieValue, idx)) : strTrim(cookieValue);
                            }
                            // If it was a Purge operation, value remains empty (STR_EMPTY)
                            break;
                        }
                    }
                }
            }
            return value;
        },
        del: function (name, path) {
            var result = false;
            if (_isMgrEnabled(cookieMgr)) {
                // Only remove the cookie if the manager and cookie support has not been disabled
                result = cookieMgr.purge(name, path);
            }
            else if (_pendingCookies) {
                // Defer the deletion operation when cookies are disabled and deferral is enabled
                // Remove any previous operation for this cookie name (latest operation wins)
                _removePendingCookie(name);
                // Append new deletion operation to the array
                _pendingCookies[_DYN_PUSH /* @min:%2epush */]({
                    n: name,
                    o: 1 /* ePendingOp.Purge */,
                    v: _formatDeletionValue(path)
                });
                result = true;
            }
            return result;
        },
        purge: function (name, path) {
            var result = false;
            if (areCookiesSupported(logger)) {
                // Setting the expiration date in the past immediately removes the cookie
                _delCookieFn(name, _formatDeletionValue(path));
                result = true;
            }
            return result;
        },
        unload: function (isAsync) {
            unloadHandler && unloadHandler.rm();
            unloadHandler = null;
            // Clear any pending cookies on unload
            _pendingCookies = null;
        }
    };
    // Associated this cookie manager with the config
    cookieMgr[strConfigCookieMgr] = cookieMgr;
    return cookieMgr;
}
/*
* Helper method to tell if document.cookie object is supported by the runtime
*/
export function areCookiesSupported(logger) {
    if (_supportsCookies === null) {
        _supportsCookies = false;
        !_doc && _getDoc();
        try {
            var doc = _doc.v || {};
            _supportsCookies = doc[strCookie] !== undefined;
        }
        catch (e) {
            _throwInternal(logger, 2 /* eLoggingSeverity.WARNING */, 68 /* _eInternalMessageId.CannotAccessCookie */, "Cannot access document.cookie - " + getExceptionName(e), { exception: dumpObj(e) });
        }
    }
    return _supportsCookies;
}
function _extractParts(theValue) {
    var values = {};
    if (theValue && theValue[_DYN_LENGTH /* @min:%2elength */]) {
        var parts = strTrim(theValue)[_DYN_SPLIT /* @min:%2esplit */](";");
        arrForEach(parts, function (thePart) {
            thePart = strTrim(thePart || STR_EMPTY);
            if (thePart) {
                var idx = strIndexOf(thePart, "=");
                if (idx === -1) {
                    values[thePart] = null;
                }
                else {
                    values[strTrim(strLeft(thePart, idx))] = strTrim(strSubstring(thePart, idx + 1));
                }
            }
        });
    }
    return values;
}
function _formatDate(theDate, func) {
    if (isFunction(theDate[func])) {
        return theDate[func]();
    }
    return null;
}
function _formatCookieValue(value, values) {
    var cookieValue = value || STR_EMPTY;
    objForEachKey(values, function (name, theValue) {
        cookieValue += "; " + name + (!isNullOrUndefined(theValue) ? "=" + theValue : STR_EMPTY);
    });
    return cookieValue;
}
function _getCookieValue(name) {
    var cookieValue = STR_EMPTY;
    !_doc && _getDoc();
    if (_doc.v) {
        var theCookie = _doc.v[strCookie] || STR_EMPTY;
        if (_parsedCookieValue !== theCookie) {
            _cookieCache = _extractParts(theCookie);
            _parsedCookieValue = theCookie;
        }
        cookieValue = strTrim(_cookieCache[name] || STR_EMPTY);
    }
    return cookieValue;
}
function _setCookieValue(name, cookieValue) {
    !_doc && _getDoc();
    if (_doc.v) {
        _doc.v[strCookie] = name + "=" + cookieValue;
    }
}
export function uaDisallowsSameSiteNone(userAgent) {
    if (!isString(userAgent)) {
        return false;
    }
    // Cover all iOS based browsers here. This includes:
    // - Safari on iOS 12 for iPhone, iPod Touch, iPad
    // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
    // - Chrome on iOS 12 for iPhone, iPod Touch, iPad
    // All of which are broken by SameSite=None, because they use the iOS networking stack
    if (strContains(userAgent, "CPU iPhone OS 12") || strContains(userAgent, "iPad; CPU OS 12")) {
        return true;
    }
    // Cover Mac OS X based browsers that use the Mac OS networking stack. This includes:
    // - Safari on Mac OS X
    // This does not include:
    // - Internal browser on Mac OS X
    // - Chrome on Mac OS X
    // - Chromium on Mac OS X
    // Because they do not use the Mac OS networking stack.
    if (strContains(userAgent, "Macintosh; Intel Mac OS X 10_14") && strContains(userAgent, "Version/") && strContains(userAgent, "Safari")) {
        return true;
    }
    // Cover Mac OS X internal browsers that use the Mac OS networking stack. This includes:
    // - Internal browser on Mac OS X
    // This does not include:
    // - Safari on Mac OS X
    // - Chrome on Mac OS X
    // - Chromium on Mac OS X
    // Because they do not use the Mac OS networking stack.
    if (strContains(userAgent, "Macintosh; Intel Mac OS X 10_14") && strEndsWith(userAgent, "AppleWebKit/605.1.15 (KHTML, like Gecko)")) {
        return true;
    }
    // Cover Chrome 50-69, because some versions are broken by SameSite=None, and none in this range require it.
    // Note: this covers some pre-Chromium Edge versions, but pre-Chromim Edge does not require SameSite=None, so this is fine.
    // Note: this regex applies to Windows, Mac OS X, and Linux, deliberately.
    if (strContains(userAgent, "Chrome/5") || strContains(userAgent, "Chrome/6")) {
        return true;
    }
    // Unreal Engine runs Chromium 59, but does not advertise as Chrome until 4.23. Treat versions of Unreal
    // that don't specify their Chrome version as lacking support for SameSite=None.
    if (strContains(userAgent, "UnrealEngine") && !strContains(userAgent, "Chrome")) {
        return true;
    }
    // UCBrowser < 12.13.2 ignores Set-Cookie headers with SameSite=None
    // NB: this rule isn't complete - you need regex to make a complete rule.
    // See: https://www.chromium.org/updates/same-site/incompatible-clients
    if (strContains(userAgent, "UCBrowser/12") || strContains(userAgent, "UCBrowser/11")) {
        return true;
    }
    return false;
}
//# sourceMappingURL=CookieMgr.js.map