const tslib = require('tslib');
const core = require('@sentry/core');
const utils = require('@sentry/utils');
const electron = require('electron');
const context = require('../../context.js');
const electronNormalize = require('../../electron-normalize.js');
const fs = require('../../fs.js');
const renderers = require('../../renderers.js');
const sessions = require('../../sessions.js');
const store = require('../../store.js');
const minidumpLoader = require('./minidump-loader.js');
const merge = require('../../../common/merge.js');

const INTEGRATION_NAME = 'SentryMinidump';
/**
 * Sends minidumps via the Sentry uploader
 */
const sentryMinidumpIntegration = core.defineIntegration((options = {}) => {
    // The remaining number of minidumps that can be sent in this session
    let minidumpsRemaining = options.maxMinidumpsPerSession || 10;
    // Store to persist context information beyond application crashes.
    let scopeStore;
    // We need to store the scope in a variable here so it can be attached to minidumps
    let scopeLastRun;
    let minidumpLoader$1;
    function startCrashReporter() {
        utils.logger.log('Starting Electron crashReporter');
        electron.crashReporter.start({
            companyName: '',
            ignoreSystemCrashHandler: true,
            productName: electron.app.name || electron.app.getName(),
            // Empty string doesn't work for Linux Crashpad and no submitURL doesn't work for older versions of Electron
            submitURL: 'https://f.a.k/e',
            uploadToServer: false,
            compress: true,
        });
    }
    function setupScopeListener(currentRelease, currentEnvironment) {
        const scopeChanged = (updatedScope) => {
            // Since the initial scope read is async, we need to ensure that any writes do not beat that
            // https://github.com/getsentry/sentry-electron/issues/585
            setImmediate(() => tslib.__awaiter(this, void 0, void 0, function* () {
                return scopeStore === null || scopeStore === void 0 ? void 0 : scopeStore.set({
                    scope: updatedScope.getScopeData(),
                    event: yield context.getEventDefaults(currentRelease, currentEnvironment),
                });
            }));
        };
        const scope = core.getCurrentScope();
        if (scope) {
            scope.addScopeListener(scopeChanged);
            // Ensure at least one event is written to disk
            scopeChanged(scope);
        }
    }
    function sendNativeCrashes(client, eventIn) {
        var _a, _b, _c, _d;
        return tslib.__awaiter(this, void 0, void 0, function* () {
            // Whenever we are called, assume that the crashes we are going to load down
            // below have occurred recently. This means, we can use the same event data
            // for all minidumps that we load now. There are two conditions:
            //
            //  1. The application crashed and we are just starting up. The stored
            //     breadcrumbs and context reflect the state during the application
            //     crash.
            //
            //  2. A renderer process crashed recently and we have just been notified
            //     about it. Just use the breadcrumbs and context information we have
            //     right now and hope that the delay was not too long.
            const event = eventIn;
            // If this is a native main process crash, we need to apply the scope and context from the previous run
            if (((_a = event.tags) === null || _a === void 0 ? void 0 : _a['event.process']) === 'browser') {
                const previousRun = yield scopeLastRun;
                if (previousRun) {
                    if (previousRun.scope) {
                        core.applyScopeDataToEvent(event, previousRun.scope);
                    }
                    event.release = ((_b = previousRun.event) === null || _b === void 0 ? void 0 : _b.release) || event.release;
                    event.environment = ((_c = previousRun.event) === null || _c === void 0 ? void 0 : _c.environment) || event.environment;
                    event.contexts = ((_d = previousRun.event) === null || _d === void 0 ? void 0 : _d.contexts) || event.contexts;
                }
            }
            if (!event) {
                return false;
            }
            if (minidumpsRemaining <= 0) {
                utils.logger.log('Not sending minidumps because the limit has been reached');
            }
            // If the SDK is not enabled, or we've already reached the minidump limit, tell the loader to delete all minidumps
            const deleteAll = client.getOptions().enabled === false || minidumpsRemaining <= 0;
            let minidumpFound = false;
            yield (minidumpLoader$1 === null || minidumpLoader$1 === void 0 ? void 0 : minidumpLoader$1(deleteAll, (attachment) => {
                minidumpFound = true;
                if (minidumpsRemaining > 0) {
                    minidumpsRemaining -= 1;
                    core.captureEvent(event, { attachments: [attachment] });
                }
            }));
            return minidumpFound;
        });
    }
    function sendRendererCrash(client, options, contents, details) {
        var _a;
        return tslib.__awaiter(this, void 0, void 0, function* () {
            const { getRendererName, release, environment } = options;
            const crashedProcess = (getRendererName === null || getRendererName === void 0 ? void 0 : getRendererName(contents)) || 'renderer';
            utils.logger.log(`'${crashedProcess}' process '${details.reason}'`);
            const event = merge.mergeEvents(yield context.getEventDefaults(release, environment), {
                contexts: {
                    electron: {
                        crashed_url: ((_a = renderers.getRendererProperties(contents.id)) === null || _a === void 0 ? void 0 : _a.url) || 'unknown',
                        details,
                    },
                },
                level: 'fatal',
                // The default is javascript
                platform: 'native',
                tags: {
                    'event.environment': 'native',
                    'event.process': crashedProcess,
                    'exit.reason': details.reason,
                    event_type: 'native',
                },
            });
            const found = yield sendNativeCrashes(client, event);
            if (found) {
                sessions.sessionCrashed();
            }
        });
    }
    function sendChildProcessCrash(client, options, details) {
        return tslib.__awaiter(this, void 0, void 0, function* () {
            utils.logger.log(`${details.type} process has ${details.reason}`);
            const { release, environment } = options;
            const event = merge.mergeEvents(yield context.getEventDefaults(release, environment), {
                contexts: {
                    electron: { details },
                },
                level: 'fatal',
                // The default is javascript
                platform: 'native',
                tags: {
                    'event.environment': 'native',
                    'event.process': details.type,
                    'exit.reason': details.reason,
                    event_type: 'native',
                },
            });
            const found = yield sendNativeCrashes(client, event);
            if (found) {
                sessions.sessionCrashed();
            }
        });
    }
    return {
        name: INTEGRATION_NAME,
        setupOnce() {
            // noop
        },
        setup(client) {
            // Mac AppStore builds cannot run the crash reporter due to the sandboxing
            // requirements. In this case, we prevent enabling native crashes entirely.
            // https://electronjs.org/docs/tutorial/mac-app-store-submission-guide#limitations-of-mas-build
            if (process.mas) {
                return;
            }
            startCrashReporter();
            scopeStore = new store.BufferedWriteStore(fs.getSentryCachePath(), 'scope_v3', {
                scope: new core.Scope().getScopeData(),
            });
            scopeLastRun = scopeStore.get();
            minidumpLoader$1 = minidumpLoader.getMinidumpLoader();
            const options = client.getOptions();
            const currentRelease = (options === null || options === void 0 ? void 0 : options.release) || context.getDefaultReleaseName();
            const currentEnvironment = (options === null || options === void 0 ? void 0 : options.environment) || context.getDefaultEnvironment();
            setupScopeListener(currentRelease, currentEnvironment);
            if (!(options === null || options === void 0 ? void 0 : options.dsn)) {
                throw new utils.SentryError('Attempted to enable Electron native crash reporter but no DSN was supplied');
            }
            renderers.trackRendererProperties();
            electronNormalize.onRendererProcessGone(electronNormalize.EXIT_REASONS, (contents, details) => sendRendererCrash(client, options, contents, details));
            electronNormalize.onChildProcessGone(electronNormalize.EXIT_REASONS, (details) => sendChildProcessCrash(client, options, details));
            // Start to submit recent minidump crashes. This will load breadcrumbs and
            // context information that was cached on disk in the previous app run, prior to the crash.
            sendNativeCrashes(client, {
                level: 'fatal',
                platform: 'native',
                tags: {
                    'event.environment': 'native',
                    'event.process': 'browser',
                    event_type: 'native',
                },
            })
                .then((minidumpsFound) => 
            // Check for previous uncompleted session. If a previous session exists
            // and no minidumps were found, its likely an abnormal exit
            sessions.checkPreviousSession(minidumpsFound))
                .catch((error) => utils.logger.error(error));
        },
    };
});
/**
 * Sends minidumps via the Sentry uploader
 *
 * @deprecated Use `sentryMinidumpIntegration()` instead
 */
// eslint-disable-next-line deprecation/deprecation
const SentryMinidump = core.convertIntegrationFnToClass(INTEGRATION_NAME, sentryMinidumpIntegration);

exports.SentryMinidump = SentryMinidump;
exports.sentryMinidumpIntegration = sentryMinidumpIntegration;
//# sourceMappingURL=index.js.map
