const tslib = require('tslib');
const node = require('@sentry/node');
const utils = require('@sentry/utils');
const electron = require('electron');
const anr = require('./integrations/anr.js');
const sessions = require('./sessions.js');

function getRendererName(contents) {
    var _a, _b;
    const options = (_a = node.getClient()) === null || _a === void 0 ? void 0 : _a.getOptions();
    return (_b = options === null || options === void 0 ? void 0 : options.getRendererName) === null || _b === void 0 ? void 0 : _b.call(options, contents);
}
function sendRendererAnrEvent(contents, blockedMs, frames) {
    sessions.sessionAnr();
    const rendererName = getRendererName(contents) || 'renderer';
    const event = {
        level: 'error',
        exception: {
            values: [
                {
                    type: 'ApplicationNotResponding',
                    value: `Application Not Responding for at least ${blockedMs} ms`,
                    stacktrace: { frames },
                    mechanism: {
                        // This ensures the UI doesn't say 'Crashed in' for the stack trace
                        type: 'ANR',
                    },
                },
            ],
        },
        tags: {
            'event.process': rendererName,
        },
    };
    node.captureEvent(event);
}
function rendererDebugger(contents, pausedStack) {
    contents.debugger.attach('1.3');
    // Collect scriptId -> url map so we can look up the filenames later
    const scripts = new Map();
    const getModuleFromFilename = node.createGetModuleFromFilename(electron.app.getAppPath());
    contents.debugger.on('message', (_, method, params) => {
        if (method === 'Debugger.scriptParsed') {
            const param = params;
            scripts.set(param.scriptId, param.url);
        }
        else if (method === 'Debugger.paused') {
            const param = params;
            if (param.reason !== 'other') {
                return;
            }
            // copy the frames
            const callFrames = [...param.callFrames];
            contents.debugger.sendCommand('Debugger.resume').then(null, () => {
                // ignore
            });
            const stackFrames = utils.stripSentryFramesAndReverse(callFrames.map((frame) => utils.callFrameToStackFrame(frame, scripts.get(frame.location.scriptId), getModuleFromFilename)));
            pausedStack(stackFrames);
        }
    });
    // In node, we enable just before pausing but for Chrome, the debugger must be enabled before he ANR event occurs
    contents.debugger.sendCommand('Debugger.enable').catch(() => {
        // ignore
    });
    return () => {
        return contents.debugger.sendCommand('Debugger.pause');
    };
}
let rendererWatchdogTimers;
function createHrTimer() {
    let lastPoll = process.hrtime();
    return {
        getTimeMs: () => {
            const [seconds, nanoSeconds] = process.hrtime(lastPoll);
            return Math.floor(seconds * 1e3 + nanoSeconds / 1e6);
        },
        reset: () => {
            lastPoll = process.hrtime();
        },
    };
}
/** Creates a renderer ANR status hook */
function createRendererAnrStatusHandler() {
    function log(message, ...args) {
        utils.logger.log(`[Renderer ANR] ${message}`, ...args);
    }
    return (message, contents) => {
        rendererWatchdogTimers = rendererWatchdogTimers || new Map();
        let watchdog = rendererWatchdogTimers.get(contents);
        if (watchdog === undefined) {
            log('Renderer sent first status message', message.config);
            let pauseAndCapture;
            if (message.config.captureStackTrace) {
                log('Connecting to debugger');
                pauseAndCapture = rendererDebugger(contents, (frames) => {
                    log('Event captured with stack frames');
                    sendRendererAnrEvent(contents, message.config.anrThreshold, frames);
                });
            }
            watchdog = utils.watchdogTimer(createHrTimer, 100, message.config.anrThreshold, () => tslib.__awaiter(this, void 0, void 0, function* () {
                log('Watchdog timeout');
                if (pauseAndCapture) {
                    log('Pausing debugger to capture stack trace');
                    pauseAndCapture();
                }
                else {
                    log('Capturing event');
                    sendRendererAnrEvent(contents, message.config.anrThreshold);
                }
            }));
            contents.once('destroyed', () => {
                rendererWatchdogTimers === null || rendererWatchdogTimers === void 0 ? void 0 : rendererWatchdogTimers.delete(contents);
            });
            rendererWatchdogTimers.set(contents, watchdog);
        }
        watchdog.poll();
        if (message.status !== 'alive') {
            log('Renderer visibility changed', message.status);
            watchdog.enabled(message.status === 'visible');
        }
    };
}
/**
 * @deprecated Use `Anr` integration instead.
 *
 * ```js
 * import { init, anrIntegration } from '@sentry/electron';
 *
 * init({
 *   dsn: "__DSN__",
 *   integrations: [anrIntegration({ captureStackTrace: true })],
 * });
 * ```
 */
function enableMainProcessAnrDetection(options = {}) {
    var _a;
    // eslint-disable-next-line deprecation/deprecation
    const integration = new anr.Anr(options);
    const client = node.getClient();
    (_a = integration.setup) === null || _a === void 0 ? void 0 : _a.call(integration, client);
    return Promise.resolve();
}

exports.createRendererAnrStatusHandler = createRendererAnrStatusHandler;
exports.enableMainProcessAnrDetection = enableMainProcessAnrDetection;
//# sourceMappingURL=anr.js.map
