///|
#external
priv type Handler

///|
#borrow(path, mode)
extern "C" fn fopen_ffi(path : Bytes, mode : Bytes) -> Handler = "moonbitlang_x_fs_fopen_ffi"

///|
extern "C" fn is_null(ptr : Handler) -> Int = "moonbitlang_x_fs_is_null"

///|
#borrow(ptr)
extern "C" fn fwrite_ffi(
  ptr : Bytes,
  size : Int,
  nitems : Int,
  stream : Handler,
) -> Int = "moonbitlang_x_fs_fwrite_ffi"

///|
extern "C" fn fflush_ffi(stream : Handler) -> Int = "moonbitlang_x_fs_fflush_ffi"

///|
extern "C" fn fclose_ffi(file : Handler) -> Int = "moonbitlang_x_fs_fclose_ffi"

///|
extern "C" fn get_error_message_ffi() -> Bytes = "moonbitlang_x_fs_get_error_message"

///|
fn get_error_message() -> String {
  bytes_to_string(get_error_message_ffi())
}

///|
fn string_to_null_terminated_bytes(s : String) -> Bytes {
  let len = s.length()
  let arr = Array::make(len + 1, (0).to_byte())
  for i = 0; i < len; i = i + 1 {
    arr[i] = s[i].to_int().to_byte()
  }
  arr[len] = (0).to_byte()
  Bytes::from_array(arr)
}

///|
/// Appends a Bytes payload to the file at path for Native target.
pub fn append_bytes_to_file(
  path : String,
  content : Bytes,
) -> Unit raise MoonKVError {
  let file = fopen_ffi(string_to_null_terminated_bytes(path), b"ab\x00")
  if is_null(file) != 0 {
    raise MoonKVError::IOError(
      "Failed to open file \{path} in append mode: " + get_error_message(),
    )
  }
  let bytes_written = fwrite_ffi(content, 1, content.length(), file)
  if bytes_written != content.length() {
    let err = get_error_message()
    let _ = fclose_ffi(file)
    raise MoonKVError::IOError("Failed to write to file \{path}: " + err)
  }
  if fflush_ffi(file) != 0 {
    let err = get_error_message()
    let _ = fclose_ffi(file)
    raise MoonKVError::IOError("Failed to flush file \{path}: " + err)
  }
  if fclose_ffi(file) != 0 {
    raise MoonKVError::IOError(
      "Failed to close file \{path}: " + get_error_message(),
    )
  }
}