/**
 * Sentry init for the Express backend (Session 10).
 *
 * Boot is GRACEFUL when SENTRY_DSN is unset: `initSentry()` no-ops and
 * the module's exported `Sentry` is a stub whose every method is a
 * noop. This lets callers wire `Sentry.captureException(...)` etc.
 * unconditionally — when the DSN isn't configured (dev, CI, tests),
 * nothing crashes and no events are sent.
 *
 * PII posture: `sendDefaultPii: false` plus an `events`-stage hook
 * that strips `user.ip_address` and `request.cookies`. We log code
 * paths and errors, not user identities.
 *
 * Sample rate: 10% traces (free tier friendly). 100% errors.
 */

const realSentry = require('@sentry/node');

// Initialized lazily so this module is safe to require even when the
// DSN is absent.
let _initialized = false;

function buildClient() {
  return {
    init: realSentry.init,
    captureException: realSentry.captureException,
    captureMessage: realSentry.captureMessage,
    setupExpressErrorHandler: realSentry.setupExpressErrorHandler,
    addBreadcrumb: realSentry.addBreadcrumb,
    setUser: realSentry.setUser,
    setTag: realSentry.setTag,
    setContext: realSentry.setContext,
  };
}

function buildNoop() {
  // Every Sentry surface used in the codebase returns either undefined
  // (most) or a no-op express middleware (`setupExpressErrorHandler`).
  const noopMiddleware = (_req, _res, next) => next();
  return {
    init: () => {},
    captureException: () => {},
    captureMessage: () => {},
    setupExpressErrorHandler: (_app) => {
      // The real one mutates the app; the noop simply returns without
      // attaching anything. The caller pattern is:
      //   Sentry.setupExpressErrorHandler(app);
      // which evaluates the call for its side effects.
    },
    addBreadcrumb: () => {},
    setUser: () => {},
    setTag: () => {},
    setContext: () => {},
    Handlers: { errorHandler: () => noopMiddleware },
  };
}

let Sentry = buildNoop();

function initSentry({ dsn = process.env.SENTRY_DSN, environment = process.env.NODE_ENV, release } = {}) {
  if (_initialized) return Sentry;
  if (!dsn) {
    // Stay on the noop client.
    return Sentry;
  }
  Sentry = buildClient();
  Sentry.init({
    dsn,
    environment: environment || 'development',
    release,
    tracesSampleRate: 0.1,
    sendDefaultPii: false,
    beforeSend(event) {
      // PII scrubbing. Sentry occasionally fills these via auto-context.
      if (event.user) {
        delete event.user.ip_address;
        delete event.user.email;
      }
      if (event.request) {
        delete event.request.cookies;
        // Strip the Authorization header to avoid logging bearer tokens.
        if (event.request.headers) {
          delete event.request.headers.authorization;
          delete event.request.headers.cookie;
          delete event.request.headers['x-internal-key'];
          delete event.request.headers['x-vyndr-internal-key'];
        }
      }
      return event;
    },
  });
  _initialized = true;
  return Sentry;
}

function getSentry() {
  return Sentry;
}

function isInitialized() {
  return _initialized;
}

// Test helper — reset to the noop client so subsequent initSentry()
// calls re-run. Not exported via the main surface; live behind __internals.
function __resetForTests() {
  _initialized = false;
  Sentry = buildNoop();
}

module.exports = {
  initSentry,
  getSentry,
  isInitialized,
  // Convenience re-export so callers can do `const { Sentry } = require(...)`.
  // Note: this is a live binding — mutated by initSentry() on first call.
  get Sentry() { return Sentry; },
  __internals: { __resetForTests, buildNoop, buildClient },
};
