import { __assign, __read, __spreadArray } from "tslib";
/* eslint-disable @typescript-eslint/prefer-optional-chain */
import { uuid, arrayIncludes } from '@slardar/sdk-template';
import { getDefaultBrowser, getDefaultDocument, getDefaultPerformance, applyPerformance, getDefaultMutationObserver, getDefaultXMLHttpRequest, applyMutationObserver, } from '../../utils';
import { applyIncompleteReq, waitActionComplete, getDomPath, getActionName } from './util';
var ACTION_EV_TYPE = 'action';
var HTTP_EV_TYPE = 'http';
var LONGTASK_RV_TYPE = 'performance_longtask';
var JS_ERROR_EV_TYPE = 'js_error';
var RESOURCE_EV_TYPE = 'resource';
var RELATED_EV_TYPES = [LONGTASK_RV_TYPE, HTTP_EV_TYPE, JS_ERROR_EV_TYPE, RESOURCE_EV_TYPE];
var SUPPORT_TYPES = ["click" /* CLICK */];
var EVENT_LISTENER_OPTIONS = { capture: true };
export var DEFAULT_ATTR_NAME = 'data-apm-action';
export var ActionMonitor = function (win, document, xhr, MutationObserver, performance) {
    if (win === void 0) { win = getDefaultBrowser(); }
    if (document === void 0) { document = getDefaultDocument(); }
    if (xhr === void 0) { xhr = getDefaultXMLHttpRequest(); }
    if (MutationObserver === void 0) { MutationObserver = getDefaultMutationObserver(); }
    if (performance === void 0) { performance = getDefaultPerformance(); }
    if (!document || !win) {
        return;
    }
    return function (_a, cb) {
        var types = _a.types, pure = _a.pure;
        var restoreFns = [];
        var currentPayload = null;
        var requestStartAndEnd = [];
        var longtaskDuration = 0;
        var _b = __read(applyPerformance(performance), 2), now = _b[1];
        var _c = __read(applyIncompleteReq(win, xhr), 1), incompleteReq = _c[0];
        // 结算
        var reportActionIfAllowed = function () {
            // 不存在action上下文 || 未收集到相关payload || 依然有进行中的请求
            if (!currentPayload ||
                (!pure && !Object.keys(currentPayload.metrics).length) ||
                Object.keys(incompleteReq).length) {
                return;
            }
            // longtask duration
            currentPayload.metrics[LONGTASK_RV_TYPE] &&
                (currentPayload.metrics[LONGTASK_RV_TYPE].duration = longtaskDuration);
            // request duration
            var requestDuration = requestStartAndEnd.length > 0 ? Math.max.apply(Math, __spreadArray([], __read(requestStartAndEnd), false)) - Math.min.apply(Math, __spreadArray([], __read(requestStartAndEnd), false)) : 0;
            currentPayload.metrics[HTTP_EV_TYPE] && (currentPayload.metrics[HTTP_EV_TYPE].duration = requestDuration);
            // end time
            var actionDuration = now() - currentPayload.start_time;
            cb &&
                cb({
                    ev_type: ACTION_EV_TYPE,
                    payload: __assign(__assign({}, currentPayload), { duration: actionDuration, frontend_time: actionDuration - requestDuration }),
                });
            // 上报后重置数据
            resetAction();
        };
        var resetAction = function () {
            complete();
            currentPayload = null;
            requestStartAndEnd = [];
            longtaskDuration = 0;
        };
        var _d = __read(waitActionComplete(reportActionIfAllowed, resetAction, win), 3), start = _d[0], notify = _d[1], complete = _d[2];
        var _e = __read(applyMutationObserver(MutationObserver, notify), 2), observe = _e[0], disconnect = _e[1];
        observe();
        // 补充数据
        var applyOtherReport = function (ev) {
            var _a, _b;
            if (!currentPayload) {
                return;
            }
            if (!arrayIncludes(RELATED_EV_TYPES, ev.ev_type)) {
                return;
            }
            // 通知监听，有相关变更
            notify();
            if (ev.ev_type === HTTP_EV_TYPE) {
                if (!ev.payload.response.timing) {
                    return;
                }
                var _c = ev.payload.response.timing, startTime = _c.startTime, duration = _c.duration;
                if (startTime < currentPayload.start_time) {
                    return;
                }
                requestStartAndEnd.push(startTime, startTime + duration);
            }
            else if (ev.ev_type === LONGTASK_RV_TYPE) {
                if (!ev.payload.longtasks.length || ev.payload.longtasks[0].startTime < currentPayload.start_time) {
                    return;
                }
                ev.payload.longtasks.forEach(function (e) {
                    longtaskDuration = longtaskDuration + e.duration;
                });
                // eslint-disable-next-line sonarjs/no-collapsible-if
            }
            else if (ev.ev_type === RESOURCE_EV_TYPE) {
                if (ev.payload.startTime < currentPayload.start_time) {
                    return;
                }
            }
            currentPayload.metrics[ev.ev_type] = {
                count: ((_b = (_a = currentPayload.metrics[ev.ev_type]) === null || _a === void 0 ? void 0 : _a.count) !== null && _b !== void 0 ? _b : 0) + 1,
            };
            return currentPayload.id;
        };
        var initAction = function (target, type) {
            // refresh action id in common
            var actionId = uuid();
            var _a = __read(applyPerformance(performance), 2), now = _a[1];
            // init payload
            currentPayload = {
                start_time: now(),
                id: actionId,
                type: type,
                target: target,
                metrics: {},
            };
        };
        var handleEvent = function (e) {
            reportActionIfAllowed();
            resetAction();
            if (!(e.target instanceof HTMLElement)) {
                return;
            }
            var name = getActionName(e.target, DEFAULT_ATTR_NAME);
            if (!name) {
                return;
            }
            initAction({ name: name, path: getDomPath(e.target) }, e.type);
            start();
        };
        types &&
            types.forEach(function (t) {
                if (arrayIncludes(SUPPORT_TYPES, t)) {
                    document.addEventListener(t, handleEvent, EVENT_LISTENER_OPTIONS);
                    restoreFns.push(function () { return document.removeEventListener(t, handleEvent, EVENT_LISTENER_OPTIONS); });
                }
            });
        var teardown = function () {
            restoreFns.forEach(function (fn) { return fn(); });
            disconnect();
        };
        var startAction = function (name, type) {
            reportActionIfAllowed();
            resetAction();
            initAction({ name: name }, type);
            start();
        };
        return [teardown, applyOtherReport, startAction];
    };
};
//# sourceMappingURL=index.js.map