"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SiteApps = void 0;
var core_1 = require("@posthog/core");
var globals_1 = require("./utils/globals");
var logger_1 = require("./utils/logger");
var logger = (0, logger_1.createLogger)('[SiteApps]');
var APP_INIT_ERROR = 'Error while initializing PostHog app with config id ';
var SiteApps = /** @class */ (function () {
    function SiteApps(_instance) {
        this._instance = _instance;
        this._siteAppElementPatchCount = 0;
        // events captured between loading posthog-js and the site app; up to 1000 events
        this._bufferedInvocations = [];
        this.apps = {};
    }
    Object.defineProperty(SiteApps.prototype, "isEnabled", {
        get: function () {
            return !!this._instance.config.opt_in_site_apps;
        },
        enumerable: false,
        configurable: true
    });
    SiteApps.prototype._eventCollector = function (_eventName, eventPayload) {
        if (!eventPayload) {
            return;
        }
        var globals = this.globalsForEvent(eventPayload);
        this._bufferedInvocations.push(globals);
        if (this._bufferedInvocations.length > 1000) {
            this._bufferedInvocations = this._bufferedInvocations.slice(10);
        }
    };
    Object.defineProperty(SiteApps.prototype, "siteAppLoaders", {
        get: function () {
            var _a, _b;
            return (_b = (_a = globals_1.assignableWindow._POSTHOG_REMOTE_CONFIG) === null || _a === void 0 ? void 0 : _a[this._instance.config.token]) === null || _b === void 0 ? void 0 : _b.siteApps;
        },
        enumerable: false,
        configurable: true
    });
    SiteApps.prototype.initialize = function () {
        var _this = this;
        if (this.isEnabled) {
            var stop_1 = this._instance._addCaptureHook(this._eventCollector.bind(this));
            this._stopBuffering = function () {
                stop_1();
                _this._bufferedInvocations = [];
                _this._stopBuffering = undefined;
            };
        }
    };
    SiteApps.prototype.globalsForEvent = function (event) {
        var e_1, _a;
        var _b, _c, _d, _e, _f, _g, _h;
        if (!event) {
            throw new Error('Event payload is required');
        }
        var groups = {};
        var groupIds = this._instance.get_property('$groups') || [];
        var groupProperties = this._instance.get_property('$stored_group_properties') || {};
        try {
            for (var _j = __values(Object.entries(groupProperties)), _k = _j.next(); !_k.done; _k = _j.next()) {
                var _l = __read(_k.value, 2), type = _l[0], properties = _l[1];
                groups[type] = { id: groupIds[type], type: type, properties: properties };
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (_k && !_k.done && (_a = _j.return)) _a.call(_j);
            }
            finally { if (e_1) throw e_1.error; }
        }
        var $set_once = event.$set_once, $set = event.$set, _event = __rest(event, ["$set_once", "$set"]);
        var globals = {
            event: __assign(__assign({}, _event), { properties: __assign(__assign(__assign({}, event.properties), ($set ? { $set: __assign(__assign({}, ((_c = (_b = event.properties) === null || _b === void 0 ? void 0 : _b.$set) !== null && _c !== void 0 ? _c : {})), $set) } : {})), ($set_once ? { $set_once: __assign(__assign({}, ((_e = (_d = event.properties) === null || _d === void 0 ? void 0 : _d.$set_once) !== null && _e !== void 0 ? _e : {})), $set_once) } : {})), elements_chain: (_g = (_f = event.properties) === null || _f === void 0 ? void 0 : _f['$elements_chain']) !== null && _g !== void 0 ? _g : '', 
                // TODO:
                // - elements_chain_href: '',
                // - elements_chain_texts: [] as string[],
                // - elements_chain_ids: [] as string[],
                // - elements_chain_elements: [] as string[],
                distinct_id: (_h = event.properties) === null || _h === void 0 ? void 0 : _h['distinct_id'] }),
            person: {
                properties: this._instance.get_property('$stored_person_properties'),
            },
            groups: groups,
        };
        return globals;
    };
    SiteApps.prototype._prepareElementForSiteApp = function (element) {
        var _a;
        var tagName = (_a = element.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
        if (tagName === 'style' && this._instance.config.prepare_external_dependency_stylesheet) {
            var prepared = this._instance.config.prepare_external_dependency_stylesheet(element);
            if (!prepared) {
                logger.error('prepare_external_dependency_stylesheet returned null');
                return null;
            }
            return prepared;
        }
        if (tagName === 'script' && this._instance.config.prepare_external_dependency_script) {
            var prepared = this._instance.config.prepare_external_dependency_script(element);
            if (!prepared) {
                logger.error('prepare_external_dependency_script returned null');
                return null;
            }
            return prepared;
        }
        return element;
    };
    SiteApps.prototype._patchSiteAppElementInsertionMethods = function () {
        var _this = this;
        var _a, _b, _c, _d, _e, _f, _g, _h;
        if (!this._instance.config.prepare_external_dependency_stylesheet &&
            !this._instance.config.prepare_external_dependency_script) {
            return function () { };
        }
        var win = globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.defaultView;
        var nodePrototype = (_a = win === null || win === void 0 ? void 0 : win.Node) === null || _a === void 0 ? void 0 : _a.prototype;
        if (!win || !nodePrototype) {
            return function () { };
        }
        this._siteAppElementPatchCount++;
        if (this._restoreSiteAppElementPatches) {
            return this._releaseSiteAppElementPatches();
        }
        var restore = [];
        var siteApps = this;
        var preparedNodes = new WeakSet();
        var patch = function (prototype, method, replacement) {
            if (!(prototype === null || prototype === void 0 ? void 0 : prototype[method])) {
                return;
            }
            var original = prototype[method];
            prototype[method] = replacement(original);
            restore.push(function () {
                prototype[method] = original;
            });
        };
        var prepareNode = function (node) {
            if (preparedNodes.has(node)) {
                return node;
            }
            var prepared = siteApps._prepareElementForSiteApp(node);
            if (prepared) {
                preparedNodes.add(prepared);
            }
            return prepared;
        };
        var prepareNodes = function (nodes) {
            return nodes
                .map(function (node) { return (typeof node === 'string' ? node : prepareNode(node)); })
                .filter(function (node) { return !(0, core_1.isNull)(node); });
        };
        patch(nodePrototype, 'appendChild', function (original) {
            return function (child) {
                var prepared = prepareNode(child);
                return prepared ? original.call(this, prepared) : child;
            };
        });
        patch(nodePrototype, 'insertBefore', function (original) {
            return function (newChild, refChild) {
                var prepared = prepareNode(newChild);
                return prepared ? original.call(this, prepared, refChild) : newChild;
            };
        });
        patch(nodePrototype, 'replaceChild', function (original) {
            return function (newChild, oldChild) {
                var prepared = prepareNode(newChild);
                return prepared ? original.call(this, prepared, oldChild) : oldChild;
            };
        });
        [(_b = win.Element) === null || _b === void 0 ? void 0 : _b.prototype, (_c = win.Document) === null || _c === void 0 ? void 0 : _c.prototype, (_d = win.DocumentFragment) === null || _d === void 0 ? void 0 : _d.prototype].forEach(function (prototype) {
            patch(prototype, 'append', function (original) {
                return function () {
                    var nodes = [];
                    for (var _i = 0; _i < arguments.length; _i++) {
                        nodes[_i] = arguments[_i];
                    }
                    return original.apply(this, prepareNodes(nodes));
                };
            });
            patch(prototype, 'prepend', function (original) {
                return function () {
                    var nodes = [];
                    for (var _i = 0; _i < arguments.length; _i++) {
                        nodes[_i] = arguments[_i];
                    }
                    return original.apply(this, prepareNodes(nodes));
                };
            });
        });
        [(_e = win.Element) === null || _e === void 0 ? void 0 : _e.prototype, (_f = win.CharacterData) === null || _f === void 0 ? void 0 : _f.prototype, (_g = win.DocumentType) === null || _g === void 0 ? void 0 : _g.prototype].forEach(function (prototype) {
            patch(prototype, 'before', function (original) {
                return function () {
                    var nodes = [];
                    for (var _i = 0; _i < arguments.length; _i++) {
                        nodes[_i] = arguments[_i];
                    }
                    return original.apply(this, prepareNodes(nodes));
                };
            });
            patch(prototype, 'after', function (original) {
                return function () {
                    var nodes = [];
                    for (var _i = 0; _i < arguments.length; _i++) {
                        nodes[_i] = arguments[_i];
                    }
                    return original.apply(this, prepareNodes(nodes));
                };
            });
            patch(prototype, 'replaceWith', function (original) {
                return function () {
                    var nodes = [];
                    for (var _i = 0; _i < arguments.length; _i++) {
                        nodes[_i] = arguments[_i];
                    }
                    var prepared = prepareNodes(nodes);
                    return nodes.length && !prepared.length ? undefined : original.apply(this, prepared);
                };
            });
        });
        patch((_h = win.Element) === null || _h === void 0 ? void 0 : _h.prototype, 'insertAdjacentElement', function (original) {
            return function (position, insertedElement) {
                var prepared = prepareNode(insertedElement);
                return prepared ? original.call(this, position, prepared) : null;
            };
        });
        this._restoreSiteAppElementPatches = function () {
            restore.forEach(function (restore) { return restore(); });
            _this._restoreSiteAppElementPatches = undefined;
        };
        return this._releaseSiteAppElementPatches();
    };
    SiteApps.prototype._releaseSiteAppElementPatches = function () {
        var _this = this;
        var released = false;
        return function () {
            var _a;
            if (released) {
                return;
            }
            released = true;
            _this._siteAppElementPatchCount--;
            if (_this._siteAppElementPatchCount === 0) {
                (_a = _this._restoreSiteAppElementPatches) === null || _a === void 0 ? void 0 : _a.call(_this);
            }
        };
    };
    SiteApps.prototype._runWithPreparedSiteAppElements = function (callback, restoreSynchronously) {
        if (restoreSynchronously === void 0) { restoreSynchronously = true; }
        var restore = this._patchSiteAppElementInsertionMethods();
        try {
            var result = callback(restore);
            if (restoreSynchronously) {
                restore();
            }
            return result;
        }
        catch (e) {
            restore();
            throw e;
        }
    };
    SiteApps.prototype.setupSiteApp = function (loader) {
        var _this = this;
        var app = this.apps[loader.id];
        var processBufferedEvents = function () {
            var _a;
            if (!app.errored && _this._bufferedInvocations.length) {
                logger.info("Processing ".concat(_this._bufferedInvocations.length, " events for site app with id ").concat(loader.id));
                _this._bufferedInvocations.forEach(function (globals) {
                    return _this._runWithPreparedSiteAppElements(function () { var _a; return (_a = app.processEvent) === null || _a === void 0 ? void 0 : _a.call(app, globals); });
                });
                app.processedBuffer = true;
            }
            if (Object.values(_this.apps).every(function (app) { return app.processedBuffer || app.errored; })) {
                (_a = _this._stopBuffering) === null || _a === void 0 ? void 0 : _a.call(_this);
            }
        };
        var hasInitReturned = false;
        var onLoaded = function (success) {
            app.errored = !success;
            app.loaded = true;
            logger.info("Site app with id ".concat(loader.id, " ").concat(success ? 'loaded' : 'errored'));
            // ensure that we don't call processBufferedEvents until after init() returns and we've set up processEvent
            if (hasInitReturned) {
                processBufferedEvents();
            }
        };
        try {
            var processEvent = this._runWithPreparedSiteAppElements(function (restore) {
                return loader.init({
                    posthog: _this._instance,
                    callback: function (success) {
                        restore();
                        onLoaded(success);
                    },
                });
            }, false).processEvent;
            if (processEvent) {
                app.processEvent = processEvent;
            }
            hasInitReturned = true;
        }
        catch (e) {
            logger.error(APP_INIT_ERROR + loader.id, e);
            onLoaded(false);
        }
        // if the app loaded synchronously, process the events now
        if (hasInitReturned && app.loaded) {
            try {
                processBufferedEvents();
            }
            catch (e) {
                logger.error("Error while processing buffered events PostHog app with config id ".concat(loader.id), e);
                app.errored = true;
            }
        }
    };
    SiteApps.prototype._setupSiteApps = function () {
        var e_2, _a, e_3, _b;
        var siteAppLoaders = this.siteAppLoaders || [];
        try {
            // do this in 2 passes, so that this.apps is populated before we call init
            for (var siteAppLoaders_1 = __values(siteAppLoaders), siteAppLoaders_1_1 = siteAppLoaders_1.next(); !siteAppLoaders_1_1.done; siteAppLoaders_1_1 = siteAppLoaders_1.next()) {
                var loader = siteAppLoaders_1_1.value;
                this.apps[loader.id] = {
                    id: loader.id,
                    loaded: false,
                    errored: false,
                    processedBuffer: false,
                };
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (siteAppLoaders_1_1 && !siteAppLoaders_1_1.done && (_a = siteAppLoaders_1.return)) _a.call(siteAppLoaders_1);
            }
            finally { if (e_2) throw e_2.error; }
        }
        try {
            for (var siteAppLoaders_2 = __values(siteAppLoaders), siteAppLoaders_2_1 = siteAppLoaders_2.next(); !siteAppLoaders_2_1.done; siteAppLoaders_2_1 = siteAppLoaders_2.next()) {
                var loader = siteAppLoaders_2_1.value;
                this.setupSiteApp(loader);
            }
        }
        catch (e_3_1) { e_3 = { error: e_3_1 }; }
        finally {
            try {
                if (siteAppLoaders_2_1 && !siteAppLoaders_2_1.done && (_b = siteAppLoaders_2.return)) _b.call(siteAppLoaders_2);
            }
            finally { if (e_3) throw e_3.error; }
        }
    };
    SiteApps.prototype._onCapturedEvent = function (event) {
        var e_4, _a;
        if (Object.keys(this.apps).length === 0) {
            return;
        }
        var globals = this.globalsForEvent(event);
        var _loop_1 = function (app) {
            try {
                this_1._runWithPreparedSiteAppElements(function () { var _a; return (_a = app.processEvent) === null || _a === void 0 ? void 0 : _a.call(app, globals); });
            }
            catch (e) {
                logger.error("Error while processing event ".concat(event.event, " for site app ").concat(app.id), e);
            }
        };
        var this_1 = this;
        try {
            for (var _b = __values(Object.values(this.apps)), _c = _b.next(); !_c.done; _c = _b.next()) {
                var app = _c.value;
                _loop_1(app);
            }
        }
        catch (e_4_1) { e_4 = { error: e_4_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_4) throw e_4.error; }
        }
    };
    SiteApps.prototype.onRemoteConfig = function (result) {
        var e_5, _a;
        var _this = this;
        var _b, _c, _d, _e, _f;
        if ((_b = this.siteAppLoaders) === null || _b === void 0 ? void 0 : _b.length) {
            if (!this.isEnabled) {
                logger.error("PostHog site apps are disabled. Enable the \"opt_in_site_apps\" config to proceed.");
                return;
            }
            this._setupSiteApps();
            // NOTE: We could improve this to only fire if we actually have listeners for the event
            this._instance.on('eventCaptured', function (event) { return _this._onCapturedEvent(event); });
            return;
        }
        // NOTE: Below this is now only the fallback for legacy site app support. Once we have fully removed to the remote config loader we can get rid of this
        (_c = this._stopBuffering) === null || _c === void 0 ? void 0 : _c.call(this);
        // On a failed fetch there are no legacy site apps to load; buffering has
        // already been stopped above, same as for a response without site apps.
        if (!result.ok) {
            return;
        }
        var response = result.config;
        if (!((_d = response['siteApps']) === null || _d === void 0 ? void 0 : _d.length)) {
            return;
        }
        if (!this.isEnabled) {
            logger.error("PostHog site apps are disabled. Enable the \"opt_in_site_apps\" config to proceed.");
            return;
        }
        var _loop_2 = function (id, url) {
            globals_1.assignableWindow["__$$ph_site_app_".concat(id)] = this_2._instance;
            (_f = (_e = globals_1.assignableWindow.__PosthogExtensions__) === null || _e === void 0 ? void 0 : _e.loadSiteApp) === null || _f === void 0 ? void 0 : _f.call(_e, this_2._instance, url, function (err) {
                if (err) {
                    return logger.error(APP_INIT_ERROR + id, err);
                }
            });
        };
        var this_2 = this;
        try {
            for (var _g = __values(response['siteApps']), _h = _g.next(); !_h.done; _h = _g.next()) {
                var _j = _h.value, id = _j.id, url = _j.url;
                _loop_2(id, url);
            }
        }
        catch (e_5_1) { e_5 = { error: e_5_1 }; }
        finally {
            try {
                if (_h && !_h.done && (_a = _g.return)) _a.call(_g);
            }
            finally { if (e_5) throw e_5.error; }
        }
    };
    return SiteApps;
}());
exports.SiteApps = SiteApps;
//# sourceMappingURL=site-apps.js.map