const core = require('@sentry/core');
const node = require('@sentry/node');
const electron = require('electron');
const url = require('url');

/**
 * Trimmed down version of the code from Electron here:
 * https://github.com/electron/electron/blob/f3df76dbdc58cb704637b89357e1400791c92cfe/lib/browser/api/net.ts#L209-L269
 *
 * We want to match the final URL that Electron uses
 */
function parseOptions(optionsIn) {
    if (typeof optionsIn === 'string') {
        // For full URL strings, use the WHATWG URL API directly
        try {
            return { method: 'GET', url: new URL(optionsIn).href };
        }
        catch {
            // If URL parsing fails, return the original string
            return { method: 'GET', url: optionsIn };
        }
    }
    const method = (optionsIn.method || 'GET').toUpperCase();
    const options = optionsIn;
    let url$1 = 'url' in options ? options.url : undefined;
    if (!url$1) {
        const urlObj = {};
        urlObj.protocol = options.protocol || 'http:';
        if (options.host) {
            urlObj.host = options.host;
        }
        else {
            if (options.hostname) {
                urlObj.hostname = options.hostname;
            }
            else {
                urlObj.hostname = 'localhost';
            }
            if (options.port) {
                urlObj.port = options.port;
            }
        }
        // Use parseStringToURLObject for path parsing (handles relative URLs)
        const pathObj = core.parseStringToURLObject(options.path || '/');
        urlObj.pathname = pathObj?.pathname;
        urlObj.search = pathObj?.search;
        urlObj.hash = pathObj?.hash;
        url$1 = url.format(urlObj);
    }
    return {
        method,
        url: url$1,
    };
}
function createWrappedRequestFactory({ tracing, breadcrumbs }, { enableLogs, tracePropagationTargets, propagateTraceparent }) {
    // We're caching results so we don't have to recompute regexp every time we create a request.
    const createSpanUrlMap = new core.LRUMap(100);
    const headersUrlMap = new core.LRUMap(100);
    const shouldCreateSpan = (method, url) => {
        if (tracing === undefined) {
            return true;
        }
        if (tracing === false) {
            return false;
        }
        const key = `${method}:${url}`;
        const cachedDecision = createSpanUrlMap.get(key);
        if (cachedDecision !== undefined) {
            return cachedDecision;
        }
        const decision = tracing === true || tracing(method, url);
        createSpanUrlMap.set(key, decision);
        return decision;
    };
    // This will be considerably simpler once `tracingOrigins` is removed in the next major release
    const shouldAttachTraceData = (method, url) => {
        const key = `${method}:${url}`;
        const cachedDecision = headersUrlMap.get(key);
        if (cachedDecision !== undefined) {
            return cachedDecision;
        }
        if (tracePropagationTargets) {
            const decision = core.stringMatchesSomePattern(url, tracePropagationTargets);
            headersUrlMap.set(key, decision);
            return decision;
        }
        // We cannot reach here since either `tracePropagationTargets` or `tracingOrigins` will be defined but TypeScript
        // cannot infer that
        return true;
    };
    /**
     * Captures Breadcrumb based on provided request/response pair
     */
    const addRequestBreadcrumb = (event, method, url, req, res) => {
        const level = core.getBreadcrumbLogLevelFromHttpStatusCode(res?.statusCode);
        core.addBreadcrumb({
            type: 'http',
            category: 'electron.net',
            data: {
                url,
                method: method,
                status_code: res?.statusCode,
            },
            level,
        }, {
            event,
            request: req,
            response: res,
        });
        if (!enableLogs) {
            return;
        }
        const attributes = {
            'sentry.origin': 'auto.electron.net',
            statusCode: res?.statusCode,
        };
        switch (level) {
            case 'error':
                node.logger.error(node.logger.fmt `Electron.net request failed: ${method} ${url}`, attributes);
                break;
            case 'warning':
                node.logger.warn(node.logger.fmt `Electron.net request warning: ${method} ${url}`, attributes);
                break;
            default:
                node.logger.info(node.logger.fmt `Electron.net request succeeded: ${method} ${url}`, attributes);
        }
    };
    return function wrappedRequestMethodFactory(originalRequestMethod) {
        return function requestMethod(reqOptions, ...args) {
            const { url, method } = parseOptions(reqOptions);
            const request = originalRequestMethod.apply(this, [reqOptions, ...args]);
            if (url.match(/sentry_key/) || request.getHeader('x-sentry-auth')) {
                return request;
            }
            const span = shouldCreateSpan(method, url)
                ? core.startInactiveSpan({
                    name: `${method} ${url}`,
                    onlyIfParent: true,
                    attributes: {
                        url,
                        type: 'net.request',
                        'http.method': method,
                    },
                    op: 'http.client',
                })
                : new core.SentryNonRecordingSpan();
            span.setAttribute(core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.http.electron.net');
            if (shouldAttachTraceData(method, url)) {
                const inject = () => {
                    for (const [key, value] of Object.entries(core.getTraceData({ propagateTraceparent }))) {
                        core.debug.log(`[Tracing] Adding ${key} header ${value} to outgoing request to "${url}": `);
                        request.setHeader(key, value);
                    }
                };
                if (span.isRecording()) {
                    core.withActiveSpan(span, inject);
                }
                else {
                    inject();
                }
            }
            return request
                .once('response', function (res) {
                if (breadcrumbs !== false) {
                    addRequestBreadcrumb('response', method, url, this, res);
                }
                if (res.statusCode) {
                    core.setHttpStatus(span, res.statusCode);
                }
                span.end();
            })
                .once('error', function (_error) {
                if (breadcrumbs !== false) {
                    addRequestBreadcrumb('error', method, url, this, undefined);
                }
                core.setHttpStatus(span, 500);
                span.end();
            });
        };
    };
}
/**
 * Electron 'net' module integration
 */
const electronNetIntegration = core.defineIntegration((options = {}) => {
    return {
        name: 'ElectronNet',
        setup(client) {
            // No need to instrument if we don't want to track anything
            if (options.breadcrumbs === false && options.tracing === false) {
                return;
            }
            core.fill(electron.net, 'request', createWrappedRequestFactory(options, client.getOptions()));
        },
    };
});

exports.electronNetIntegration = electronNetIntegration;
//# sourceMappingURL=net-breadcrumbs.js.map
