/*
 * Application Insights JavaScript SDK - Core, 3.2.0
 * Copyright (c) Microsoft and contributors. All rights reserved.
 */


import dynamicProto from "@microsoft/dynamicproto-js";
import { createPromise, doAwaitResponse } from "@nevware21/ts-async";
import { arrForEach, dumpObj, getNavigator, getWindow, isFunction, objKeys } from "@nevware21/ts-utils";
import { _DYN_DATA, _DYN_HEADERS, _DYN_INITIALIZE, _DYN_LENGTH, _DYN_MESSAGE, _DYN_PROTOCOL, _DYN_REPLACE, _DYN_SEND_POST, _DYN_SET_REQUEST_HEADER, _DYN_STATUS, _DYN_TIMEOUT, _DYN_TO_LOWER_CASE, _DYN_URL_STRING, _DYN__DO_TEARDOWN } from "../__DynamicConstants";
import { DisabledPropertyName } from "./Constants";
import { _throwInternal, _warnToConsole } from "./DiagnosticLogger";
import { getLocation, isBeaconsSupported, isFetchSupported, isXhrSupported, useXDomainRequest } from "./EnvUtils";
import { _getAllResponseHeaders, formatErrorMessageXdr, formatErrorMessageXhr, getResponseText, openXhr } from "./HelperFuncs";
var STR_EMPTY = "";
var STR_NO_RESPONSE_BODY = "NoResponseBody";
var _noResponseQs = "&" + STR_NO_RESPONSE_BODY + "=true";
var STR_POST_METHOD = "POST";
/**
 * This Internal component
 * Manager SendPost functions
 * SendPostManger
 * @internal for internal use only
 */
