"use strict";

// Airbrake integration helper for jso-symbolicate.
//
// Airbrake's JS notifier exposes `addFilter(fn)` which receives the notice
// and returns it (or null to drop it). Use it to demangle the error
// type/message and every backtrace frame.
//
// Usage:
//
//   const { Notifier } = require("@airbrake/browser");
//   const { buildLookup } = require("jso-symbolicate");
//   const { createAirbrakeFilter } = require("jso-symbolicate/airbrake");
//
//   const lookup = buildLookup(
//       require("./report.json").Report.GlobalIdentifierMap,
//       require("./report.json").Report.MemberIdentifierMap
//   );
//
//   const airbrake = new Notifier({ projectId: ..., projectKey: ... });
//   airbrake.addFilter(createAirbrakeFilter(lookup));

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

/**
 * Build an Airbrake filter. The filter receives the notice and must return
 * it for Airbrake to continue sending. Returning null/false would drop the
 * notice; symbolication errors never cause that — we return the original
 * notice on any internal failure.
 */
function createAirbrakeFilter(lookup) {
    if (!lookup || typeof lookup !== "object") {
        return function (notice) { return notice; };
    }
    return function filter(notice) {
        try {
            if (!notice || typeof notice !== "object") return notice;
            const errors = Array.isArray(notice.errors) ? notice.errors : null;
            if (errors) {
                for (const err of errors) {
                    if (!err) continue;
                    if (typeof err.type === "string") err.type = demangleStack(err.type, lookup);
                    if (typeof err.message === "string") err.message = demangleStack(err.message, lookup);
                    if (Array.isArray(err.backtrace)) {
                        for (const frame of err.backtrace) {
                            if (!frame) continue;
                            if (typeof frame.function === "string") frame.function = demangleStack(frame.function, lookup);
                            if (typeof frame.file === "string") frame.file = demangleStack(frame.file, lookup);
                            if (typeof frame.code === "object" && frame.code) {
                                // Airbrake source-code context: { lineNumber: "source line" }
                                for (const k of Object.keys(frame.code)) {
                                    if (typeof frame.code[k] === "string") {
                                        frame.code[k] = demangleStack(frame.code[k], lookup);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            notice.context = notice.context || {};
            notice.context.jso_symbolicated = true;
        } catch (e) {
            try {
                notice.context = notice.context || {};
                notice.context.jso_symbolicate_error = String(e && e.message || e);
            } catch (_) { /* ignore */ }
        }
        return notice;
    };
}

module.exports = { createAirbrakeFilter };
