// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
#external
priv type XStringReadHandle

///|
#external
priv type XStringCreateHandle

///|
#external
priv type XExternString

///|
fn begin_create_string() -> XStringCreateHandle = "__moonbit_fs_unstable" "begin_create_string"

///|
fn string_append_char(handle : XStringCreateHandle, ch : Char) = "__moonbit_fs_unstable" "string_append_char"

///|
fn finish_create_string(handle : XStringCreateHandle) -> XExternString = "__moonbit_fs_unstable" "finish_create_string"

///|
fn string_to_extern(s : String) -> XExternString {
  let handle = begin_create_string()
  for code_unit in s.code_units() {
    string_append_char(handle, code_unit.unsafe_to_char())
  }
  finish_create_string(handle)
}

///|
fn begin_read_string(s : XExternString) -> XStringReadHandle = "__moonbit_fs_unstable" "begin_read_string"

///|
/// Read one char from the string, returns -1 if the end of the string is reached.
/// The number returned is the unicode codepoint of the character.
fn string_read_char(handle : XStringReadHandle) -> Int = "__moonbit_fs_unstable" "string_read_char"

///|
fn finish_read_string(handle : XStringReadHandle) = "__moonbit_fs_unstable" "finish_read_string"

///|
fn string_from_extern(e : XExternString) -> String {
  let buf = StringBuilder()
  let handle = begin_read_string(e)
  while true {
    let ch = string_read_char(handle)
    if ch == -1 {
      break
    } else {
      buf.write_char(ch.unsafe_to_char())
    }
  }
  finish_read_string(handle)
  buf.to_string()
}

///|
#external
priv type XStringArrayReadHandle

///|
#external
priv type XExternStringArray

///|
fn begin_read_string_array(sa : XExternStringArray) -> XStringArrayReadHandle = "__moonbit_fs_unstable" "begin_read_string_array"

///|
fn string_array_read_string(handle : XStringArrayReadHandle) -> XExternString = "__moonbit_fs_unstable" "string_array_read_string"

///|
fn finish_read_string_array(handle : XStringArrayReadHandle) = "__moonbit_fs_unstable" "finish_read_string_array"

///|
fn string_array_from_extern(e : XExternStringArray) -> Array[String] {
  let buf = []
  let handle = begin_read_string_array(e)
  while true {
    let extern_str = string_array_read_string(handle)
    let str = string_from_extern(extern_str)
    // keep "ffi_end_of_/string_array" same with moonrun
    if str == "ffi_end_of_/string_array" {
      break
    } else {
      buf.push(str)
    }
  }
  finish_read_string_array(handle)
  buf
}

///|
fn get_cli_args_internal() -> Array[String] {
  let args = get_cli_args_ffi()
  string_array_from_extern(args)
}

///|
fn get_cli_args_ffi() -> XExternStringArray = "__moonbit_fs_unstable" "args_get"

///|
fn now_internal() -> UInt64 = "__moonbit_time_unstable" "now"

///|
fn current_dir_internal() -> String? {
  let dir = current_dir_ffi()
  let dir = string_from_extern(dir)
  if dir == "" {
    None
  } else {
    Some(dir)
  }
}

///|
fn current_dir_ffi() -> XExternString = "__moonbit_fs_unstable" "current_dir"

///|
fn get_env_var_internal(key : String) -> String? {
  let key = string_to_extern(key)
  if get_env_var_exists_ffi(key) {
    Some(string_from_extern(get_env_var_ffi(key)))
  } else {
    None
  }
}

///|
fn get_env_var_ffi(key : XExternString) -> XExternString = "__moonbit_fs_unstable" "get_env_var"

///|
fn get_env_var_exists_ffi(key : XExternString) -> Bool = "__moonbit_fs_unstable" "get_env_var_exists"

///|
fn get_env_vars_internal() -> Map[String, String] {
  let tmp = string_array_from_extern(get_env_vars_ffi())
  let res = Map([])
  for i = 0; i < tmp.length(); i = i + 2 {
    res[tmp[i]] = tmp[i + 1]
  }
  res
}

///|
fn get_env_vars_ffi() -> XExternStringArray = "__moonbit_fs_unstable" "get_env_vars"

///|
fn set_env_var_internal(key : String, value : String) -> Unit {
  set_env_var_ffi(string_to_extern(key), string_to_extern(value))
}

///|
fn set_env_var_ffi(key : XExternString, value : XExternString) = "__moonbit_fs_unstable" "set_env_var"

///|
fn unset_env_var_internal(key : String) -> Unit {
  unset_env_var_ffi(string_to_extern(key))
}

///|
fn unset_env_var_ffi(key : XExternString) = "__moonbit_fs_unstable" "unset_env_var"

///|
#cfg(target="wasm")
fn rand_internal(to_be_filled : FixedArray[Byte]) -> Bool {
  // WASI is part of the linear Wasm runtime contract, so entropy availability
  // is unconditional for this target.
  ignore(wasi_random_get(to_be_filled, to_be_filled.length()))
  true
}

///|
/// WASI treats the fixed array as a linear-memory address.
#cfg(target="wasm")
#unsafe_skip_stub_check
#borrow(to_be_filled)
fn wasi_random_get(to_be_filled : FixedArray[Byte], length : Int) -> Int = "wasi_snapshot_preview1" "random_get"

///|
#cfg(target="wasm-gc")
fn rand_internal(_to_be_filled : FixedArray[Byte]) -> Bool {
  false
}