"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const enhancer_1 = require("../../Util/Console/enhancer");
const util = __importStar(require("util"));
const MAX_ERRORS_BUFFER_COUNT = 50;
class Connect {
    window;
    messagePrefix;
    static MESSAGE_PREFIX = 'connect';
    connectedBaseUrl = null;
    errorsMap = {};
    /** @internal */
    constructor(window, messagePrefix) {
        this.window = window;
        this.messagePrefix = messagePrefix;
        this.polyfillErrorWithStack();
        this.polyfillErrorEvent();
        this.interceptConsoleLogging();
        this.bindErrorHandling();
    }
    /** @internal */
    handleMessageData(data) {
        switch (data.type) {
            case this.getMessage('update'):
                this.updateConnectedBaseUrl(data.connectedBaseUrl);
                break;
            default:
        }
    }
    updateConnectedBaseUrl(connectedBaseUrl) {
        this.connectedBaseUrl = connectedBaseUrl;
    }
    interceptConsoleLogging() {
        (0, enhancer_1.enhanceStandardConsoleLogging)((type, ...args) => {
            if (this.connectedBaseUrl) {
                const serializedArgs = args.map((message) => util.inspect(message, true, 5, false));
                void fetch(`${this.connectedBaseUrl}/log`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        type,
                        args: serializedArgs,
                    }),
                });
            }
        });
    }
    bindErrorHandling() {
        this.window.addEventListener('error', (event) => {
            if (this.connectedBaseUrl) {
                void fetch(`${this.connectedBaseUrl}/error`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        message: event.message,
                        filename: event.filename,
                        lineno: event.lineno,
                        colno: event.colno,
                        error: util.inspect(event.error, true, 5, false),
                    }),
                });
            }
        });
    }
    polyfillErrorWithStack() {
        const testError = new Error('test');
        if (testError.stack === undefined) {
            // Polyfill for Error constructor with stack for older browsers
            const ErrorOrig = Error;
            const pushErrorFn = this.pushError.bind(this); // Bind the method to avoid this alias
            function ErrorWithStackImpl(message) {
                if (!(this instanceof ErrorWithStack)) {
                    return new ErrorWithStack(message);
                }
                this.name = 'Error';
                let stack;
                try {
                    // This is the only way to get a stack in old browsers
                    throw new ErrorOrig();
                }
                catch (error) {
                    stack = error.stack;
                }
                this.constructor = ErrorWithStack;
                this.message = message;
                this.stack = stack;
                pushErrorFn(this);
            }
            const ErrorWithStack = ErrorWithStackImpl;
            ErrorWithStack.prototype = ErrorOrig.prototype;
            this.window.Error = ErrorWithStack;
        }
    }
    polyfillErrorEvent() {
        // Polyfill ErrorEvent.error of addEventListener('error') for older browsers
        this.window.addEventListener('error', (event) => {
            if (event.error === undefined) {
                // Not standardized. ErrorEvent.message can be prefixed with 'Error: ' or 'Uncaught Error: ' based on version of chrome
                const errorMessage = Object.keys(this.errorsMap).find(function (key) {
                    return event.message.indexOf(key) !== -1;
                });
                if (errorMessage) {
                    const error = this.errorsMap[errorMessage];
                    delete this.errorsMap[errorMessage];
                    event.error = error;
                    console.warn('Error was polyfilled with not exact algorithm and may not be accurate', error);
                }
            }
        });
    }
    pushError(error) {
        this.errorsMap[error.message] = error;
        const keys = Object.keys(this.errorsMap);
        if (keys.length > MAX_ERRORS_BUFFER_COUNT) {
            // Remove the oldest error
            const oldestKey = keys[0];
            delete this.errorsMap[oldestKey];
        }
    }
    getMessage(name) {
        return this.messagePrefix + '.' + Connect.MESSAGE_PREFIX + '.' + name;
    }
}
exports.default = Connect;
//# sourceMappingURL=Connect.js.map