///|
pub(all) suberror RuntimeError {
  UnsupportedHostCapability(String)
  HostExit(Int32)
  StackUnderflow
  StackOverflow
  TypeMismatch
  InvalidModule(String)
  ForeignStore
  InvalidHandle
  StoreClosed
  MemoryClosed
  InvalidMemoryType(String)
  InvalidTableType(String)
  HeapRepresentationInUse
  OutOfBoundsMemoryAccess
  OutOfBoundsTableAccess
  OutOfBoundsArrayAccess // GC array access out of bounds
  UndefinedElement
  UninitializedElement
  IndirectCallTypeMismatch
  DivisionByZero
  IntegerOverflow
  InvalidConversion
  OutOfMemory
  InvalidResourceLimit
  InvalidExecutionLimit
  ExecutionLimitsUnavailable
  ExecutionCancelled
  ExecutionTimedOut
  ExecutionBudgetExceeded
  ResourceLimitExceeded(ResourceKind)
  TableAllocationOverflow
  TableAllocationFailed
  MemoryMinimumExceedsLimits(Int64)
  AllocationTooLarge
  AtomicWaitNonSharedMemory
  Unreachable
  UncaughtException
  CallStackExhausted
  UnknownImport(String, String) // Import not found: (module_name, field_name)
  LinkError // Import type mismatch during linking
  // Link error with extra context (e.g. which import mismatched).
  // The `Show` output still begins with "link error" for compatibility.
  LinkErrorDetail(String)
  CastFailure
  NullReference // Null exception reference (null exnref thrown)
  UnalignedAtomic
  // Used when evaluating init expressions during instantiation; this should
  // never be reached for validated modules, but helps diagnose engine gaps.
  InvalidConstantExpression(String)
  ContinuationAlreadyConsumed
  UnhandledSuspension
  UnsupportedGcContinuationStorage
  AsyncHostRequiresAsyncCall
  AsyncHostCallInProgress
  AsyncHostCallCancelled
  AsyncHostError(String)
} derive(Debug, Eq)

///|
/// Recover a structured runtime error from the language-wide `Error` type.
///
/// This is the single transport boundary used by executors and asynchronous
/// host-call adapters; unrelated errors return `None` instead of being
/// flattened to a string here.
pub fn RuntimeError::from_error(error : Error) -> RuntimeError? {
  match error {
    ContinuationAlreadyConsumed => Some(ContinuationAlreadyConsumed)
    UnhandledSuspension => Some(UnhandledSuspension)
    UnsupportedGcContinuationStorage => Some(UnsupportedGcContinuationStorage)
    HostExit(_) as error => Some(error)
    StackUnderflow => Some(StackUnderflow)
    StackOverflow => Some(StackOverflow)
    TypeMismatch => Some(TypeMismatch)
    InvalidModule(detail) => Some(InvalidModule(detail))
    ForeignStore => Some(ForeignStore)
    InvalidHandle => Some(InvalidHandle)
    StoreClosed => Some(StoreClosed)
    MemoryClosed => Some(MemoryClosed)
    InvalidMemoryType(_) as error => Some(error)
    InvalidTableType(_) as error => Some(error)
    HeapRepresentationInUse => Some(HeapRepresentationInUse)
    OutOfBoundsMemoryAccess => Some(OutOfBoundsMemoryAccess)
    OutOfBoundsTableAccess => Some(OutOfBoundsTableAccess)
    OutOfBoundsArrayAccess => Some(OutOfBoundsArrayAccess)
    UndefinedElement => Some(UndefinedElement)
    UninitializedElement => Some(UninitializedElement)
    IndirectCallTypeMismatch => Some(IndirectCallTypeMismatch)
    DivisionByZero => Some(DivisionByZero)
    IntegerOverflow => Some(IntegerOverflow)
    InvalidConversion => Some(InvalidConversion)
    MemoryMinimumExceedsLimits(_) as error => Some(error)
    TableAllocationOverflow => Some(TableAllocationOverflow)
    TableAllocationFailed => Some(TableAllocationFailed)
    InvalidExecutionLimit => Some(InvalidExecutionLimit)
    ExecutionLimitsUnavailable => Some(ExecutionLimitsUnavailable)
    ExecutionCancelled => Some(ExecutionCancelled)
    ExecutionTimedOut => Some(ExecutionTimedOut)
    ExecutionBudgetExceeded => Some(ExecutionBudgetExceeded)
    InvalidResourceLimit => Some(InvalidResourceLimit)
    ResourceLimitExceeded(_) as error => Some(error)
    OutOfMemory => Some(OutOfMemory)
    AllocationTooLarge => Some(AllocationTooLarge)
    AtomicWaitNonSharedMemory => Some(AtomicWaitNonSharedMemory)
    Unreachable => Some(Unreachable)
    UncaughtException => Some(UncaughtException)
    CallStackExhausted => Some(CallStackExhausted)
    UnknownImport(_, _) as runtime_error => Some(runtime_error)
    LinkError => Some(LinkError)
    LinkErrorDetail(_) as runtime_error => Some(runtime_error)
    CastFailure => Some(CastFailure)
    NullReference => Some(NullReference)
    UnalignedAtomic => Some(UnalignedAtomic)
    InvalidConstantExpression(_) as runtime_error => Some(runtime_error)
    AsyncHostRequiresAsyncCall => Some(AsyncHostRequiresAsyncCall)
    AsyncHostCallInProgress => Some(AsyncHostCallInProgress)
    AsyncHostCallCancelled => Some(AsyncHostCallCancelled)
    AsyncHostError(_) as runtime_error => Some(runtime_error)
    _ => None
  }
}