var SenderPostManager = /** @class */ (function () {
    function SenderPostManager() {
        var _syncFetchPayload = 0; // Keep track of the outstanding sync fetch payload total (as sync fetch has limits)
        var _enableSendPromise;
        var _isInitialized;
        var _diagLog;
        var _isOneDs;
        var _onCompleteFuncs;
        var _disableCredentials;
        var _fallbackInst;
        var _disableXhr;
        var _disableBeacon;
        var _disableBeaconSync;
        var _disableFetchKeepAlive;
        var _addNoResponse;
        var _timeoutWrapper;
        dynamicProto(SenderPostManager, this, function (_self, _base) {
            var _sendCredentials = true; // for 1ds
            _initDefaults();
            _self[_DYN_INITIALIZE /* @min:%2einitialize */] = function (config, diagLog) {
                _diagLog = diagLog;
                if (_isInitialized) {
                    _throwInternal(_diagLog, 1 /* eLoggingSeverity.CRITICAL */, 28 /* _eInternalMessageId.SenderNotInitialized */, "Sender is already initialized");
                }
                _self.SetConfig(config);
                _isInitialized = true;
            };
            _self["_getDbgPlgTargets"] = function () {
                return [_isInitialized, _isOneDs, _disableCredentials, _enableSendPromise];
            };
            // This componet might get its config from sender, offline sender, 1ds post
            // so set this function to mock dynamic changes
            _self.SetConfig = function (config) {
                try {
                    _onCompleteFuncs = config.senderOnCompleteCallBack || {};
                    _disableCredentials = !!config.disableCredentials;
                    _isOneDs = !!config.isOneDs;
                    _enableSendPromise = !!config.enableSendPromise;
                    _disableXhr = !!config.disableXhr;
                    _disableBeacon = !!config.disableBeacon;
                    _disableBeaconSync = !!config.disableBeaconSync;
                    _timeoutWrapper = config.timeWrapper;
                    _addNoResponse = !!config.addNoResponse;
                    _disableFetchKeepAlive = !!config.disableFetchKeepAlive;
                    _fallbackInst = { sendPOST: _xhrSender };
                    if (!_isOneDs) {
                        _sendCredentials = false; // for appInsights, set it to false always
                    }
                    if (_disableCredentials) {
                        var location_1 = getLocation();
                        if (location_1 && location_1.protocol && location_1.protocol[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */]() === "file:") {
                            // Special case where a local html file fails with a CORS error on Chromium browsers
                            _sendCredentials = false;
                        }
                    }
                    return true;
                }
                catch (e) {
                    // eslint-disable-next-line no-empty
                }
                return false;
            };
            _self.getSyncFetchPayload = function () {
                return _syncFetchPayload;
            };
            _self.getSenderInst = function (transports, sync) {
                if (transports && transports[_DYN_LENGTH /* @min:%2elength */]) {
                    return _getSenderInterface(transports, sync);
                }
                return null;
            };
            _self.getFallbackInst = function () {
                return _fallbackInst;
            };
            _self[_DYN__DO_TEARDOWN /* @min:%2e_doTeardown */] = function (unloadCtx, unloadState) {
                _initDefaults();
            };
            /**
             * success handler
             */
            function _onSuccess(res, onComplete) {
                _doOnComplete(onComplete, 200, {}, res);
            }
            /**
             * error handler
             */
            function _onError(message, onComplete) {
                _throwInternal(_diagLog, 2 /* eLoggingSeverity.WARNING */, 26 /* _eInternalMessageId.OnError */, "Failed to send telemetry.", { message: message });
                _doOnComplete(onComplete, 400, {});
            }
            function _onNoPayloadUrl(onComplete) {
                _onError("No endpoint url is provided for the batch", onComplete);
            }
            function _getSenderInterface(transports, syncSupport) {
                var _a;
                var transportType = 0 /* TransportType.NotSet */;
                var sendPostFunc = null;
                var lp = 0;
                while (sendPostFunc == null && lp < transports[_DYN_LENGTH /* @min:%2elength */]) {
                    transportType = transports[lp];
                    if (!_disableXhr && transportType === 1 /* TransportType.Xhr */) {
                        if (useXDomainRequest()) {
                            // IE 8 and 9
                            sendPostFunc = _xdrSender;
                        }
                        else if (isXhrSupported()) {
                            sendPostFunc = _xhrSender;
                        }
                    }
                    else if (transportType === 2 /* TransportType.Fetch */ && isFetchSupported(syncSupport) && (!syncSupport || !_disableFetchKeepAlive)) {
                        sendPostFunc = _doFetchSender;
                    }
                    else if (transportType === 3 /* TransportType.Beacon */ && isBeaconsSupported() && (syncSupport ? !_disableBeaconSync : !_disableBeacon)) {
                        sendPostFunc = _beaconSender;
                    }
                    lp++;
                }
                if (sendPostFunc) {
                    return _a = {
                            _transport: transportType,
                            _isSync: syncSupport
                        },
                        _a[_DYN_SEND_POST /* @min:sendPOST */] = sendPostFunc,
                        _a;
                }
                return null;
            }
            function _doOnComplete(oncomplete, status, headers, response) {
                try {
                    oncomplete && oncomplete(status, headers, response);
                }
                catch (e) {
                    // eslint-disable-next-line no-empty
                }
            }
            function _doBeaconSend(payload, oncomplete) {
                var nav = getNavigator();
                var url = payload[_DYN_URL_STRING /* @min:%2eurlString */];
                if (!url) {
                    _onNoPayloadUrl(oncomplete);
                    // return true here, because we don't want to retry it with fallback sender
                    return true;
                }
                url = payload[_DYN_URL_STRING /* @min:%2eurlString */] + (_addNoResponse ? _noResponseQs : STR_EMPTY);
                var data = payload[_DYN_DATA /* @min:%2edata */];
                // Chrome only allows CORS-safelisted values for the sendBeacon data argument
                // see: https://bugs.chromium.org/p/chromium/issues/detail?id=720283
                // Chrome only allows CORS-safelisted values for the sendBeacon data argument
                // see: https://bugs.chromium.org/p/chromium/issues/detail?id=720283
                var plainTextBatch = _isOneDs ? data : new Blob([data], { type: "text/plain;charset=UTF-8" });
                // The sendBeacon method returns true if the user agent is able to successfully queue the data for transfer. Otherwise it returns false.
                var queued = nav.sendBeacon(url, plainTextBatch);
                return queued;
            }
            /**
             * Send Beacon API request
             * @param payload - {string} - The data payload to be sent.
             * @param sync - {boolean} - not used
             * Note: Beacon API does not support custom headers and we are not able to get
             * appId from the backend for the correct correlation.
             */
            function _beaconSender(payload, oncomplete, sync) {
                var data = payload[_DYN_DATA /* @min:%2edata */];
                try {
                    if (data) {
                        // The sendBeacon method returns true if the user agent is able to successfully queue the data for transfer. Otherwise it returns false.
                        if (!_doBeaconSend(payload, oncomplete)) {
                            var onRetry = _onCompleteFuncs && _onCompleteFuncs.beaconOnRetry;
                            if (onRetry && isFunction(onRetry)) {
                                onRetry(payload, oncomplete, _doBeaconSend);
                            }
                            else {
                                _fallbackInst && _fallbackInst[_DYN_SEND_POST /* @min:%2esendPOST */](payload, oncomplete, true);
                                _throwInternal(_diagLog, 2 /* eLoggingSeverity.WARNING */, 40 /* _eInternalMessageId.TransmissionFailed */, ". " + "Failed to send telemetry with Beacon API, retried with normal sender.");
                            }
                        }
                        else {
                            // if can send
                            _onSuccess(STR_EMPTY, oncomplete);
                        }
                    }
                }
                catch (e) {
                    _isOneDs && _warnToConsole(_diagLog, "Failed to send telemetry using sendBeacon API. Ex:" + dumpObj(e));
                    _doOnComplete(oncomplete, _isOneDs ? 0 : 400, {}, STR_EMPTY);
                }
                return;
            }
            /**
             * Send XMLHttpRequest
             * @param payload - {string} - The data payload to be sent.
             * @param sync - {boolean} - Indicates if the request should be sent synchronously
             */
            function _xhrSender(payload, oncomplete, sync) {
                //let  internalPayload = payload as IInternalPayloadData;
                var thePromise;
                var resolveFunc;
                var rejectFunc;
                var headers = payload[_DYN_HEADERS /* @min:%2eheaders */] || {};
                if (!sync && _enableSendPromise) {
                    thePromise = createPromise(function (resolve, reject) {
                        resolveFunc = resolve;
                        rejectFunc = reject;
                    });
                }
                if (_isOneDs && sync && payload.disableXhrSync) {
                    sync = false;
                }
                //const xhr = new XMLHttpRequest();
                var endPointUrl = payload[_DYN_URL_STRING /* @min:%2eurlString */];
                if (!endPointUrl) {
                    _onNoPayloadUrl(oncomplete);
                    resolveFunc && resolveFunc(false);
                    return;
                }
                var xhr = openXhr(STR_POST_METHOD, endPointUrl, _sendCredentials, true, sync, payload[_DYN_TIMEOUT /* @min:%2etimeout */]);
                if (!_isOneDs) {
                    // application/json should NOT add to 1ds post by default
                    xhr[_DYN_SET_REQUEST_HEADER /* @min:%2esetRequestHeader */]("Content-type", "application/json");
                }
                arrForEach(objKeys(headers), function (headerName) {
                    xhr[_DYN_SET_REQUEST_HEADER /* @min:%2esetRequestHeader */](headerName, headers[headerName]);
                });
                xhr.onreadystatechange = function () {
                    if (!_isOneDs) {
                        _doOnReadyFunc(xhr);
                        if (xhr.readyState === 4) {
                            resolveFunc && resolveFunc(true);
                        }
                    }
                };
                xhr.onload = function () {
                    if (_isOneDs) {
                        _doOnReadyFunc(xhr);
                    }
                };
                function _doOnReadyFunc(xhr) {
                    var onReadyFunc = _onCompleteFuncs && _onCompleteFuncs.xhrOnComplete;
                    var onReadyFuncExist = onReadyFunc && isFunction(onReadyFunc);
                    if (onReadyFuncExist) {
                        onReadyFunc(xhr, oncomplete, payload);
                    }
                    else {
                        var response = getResponseText(xhr);
                        _doOnComplete(oncomplete, xhr[_DYN_STATUS /* @min:%2estatus */], _getAllResponseHeaders(xhr, _isOneDs), response);
                    }
                }
                xhr.onerror = function (event) {
                    _doOnComplete(oncomplete, _isOneDs ? xhr[_DYN_STATUS /* @min:%2estatus */] : 400, _getAllResponseHeaders(xhr, _isOneDs), _isOneDs ? STR_EMPTY : formatErrorMessageXhr(xhr));
                    rejectFunc && rejectFunc(event);
                };
                xhr.ontimeout = function () {
                    _doOnComplete(oncomplete, _isOneDs ? xhr[_DYN_STATUS /* @min:%2estatus */] : 500, _getAllResponseHeaders(xhr, _isOneDs), _isOneDs ? STR_EMPTY : formatErrorMessageXhr(xhr));
                    resolveFunc && resolveFunc(false);
                };
                xhr.send(payload[_DYN_DATA /* @min:%2edata */]);
                return thePromise;
            }
            /**
             * Send fetch API request
             * @param payload - {string} - The data payload to be sent.
             * @param sync - {boolean} - For fetch this identifies whether we are "unloading" (false) or a normal request
             */
            function _doFetchSender(payload, oncomplete, sync) {
                var _a;
                var endPointUrl = payload[_DYN_URL_STRING /* @min:%2eurlString */];
                var batch = payload[_DYN_DATA /* @min:%2edata */];
                var plainTextBatch = _isOneDs ? batch : new Blob([batch], { type: "application/json" });
                var thePromise;
                var resolveFunc;
                var rejectFunc;
                var requestHeaders = new Headers();
                var batchLength = batch[_DYN_LENGTH /* @min:%2elength */];
                var ignoreResponse = false;
                var responseHandled = false;
                var headers = payload[_DYN_HEADERS /* @min:%2eheaders */] || {};
                //TODO: handle time out for 1ds
                var init = (_a = {
                        method: STR_POST_METHOD,
                        body: plainTextBatch
                    },
                    _a[DisabledPropertyName] = true // Mark so we don't attempt to track this request
                ,
                    _a);
                // Only add headers if there are headers to add, due to issue with some polyfills
                if (payload.headers && objKeys(payload.headers)[_DYN_LENGTH /* @min:%2elength */] > 0) {
                    arrForEach(objKeys(headers), function (headerName) {
                        requestHeaders.append(headerName, headers[headerName]);
                    });
                    init[_DYN_HEADERS /* @min:%2eheaders */] = requestHeaders;
                }
                if (_sendCredentials && _isOneDs) {
                    // for 1ds, Don't send credentials when URL is file://
                    init.credentials = "include";
                }
                if (sync) {
                    init.keepalive = true;
                    _syncFetchPayload += batchLength;
                    if (_isOneDs) {
                        if (payload["_sendReason"] === 2 /* SendRequestReason.Unload */) {
                            // As a sync request (during unload), it is unlikely that we will get a chance to process the response so
                            // just like beacon send assume that the events have been accepted and processed
                            ignoreResponse = true;
                            if (_addNoResponse) {
                                endPointUrl += _noResponseQs;
                            }
                        }
                    }
                    else {
                        // for appinsights, set to true for all sync request
                        ignoreResponse = true;
                    }
                }
                var request = new Request(endPointUrl, init);
                try {
                    // Also try and tag the request (just in case the value in init is not copied over)
                    request[DisabledPropertyName] = true;
                }
                catch (e) {
                    // If the environment has locked down the XMLHttpRequest (preventExtensions and/or freeze), this would
                    // cause the request to fail and we no telemetry would be sent
                }
                if (!sync && _enableSendPromise) {
                    thePromise = createPromise(function (resolve, reject) {
                        resolveFunc = resolve;
                        rejectFunc = reject;
                    });
                }
                if (!endPointUrl) {
                    _onNoPayloadUrl(oncomplete);
                    resolveFunc && resolveFunc(false);
                    return;
                }
                function _handleError(res) {
                    // In case there is an error in the request. Set the status to 0 for 1ds and 400 for appInsights
                    // so that the events can be retried later.
                    _doOnComplete(oncomplete, _isOneDs ? 0 : 400, {}, _isOneDs ? STR_EMPTY : res);
                }
                function _onFetchComplete(response, payload, value) {
                    var status = response[_DYN_STATUS /* @min:%2estatus */];
                    var onCompleteFunc = _onCompleteFuncs.fetchOnComplete;
                    if (onCompleteFunc && isFunction(onCompleteFunc)) {
                        onCompleteFunc(response, oncomplete, value || STR_EMPTY, payload);
                    }
                    else {
                        _doOnComplete(oncomplete, status, {}, value || STR_EMPTY);
                    }
                }
                try {
                    doAwaitResponse(fetch(_isOneDs ? endPointUrl : request, _isOneDs ? init : null), function (result) {
                        if (sync) {
                            _syncFetchPayload -= batchLength;
                            batchLength = 0;
                        }
                        if (!responseHandled) {
                            responseHandled = true;
                            if (!result.rejected) {
                                var response_1 = result.value;
                                try {
                                    /**
                                     * The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500.
                                     * Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure
                                     * or if anything prevented the request from completing.
                                     */
                                    if (!_isOneDs && !response_1.ok) {
                                        // this is for appInsights only
                                        _handleError(response_1.statusText);
                                        resolveFunc && resolveFunc(false);
                                    }
                                    else {
                                        if (_isOneDs && !response_1.body) {
                                            _onFetchComplete(response_1, null, STR_EMPTY);
                                            resolveFunc && resolveFunc(true);
                                        }
                                        else {
                                            doAwaitResponse(response_1.text(), function (resp) {
                                                _onFetchComplete(response_1, payload, resp.value);
                                                resolveFunc && resolveFunc(true);
                                            });
                                        }
                                    }
                                }
                                catch (e) {
                                    _handleError(dumpObj(e));
                                    rejectFunc && rejectFunc(e);
                                }
                            }
                            else {
                                _handleError(result.reason && result.reason[_DYN_MESSAGE /* @min:%2emessage */]);
                                rejectFunc && rejectFunc(result.reason);
                            }
                        }
                    });
                }
                catch (e) {
                    if (!responseHandled) {
                        _handleError(dumpObj(e));
                        rejectFunc && rejectFunc(e);
                    }
                }
                if (ignoreResponse && !responseHandled) {
                    // Assume success during unload processing as we most likely won't get the response
                    responseHandled = true;
                    _doOnComplete(oncomplete, 200, {});
                    resolveFunc && resolveFunc(true);
                }
                if (_isOneDs && !responseHandled && payload[_DYN_TIMEOUT /* @min:%2etimeout */] > 0) {
                    // Simulate timeout
                    _timeoutWrapper && _timeoutWrapper.set(function () {
                        if (!responseHandled) {
                            // Assume a 500 response (which will cause a retry)
                            responseHandled = true;
                            _doOnComplete(oncomplete, 500, {});
                            resolveFunc && resolveFunc(true);
                        }
                    }, payload[_DYN_TIMEOUT /* @min:%2etimeout */]);
                }
                return thePromise;
            }
            /**
             * Send XDomainRequest
             * @param payload - {string} - The data payload to be sent.
             * @param sync - {boolean} - Indicates if the request should be sent synchronously
             *
             * Note: XDomainRequest does not support sync requests. This 'isAsync' parameter is added
             * to maintain consistency with the xhrSender's contract
             * Note: XDomainRequest does not support custom headers and we are not able to get
             * appId from the backend for the correct correlation.
             */
            function _xdrSender(payload, oncomplete, sync) {
                // It doesn't support custom headers, so no action is taken with current requestHeaders
                var _window = getWindow();
                var xdr = new XDomainRequest();
                var data = payload[_DYN_DATA /* @min:%2edata */];
                xdr.onload = function () {
                    var response = getResponseText(xdr);
                    var onloadFunc = _onCompleteFuncs && _onCompleteFuncs.xdrOnComplete;
                    if (onloadFunc && isFunction(onloadFunc)) {
                        onloadFunc(xdr, oncomplete, payload);
                    }
                    else {
                        _doOnComplete(oncomplete, 200, {}, response);
                    }
                };
                xdr.onerror = function () {
                    _doOnComplete(oncomplete, 400, {}, _isOneDs ? STR_EMPTY : formatErrorMessageXdr(xdr));
                };
                xdr.ontimeout = function () {
                    _doOnComplete(oncomplete, 500, {});
                };
                xdr.onprogress = function () { };
                // XDomainRequest requires the same protocol as the hosting page.
                // If the protocol doesn't match, we can't send the telemetry :(.
                var hostingProtocol = _window && _window.location && _window.location[_DYN_PROTOCOL /* @min:%2eprotocol */] || "";
                var endpoint = payload[_DYN_URL_STRING /* @min:%2eurlString */];
                if (!endpoint) {
                    _onNoPayloadUrl(oncomplete);
                    return;
                }
                if (!_isOneDs && endpoint.lastIndexOf(hostingProtocol, 0) !== 0) {
                    var msg = "Cannot send XDomain request. The endpoint URL protocol doesn't match the hosting page protocol.";
                    _throwInternal(_diagLog, 2 /* eLoggingSeverity.WARNING */, 40 /* _eInternalMessageId.TransmissionFailed */, ". " + msg);
                    _onError(msg, oncomplete);
                    return;
                }
                var endpointUrl = _isOneDs ? endpoint : endpoint[_DYN_REPLACE /* @min:%2ereplace */](/^(https?:)/, "");
                xdr.open(STR_POST_METHOD, endpointUrl);
                if (payload[_DYN_TIMEOUT /* @min:%2etimeout */]) {
                    xdr[_DYN_TIMEOUT /* @min:%2etimeout */] = payload[_DYN_TIMEOUT /* @min:%2etimeout */];
                }
                xdr.send(data);
                if (_isOneDs && sync) {
                    _timeoutWrapper && _timeoutWrapper.set(function () {
                        xdr.send(data);
                    }, 0);
                }
                else {
                    xdr.send(data);
                }
            }
            function _initDefaults() {
                _syncFetchPayload = 0;
                _isInitialized = false;
                _enableSendPromise = false;
                _diagLog = null;
                _isOneDs = null;
                _onCompleteFuncs = null;
                _disableCredentials = null;
                _fallbackInst = null;
                _disableXhr = false;
                _disableBeacon = false;
                _disableBeaconSync = false;
                _disableFetchKeepAlive = false;
                _addNoResponse = false;
                _timeoutWrapper = null;
            }
        });
    }
// Removed Stub for SenderPostManager.prototype.initialize.
// Removed Stub for SenderPostManager.prototype.getSyncFetchPayload.
// Removed Stub for SenderPostManager.prototype.SetConfig.
// Removed Stub for SenderPostManager.prototype.getSenderInst.
// Removed Stub for SenderPostManager.prototype.getFallbackInst.
// Removed Stub for SenderPostManager.prototype._doTeardown.
    // This is a workaround for an IE bug when using dynamicProto() with classes that don't have any
    // non-dynamic functions or static properties/functions when using uglify-js to minify the resulting code.
    SenderPostManager.__ieDyn=1;

    return SenderPostManager;
}());
export { SenderPostManager };
//# sourceMappingURL=SenderPostManager.js.map