"use strict";

// Bugsnag integration helper for jso-symbolicate.
//
// Bugsnag's auto-symbolication requires uploaded Source Map v3 files. JSO's
// identifier map is name-only (no positions), so the recommended pattern is
// a Bugsnag onError callback that runs *before* the event ships: demangle
// function names in every stack frame, then let Bugsnag display the cleaned-up
// names. Mirror of the Sentry beforeSend pattern.
//
// Usage:
//
//   const Bugsnag = require("@bugsnag/js");
//   const symbolicate = require("jso-symbolicate");
//   const { createBugsnagOnError } = require("jso-symbolicate/bugsnag");
//
//   const lookup = symbolicate.buildLookup(
//       require("./report.json").Report.GlobalIdentifierMap,
//       require("./report.json").Report.MemberIdentifierMap
//   );
//
//   Bugsnag.start({
//       apiKey: "...",
//       onError: createBugsnagOnError(lookup)
//   });

const { demangleStack } = require("./index.js");

/**
 * Build a Bugsnag onError callback. Returns true so Bugsnag continues sending
 * the (now-demangled) event.
 */
function createBugsnagOnError(lookup) {
    if (!lookup || typeof lookup !== "object") {
        return function () { return true; };
    }

    return function onError(event) {
        try {
            const errors = event && (event.errors || []);
            for (const err of errors) {
                if (!err) continue;
                if (typeof err.errorMessage === "string") {
                    err.errorMessage = demangleStack(err.errorMessage, lookup);
                }
                if (typeof err.errorClass === "string") {
                    err.errorClass = demangleStack(err.errorClass, lookup);
                }
                if (Array.isArray(err.stacktrace)) {
                    for (const frame of err.stacktrace) {
                        if (!frame) continue;
                        if (typeof frame.method === "string") {
                            frame.method = demangleStack(frame.method, lookup);
                        }
                        if (typeof frame.file === "string") {
                            // file names rarely contain mangled identifiers but
                            // run the substitution defensively in case bundlers
                            // embed module names.
                            frame.file = demangleStack(frame.file, lookup);
                        }
                    }
                }
            }
            if (event && event.addMetadata) {
                event.addMetadata("jso", { symbolicated: true });
            }
        } catch (e) {
            // Don't let a symbolication error swallow the crash report.
            if (event && event.addMetadata) {
                event.addMetadata("jso", { symbolicate_error: String(e && e.message || e) });
            }
        }
        return true;
    };
}

module.exports = { createBugsnagOnError };