///|
/// Custom Show implementation for WebAssembly-spec-compliant error messages
pub impl Show for RuntimeError with fn output(self, logger) {
  let msg = match self {
    UnsupportedHostCapability(detail) =>
      "unsupported host capability: \{detail}"
    ContinuationAlreadyConsumed => "continuation already consumed"
    UnhandledSuspension => "unhandled suspension"
    UnsupportedGcContinuationStorage =>
      "Stack switching feature not compatible with GC storage"
    HostExit(code) => "host requested exit: \{code}"
    StackUnderflow => "stack underflow"
    StackOverflow => "stack overflow"
    TypeMismatch => "type mismatch"
    InvalidModule(detail) => "invalid module: \{detail}"
    ForeignStore => "instance belongs to another store"
    InvalidHandle => "invalid or stale runtime handle"
    StoreClosed => "store is closed"
    MemoryClosed => "memory is closed"
    InvalidMemoryType(detail) => "invalid memory type: \{detail}"
    InvalidTableType(detail) => "invalid table type: \{detail}"
    HeapRepresentationInUse =>
      "cannot enable native GC with live interpreter objects"
    OutOfBoundsMemoryAccess => "out of bounds memory access"
    OutOfBoundsTableAccess => "undefined element: out of bounds table access"
    OutOfBoundsArrayAccess => "out of bounds array access"
    UndefinedElement => "undefined element: out of bounds table access"
    UninitializedElement => "uninitialized element"
    IndirectCallTypeMismatch => "indirect call type mismatch"
    DivisionByZero => "integer divide by zero"
    IntegerOverflow => "integer overflow"
    InvalidConversion => "invalid conversion to integer"
    MemoryMinimumExceedsLimits(pages) =>
      "memory minimum size of \{pages.reinterpret_as_uint64()} pages exceeds memory limits"
    TableAllocationOverflow => "overflow calculating table allocation size"
    TableAllocationFailed => "failed to allocate"
    InvalidExecutionLimit => "invalid execution limit"
    ExecutionLimitsUnavailable => "execution limits require compiled safepoints"
    ExecutionCancelled => "execution cancelled"
    ExecutionTimedOut => "execution deadline exceeded"
    ExecutionBudgetExceeded => "execution safepoint budget exhausted"
    InvalidResourceLimit => "invalid resource limit"
    ResourceLimitExceeded(kind) => "host resource limit exceeded: \{Repr(kind)}"
    OutOfMemory => "out of memory"
    AllocationTooLarge => "allocation size too large"
    AtomicWaitNonSharedMemory => "atomic wait on non-shared memory"
    Unreachable => "unreachable"
    UncaughtException => "uncaught exception"
    CallStackExhausted => "call stack exhausted"
    UnknownImport(mod_name, field_name) =>
      "unknown import: \{mod_name}.\{field_name}"
    LinkError => "link error"
    LinkErrorDetail(detail) =>
      if detail == "" {
        "link error"
      } else {
        "link error: \{detail}"
      }
    CastFailure => "cast failure"
    NullReference => "null reference"
    UnalignedAtomic => "unaligned atomic"
    InvalidConstantExpression(detail) =>
      if detail == "" {
        "invalid constant expression"
      } else {
        "invalid constant expression: \{detail}"
      }
    AsyncHostRequiresAsyncCall =>
      "async host function requires an async wasm call"
    AsyncHostCallInProgress => "another async host call is already pending"
    AsyncHostCallCancelled => "async wasm call was cancelled"
    AsyncHostError(message) => "async host function failed: \{message}"
  }
  logger.write_string(msg)
}

