///|
pub struct NapiBufferView {
  buf : UnmanagedBuffer
  len : Int
  mut is_alive : Bool
}

///|
pub fn NapiBufferView::length(self : NapiBufferView) -> Int {
  if !self.is_alive {
    abort("Buffer view is no longer valid") // This should be a panic or error, but we'll use panic for now, generator wrapper handles typed error if possible.
  }
  self.len
}

///|
pub fn NapiBufferView::load8(self : NapiBufferView, offset : Int) -> Int {
  if !self.is_alive {
    abort("Buffer view is no longer valid")
  }
  if offset < 0 || offset >= self.len {
    abort("Buffer view out of bounds")
  }
  self.buf.load8(offset)
}

///|
pub fn NapiBufferView::store8(
  self : NapiBufferView,
  offset : Int,
  val : Int,
) -> Unit {
  if !self.is_alive {
    abort("Buffer view is no longer valid")
  }
  if offset < 0 || offset >= self.len {
    abort("Buffer view out of bounds")
  }
  self.buf.store8(offset, val)
}

///|
/// Invalidates a borrowed Buffer view after the callback returns.
pub fn NapiBufferView::invalidate(self : NapiBufferView) -> Unit {
  self.is_alive = false
}

///|
fn result_from_status(env : NapiEnv, status : Int, msg : Bytes) -> Unit {
  if status != 0 {
    let _ = napi_throw_error(env, b"", msg)
    ()
  }
}

// 供生成的包装函数调用的转换器

///|
pub fn get_Int(env : NapiEnv, val : NapiValue) -> Int {
  let res = Ref(0)
  let status = napi_get_value_int32(env, val, res)
  result_from_status(env, status, b"Invalid argument, expected Int")
  res.val
}

///|
pub fn set_Int(env : NapiEnv, val : Int) -> NapiValue {
  let res = Ref(moonbit_dummy_napi_value())
  let status = napi_create_int32(env, val, res)
  result_from_status(env, status, b"Failed to create Int")
  res.val
}

///|
pub fn get_Double(env : NapiEnv, val : NapiValue) -> Double {
  let res = Ref(0.0)
  let status = napi_get_value_double(env, val, res)
  result_from_status(env, status, b"Invalid argument, expected Double")
  res.val
}

///|
pub fn set_Double(env : NapiEnv, val : Double) -> NapiValue {
  let res = Ref(moonbit_dummy_napi_value())
  let status = napi_create_double(env, val, res)
  result_from_status(env, status, b"Failed to create Double")
  res.val
}

///|
pub fn get_Bool(env : NapiEnv, val : NapiValue) -> Bool {
  let res = Ref(false)
  let status = napi_get_value_bool(env, val, res)
  result_from_status(env, status, b"Invalid argument, expected Bool")
  res.val
}

///|
pub fn set_Bool(env : NapiEnv, val : Bool) -> NapiValue {
  let res = Ref(moonbit_dummy_napi_value())
  let status = napi_get_boolean(env, val, res)
  result_from_status(env, status, b"Failed to create Bool")
  res.val
}

///|
pub fn get_String(env : NapiEnv, val : NapiValue) -> String {
  let len = Ref(0)
  let status = napi_get_value_string_utf8_length(
    env,
    val,
    moonbit_dummy_unmanaged_buffer(),
    0,
    len,
  )
  result_from_status(env, status, b"Failed to get string length")

  let buf = Bytes::make(len.val + 1, b'\x00')
  let status2 = napi_get_value_string_utf8(env, val, buf, len.val + 1, len)
  result_from_status(env, status2, b"Failed to read string")

  let arr = Array::make(len.val, b'\x00')
  for i = 0; i < len.val; i = i + 1 {
    arr[i] = buf[i]
  }

  // Use utf8.decode for proper string construction
  let res = @utf8.decode(Bytes::from_array(arr)) catch {
    _ => {
      let _ = napi_throw_error(env, b"", b"Failed to decode UTF-8 string")
      ""
    }
  }
  res
}

///|
pub fn set_String(env : NapiEnv, val : String) -> NapiValue {
  let res_bytes = @utf8.encode(val)
  let res = Ref(moonbit_dummy_napi_value())
  let status = napi_create_string_utf8(env, res_bytes, res_bytes.length(), res)
  result_from_status(env, status, b"Failed to create String")
  res.val
}

///|
pub fn get_Bytes(env : NapiEnv, val : NapiValue) -> Bytes {
  let len = Ref(0)
  let unmanaged = Ref(moonbit_dummy_unmanaged_buffer())
  let status = napi_get_buffer_info(env, val, unmanaged, len)
  result_from_status(env, status, b"Failed to get buffer")

  let arr = Array::make(len.val, b'\x00')
  for i = 0; i < len.val; i = i + 1 {
    arr[i] = unmanaged.val.load8(i).to_byte()
  }
  Bytes::from_array(arr)
}

///|
pub fn set_Bytes(env : NapiEnv, val : Bytes) -> NapiValue {
  // Currently MoonBit doesn't expose malloc natively to FFI.
  // Actually we need to pass a C-allocated buffer if we want external buffer.
  // For now we'll just create a string if we can't allocate.
  // wait, Node N-API provides `napi_create_buffer_copy` which allocates!
  // Let's add napi_create_buffer_copy to bindings.
  let res = Ref(moonbit_dummy_napi_value())
  let data = Ref(moonbit_dummy_unmanaged_buffer())
  let status = napi_create_buffer_copy(env, val.length(), val, data, res)
  result_from_status(env, status, b"Failed to create buffer")
  res.val
}

///|
pub fn get_NapiBufferView(env : NapiEnv, val : NapiValue) -> NapiBufferView {
  let len = Ref(0)
  let unmanaged = Ref(moonbit_dummy_unmanaged_buffer())
  let status = napi_get_buffer_info(env, val, unmanaged, len)
  result_from_status(env, status, b"Failed to get buffer")
  { buf: unmanaged.val, len: len.val, is_alive: true, }
}

///|
pub fn set_Unit(env : NapiEnv, _val : Unit) -> NapiValue {
  let res = Ref(moonbit_dummy_napi_value())
  let status = napi_get_undefined(env, res)
  result_from_status(env, status, b"Failed to create undefined")
  res.val
}

///|
#export_name("add")
pub fn add(a : Int, b : Int) -> Int {
  a + b
}

///|
#export_name("concat")
pub fn concat(a : String, b : String) -> String {
  a + b
}

///|
#export_name("mutate_buffer")
pub fn mutate_buffer(view : NapiBufferView) -> Unit {
  if view.length() > 0 {
    let old = view.load8(0)
    view.store8(0, old + 1)
  }
}

///|
#export_name("roundtrip_bytes")
pub fn roundtrip_bytes(b : Bytes) -> Bytes {
  b
}

///|
#export_name("check_bool")
pub fn check_bool(b : Bool) -> Bool {
  !b
}

///|
#export_name("check_double")
pub fn check_double(d : Double) -> Double {
  d * 2.0
}