// sentry.scrub.ts — strip secrets/PII before any event leaves the process.
// Shared by sentry.client.config.ts (browser) and sentry.server.config.ts (node).
//
// This app stores the iDempiere JWT in the `logship_it` cookie and a base64-encoded
// password in `logship_py` — those must NEVER reach Sentry. We also drop request
// bodies, auth headers and query strings, which can carry credentials or customer PII.
const SENSITIVE_HEADERS = ['cookie', 'set-cookie', 'authorization', 'x-api-key']

// Keys whose VALUES must never leave the process (secrets/credentials only — business
// data like names/addresses/IDs is intentionally kept so server errors stay debuggable).
const SENSITIVE_KEY_RX = /(pass|secret|token|credential|authorization|api[_-]?key|marketplace_key|marketplace_secret|logship_py|logship_it|vapid)/i

const MAX_STRING = 2000   // truncate long strings (base64 blobs, HTML, label data)
const MAX_DEPTH = 8       // guard against deeply nested / pathological payloads
const MAX_BYTES = 16000   // overall cap on the serialised payload Sentry stores

export function scrubEvent(event: any): any | null {
  if (event?.request) {
    // Cookies carry the JWT (logship_it) and the base64 password (logship_py) — never send.
    delete event.request.cookies
    delete event.request.data // raw request body auto-attached by Sentry (may carry secrets)
    if (event.request.query_string) event.request.query_string = '[stripped]'
    if (event.request.headers) {
      for (const h of Object.keys(event.request.headers)) {
        if (SENSITIVE_HEADERS.includes(h.toLowerCase())) delete event.request.headers[h]
      }
    }
  }

  // Drop any captured fetch/xhr breadcrumb bodies (keep method + url for context).
  if (Array.isArray(event?.breadcrumbs)) {
    for (const b of event.breadcrumbs) {
      if (b?.data && typeof b.data === 'object' && 'body' in b.data) delete b.data.body
    }
  }

  // 2nd line of defence: re-scrub the request_payload context attached by the
  // sentry-payload middleware, in case it was ever set elsewhere unscrubbed.
  const payload = event?.contexts?.request_payload
  if (payload) {
    if (payload.body) payload.body = scrubPayload(payload.body)
    if (payload.query) payload.query = scrubPayload(payload.query)
  }

  return event
}

// Deep-clone a request payload, redacting secret-bearing keys and capping size.
// Idempotent so beforeSend can safely re-run it as a 2nd line of defence.
export function scrubPayload(input: any): any {
  const seen = new WeakSet()
  const walk = (val: any, depth: number): any => {
    if (val === null || val === undefined) return val
    if (typeof val === 'string') {
      return val.length > MAX_STRING
        ? val.slice(0, MAX_STRING) + `…[+${val.length - MAX_STRING} chars]`
        : val
    }
    if (typeof val !== 'object') return val
    if (depth >= MAX_DEPTH) return '[depth-limited]'
    if (seen.has(val)) return '[circular]'
    seen.add(val)
    if (Array.isArray(val)) return val.map((v) => walk(v, depth + 1))
    const out: any = {}
    for (const k of Object.keys(val)) {
      out[k] = SENSITIVE_KEY_RX.test(k) ? '[redacted]' : walk(val[k], depth + 1)
    }
    return out
  }
  let result = walk(input, 0)
  try {
    const json = JSON.stringify(result)
    if (json && json.length > MAX_BYTES) {
      result = { _truncated: true, _bytes: json.length, preview: json.slice(0, MAX_BYTES) }
    }
  } catch { /* non-serialisable → leave as-is */ }
  return result
}
