/**
 * Utility for safely manipulating `Error.stackTraceLimit` in environments
 * where intrinsics may be frozen (e.g., SES / hardened JavaScript or
 * deterministic sandboxes such as Temporal). When the property is non-writable,
 * mutating it throws, so all manipulation here degrades to a best-effort,
 * silent no-op.
 *
 * Mirrors the guard Node uses internally:
 * https://github.com/nodejs/node/blob/e77694631f1642c302f664703197b5aabc65b482/lib/internal/errors.js#L246
 *
 * The error is constructed inline at each call site (rather than inside a
 * closure here) so the captured stack trace keeps pointing at the real caller
 * instead of this module.
 *
 * @internal
 */
import type { ErrorWithStackTraceLimit } from "./tracer.ts"

const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
const ObjectPrototypeHasOwnProperty = Object.prototype.hasOwnProperty
const ObjectIsExtensible = Object.isExtensible

/**
 * Check if `Error.stackTraceLimit` is writable.
 * Returns `false` if the property is frozen, non-writable, or `Error` is non-extensible.
 *
 * @internal
 */
export const isStackTraceLimitWritable = (): boolean => {
  const desc = ObjectGetOwnPropertyDescriptor(Error, "stackTraceLimit")
  if (desc === undefined) {
    return ObjectIsExtensible(Error)
  }

  return ObjectPrototypeHasOwnProperty.call(desc, "writable")
    ? desc.writable === true
    : desc.set !== undefined
}

// Cache the check result since it won't change during runtime
const canWriteStackTraceLimit = isStackTraceLimitWritable()

/**
 * Get the current `Error.stackTraceLimit` value.
 * Returns `undefined` if the property doesn't exist.
 *
 * @internal
 */
export const getStackTraceLimit = (): number | undefined => (Error as ErrorWithStackTraceLimit).stackTraceLimit

/**
 * Safely set `Error.stackTraceLimit` if possible, otherwise no-op.
 *
 * Accepts `undefined` so a value read via {@link getStackTraceLimit} can be
 * restored faithfully.
 *
 * @internal
 */
export const setStackTraceLimit = (value: number | undefined): void => {
  if (canWriteStackTraceLimit) {
    ;(Error as ErrorWithStackTraceLimit).stackTraceLimit = value
  }
}
