// server/middleware/sentry-payload.ts
// Attaches the (secret-scrubbed) request payload + query to the per-request Sentry scope so
// captured server errors show what was actually sent. Zero-touch on the ~1177 routes:
// errorHandlingHelper's captureException automatically merges this isolation scope into the
// event. The context is only transmitted when an error is captured (>= 500) — see
// server/utils/errorHandlingHelper.ts. Read-only w.r.t. the request: it never mutates the
// request/response/event.context, and self-disables when SENTRY_DSN is empty.
import * as Sentry from '@sentry/nuxt'
import { scrubPayload } from '../../sentry.scrub'

const WRITE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE'])

export default defineEventHandler(async (event) => {
  // Only when Sentry is enabled; never disturb the request otherwise.
  if (!process.env.SENTRY_DSN) return

  try {
    const ctx: any = { method: event.method, path: event.path }

    // Query params (all methods) — failing GET list/filter endpoints show their inputs.
    const query = getQuery(event)
    if (query && Object.keys(query).length) ctx.query = scrubPayload(query)

    // JSON write bodies only — skips multipart uploads & SSE streams (non-JSON content-type),
    // so file-upload / streaming routes are never consumed early.
    if (WRITE_METHODS.has(event.method)) {
      const contentType = getHeader(event, 'content-type') || ''
      if (contentType.includes('application/json')) {
        // h3 caches the parsed body on the event, so the route's own readBody() still works.
        const body = await readBody(event)
        if (body !== undefined) ctx.body = scrubPayload(body)
      }
    }

    // Isolation scope (not current scope): shared across the whole request regardless of
    // span forking, so it's present when errorHandlingHelper captures later.
    Sentry.getIsolationScope().setContext('request_payload', ctx)
  } catch {
    /* telemetry must never break a request */
  }
})
