///|
/// Returns the native platform code for the current process.
extern "C" fn platform_code_ffi() -> Int = "mb_replace_self_platform_code"

///|
/// Returns the current executable path in the native platform encoding, or an empty value.
extern "C" fn current_executable_path_ffi() -> Bytes = "mb_replace_self_current_executable_path"

///|
/// Replaces the current executable with `replacement_path`.
#borrow(replacement_path)
extern "C" fn replace_self_ffi(replacement_path : Bytes) -> Int = "mb_replace_self_replace_self"

///|
/// Deletes the current executable.
extern "C" fn delete_self_ffi() -> Int = "mb_replace_self_delete_self"

///|
/// Returns the last native error code observed by the C shim.
extern "C" fn last_error_code_ffi() -> Int = "mb_replace_self_last_error_code"

///|
/// Returns the last native error message as UTF-8 bytes.
extern "C" fn last_error_message_ffi() -> Bytes = "mb_replace_self_last_error_message"

///|
/// Decodes UTF-8 bytes into a MoonBit string with lossy fallback.
fn decode_utf8(bytes : Bytes) -> String {
  @utf8.decode_lossy(bytes[:])
}

///|
/// Decodes native text returned by the platform-specific shim.
fn decode_native_text(platform : Platform, bytes : Bytes) -> String {
  if bytes.length() == 0 {
    return ""
  }

  match platform {
    Windows => @ffi.from_wstr_lossy(bytes)
    MacOS | Linux | Unsupported => decode_utf8(bytes)
  }
}

///|
/// Decodes optional text returned by the native layer.
fn decode_optional_text(platform : Platform, bytes : Bytes) -> String? {
  let value = decode_native_text(platform, bytes)
  if value.is_empty() {
    None
  } else {
    Some(value)
  }
}

///|
/// Decodes a native error message and falls back to `default_message`.
fn decode_error_message(bytes : Bytes, default_message : String) -> String {
  let message = decode_utf8(bytes)
  if message.is_empty() {
    default_message
  } else {
    message
  }
}

///|
/// Returns the last native error message or `default_message`.
fn last_error_message_or(default_message : String) -> String {
  decode_error_message(last_error_message_ffi(), default_message)
}

///|
/// Converts the last native failure into `ReplaceSelfError`.
fn native_failure(
  action : String,
  fallback_message : String,
) -> ReplaceSelfError {
  ignore(last_error_code_ffi())
  NativeFailure(action~, message=last_error_message_or(fallback_message))
}

///|
/// Converts a native "0 means success" status code into `Result`.
fn unit_result_from_status(
  status : Int,
  action : String,
  fallback_message : String,
) -> Result[Unit, ReplaceSelfError] {
  if status == 0 {
    Ok(())
  } else {
    Err(native_failure(action, fallback_message))
  }
}

///|
/// Returns the current executable path or a typed discovery error.
fn current_executable_path_result() -> Result[String, ReplaceSelfError] {
  let platform = current_platform()
  let current_path = decode_optional_text(
    platform,
    current_executable_path_ffi(),
  )
  match current_path {
    Some(path) => Ok(path)
    None => Err(ExecutablePathUnavailable)
  }
}