///|
/// A stack frame entry for error reporting
pub(all) struct CallStackEntry {
  func_idx : Int32
  func_name : String? // Optional function name from debug info
}

///|
fn CallStackEntry::to_string(self : CallStackEntry) -> String {
  match self.func_name {
    Some(name) => "func[\{self.func_idx}] \{name}"
    None => "func[\{self.func_idx}]"
  }
}

///|
/// Runtime error with additional context for debugging
pub struct RuntimeErrorContext {
  error : RuntimeError
  call_stack : Array[CallStackEntry]
  instruction : String? // The instruction that caused the error
  func_idx : Int32? // The function index where error occurred
}

///|
pub fn RuntimeErrorContext::RuntimeErrorContext(
  error : RuntimeError,
) -> RuntimeErrorContext {
  { error, call_stack: [], instruction: None, func_idx: None, }
}

///|
pub fn RuntimeErrorContext::with_call_stack(
  self : RuntimeErrorContext,
  stack : Array[CallStackEntry],
) -> RuntimeErrorContext {
  { ..self, call_stack: stack, }
}

///|
pub fn RuntimeErrorContext::with_instruction(
  self : RuntimeErrorContext,
  instr : String,
) -> RuntimeErrorContext {
  { ..self, instruction: Some(instr), }
}

///|
pub fn RuntimeErrorContext::with_func_idx(
  self : RuntimeErrorContext,
  idx : Int32,
) -> RuntimeErrorContext {
  { ..self, func_idx: Some(idx), }
}

///|
/// Format the runtime error with full context for display
pub fn RuntimeErrorContext::format(self : RuntimeErrorContext) -> String {
  let result = StringBuilder::StringBuilder()
  result.write_string("runtime error: ")
  result.write_string(self.error.to_string())
  result.write_string("\n")

  // Show instruction if available
  if self.instruction is Some(instr) {
    result.write_string("  at instruction: ")
    result.write_string(instr)
    result.write_string("\n")
  }

  // Show function index if available
  if self.func_idx is Some(idx) {
    result.write_string("  in function: func[\{idx}]\n")
  }

  // Show call stack
  if self.call_stack.length() > 0 {
    result.write_string("\ncall stack (most recent call last):\n")
    for i, entry in self.call_stack {
      result.write_string("  \{i}: ")
      result.write_string(entry.to_string())
      result.write_string("\n")
    }
  }
  result.to_string()
}

///|
pub impl Show for RuntimeErrorContext with fn output(self, logger) {
  logger.write_string(self.format())
}

///|
/// Format a basic RuntimeError without context
pub fn format_runtime_error(error : RuntimeError) -> String {
  RuntimeErrorContext::RuntimeErrorContext(error).format()
}