// core_wasm.mbt - WASM-GC target implementation
// Requires: use-js-builtin-string: true in moon.pkg.json

// ============================================
// WASM imports from "jscore" namespace
// ============================================

///|
extern "wasm" fn wasm_get(obj : Any, key : String) -> Any = "jscore" "get"

///|
extern "wasm" fn wasm_get_by_index(obj : Any, index : Int) -> Any = "jscore" "get_by_index"

///|
extern "wasm" fn wasm_set(obj : Any, key : String, value : Any) -> Unit = "jscore" "set"

///|
extern "wasm" fn wasm_set_by_index(obj : Any, index : Int, value : Any) -> Unit = "jscore" "set_by_index"

// Fixed-arity call functions (WASM can't use Array in extern)

///|
extern "wasm" fn wasm_call0(obj : Any, name : String) -> Any = "jscore" "call0"

///|
extern "wasm" fn wasm_call1(obj : Any, name : String, a1 : Any) -> Any = "jscore" "call1"

///|
extern "wasm" fn wasm_call2(
  obj : Any,
  name : String,
  a1 : Any,
  a2 : Any,
) -> Any = "jscore" "call2"

///|
extern "wasm" fn wasm_call3(
  obj : Any,
  name : String,
  a1 : Any,
  a2 : Any,
  a3 : Any,
) -> Any = "jscore" "call3"

///|
extern "wasm" fn wasm_call4(
  obj : Any,
  name : String,
  a1 : Any,
  a2 : Any,
  a3 : Any,
  a4 : Any,
) -> Any = "jscore" "call4"

// Fixed-arity invoke functions

///|
extern "wasm" fn wasm_invoke0(fn_ : Any) -> Any = "jscore" "invoke0"

///|
extern "wasm" fn wasm_invoke1(fn_ : Any, a1 : Any) -> Any = "jscore" "invoke1"

///|
extern "wasm" fn wasm_invoke2(fn_ : Any, a1 : Any, a2 : Any) -> Any = "jscore" "invoke2"

///|
extern "wasm" fn wasm_invoke3(fn_ : Any, a1 : Any, a2 : Any, a3 : Any) -> Any = "jscore" "invoke3"

///|
extern "wasm" fn wasm_invoke4(
  fn_ : Any,
  a1 : Any,
  a2 : Any,
  a3 : Any,
  a4 : Any,
) -> Any = "jscore" "invoke4"

// Fixed-arity new functions

///|
extern "wasm" fn wasm_new0(cls : Any) -> Any = "jscore" "new0"

///|
extern "wasm" fn wasm_new1(cls : Any, a1 : Any) -> Any = "jscore" "new1"

///|
extern "wasm" fn wasm_new2(cls : Any, a1 : Any, a2 : Any) -> Any = "jscore" "new2"

///|
extern "wasm" fn wasm_new3(cls : Any, a1 : Any, a2 : Any, a3 : Any) -> Any = "jscore" "new3"

///|
extern "wasm" fn wasm_new4(
  cls : Any,
  a1 : Any,
  a2 : Any,
  a3 : Any,
  a4 : Any,
) -> Any = "jscore" "new4"

// Utility imports

///|
extern "wasm" fn wasm_typeof(v : Any) -> String = "jscore" "typeof"

///|
extern "wasm" fn wasm_is_nullish(v : Any) -> Bool = "jscore" "is_nullish"

///|
extern "wasm" fn wasm_is_null(v : Any) -> Bool = "jscore" "is_null"

///|
extern "wasm" fn wasm_is_undefined(v : Any) -> Bool = "jscore" "is_undefined"

///|
extern "wasm" fn wasm_is_array(v : Any) -> Bool = "jscore" "is_array"

///|
extern "wasm" fn wasm_is_object(v : Any) -> Bool = "jscore" "is_object"

///|
extern "wasm" fn wasm_instanceof(v : Any, cls : Any) -> Bool = "jscore" "instanceof"

///|
extern "wasm" fn wasm_equal(a : Any, b : Any) -> Bool = "jscore" "equal"

///|
extern "wasm" fn wasm_to_string(v : Any) -> String = "jscore" "to_string"

///|
extern "wasm" fn wasm_global_this() -> Any = "jscore" "global_this"

///|
extern "wasm" fn wasm_undefined() -> Any = "jscore" "undefined"

///|
extern "wasm" fn wasm_null() -> Any = "jscore" "null"

///|
extern "wasm" fn wasm_new_object() -> Any = "jscore" "new_object"

///|
extern "wasm" fn wasm_new_array() -> Any = "jscore" "new_array"

// Array-returning functions: return Any, convert in MoonBit

///|
extern "wasm" fn wasm_object_keys_any(obj : Any) -> Any = "jscore" "object_keys"

///|
extern "wasm" fn wasm_object_values_any(obj : Any) -> Any = "jscore" "object_values"

///|
extern "wasm" fn wasm_array_from_any(v : Any) -> Any = "jscore" "array_from"

///|
extern "wasm" fn wasm_array_length(arr : Any) -> Int = "jscore" "array_length"

///|
extern "wasm" fn wasm_object_assign(target : Any, source : Any) -> Any = "jscore" "object_assign"

///|
extern "wasm" fn wasm_object_has_own(obj : Any, key : String) -> Bool = "jscore" "object_has_own"

///|
extern "wasm" fn wasm_json_stringify(value : Any) -> String = "jscore" "json_stringify"

///|
extern "wasm" fn wasm_json_stringify_pretty(value : Any, space : Int) -> String = "jscore" "json_stringify_pretty"

///|
extern "wasm" fn wasm_json_parse(text : String) -> Any = "jscore" "json_parse"

///|
extern "wasm" fn wasm_log(message : Any) -> Unit = "jscore" "log"

///|
extern "wasm" fn wasm_throw(value : Any) -> Unit = "jscore" "throw"

// ============================================
// Core operations (public API)
// ============================================

///|
/// Get property: obj[key]
#alias("_[_]")
pub fn Any::_get(self : Any, key : String) -> Any {
  wasm_get(self, key)
}

///|
/// Get by index: obj[index]
pub fn Any::_get_by_index(self : Any, key : Int) -> Any {
  wasm_get_by_index(self, key)
}

///|
/// Set property: obj[key] = value
#alias("_[_]=_")
pub fn Any::_set(self : Any, key : String, value : Any) -> Unit {
  wasm_set(self, key, value)
}

///|
/// Call method: obj._call("method", [arg1, arg2])
pub fn Any::_call(self : Any, key : String, args : Array[Any]) -> Any {
  match args.length() {
    0 => wasm_call0(self, key)
    1 => wasm_call1(self, key, args[0])
    2 => wasm_call2(self, key, args[0], args[1])
    3 => wasm_call3(self, key, args[0], args[1], args[2])
    4 => wasm_call4(self, key, args[0], args[1], args[2], args[3])
    _ => abort("_call: too many arguments (max 4)")
  }
}

///|
/// Call function: func._invoke([arg1, arg2])
pub fn Any::_invoke(self : Any, args : Array[Any]) -> Any {
  match args.length() {
    0 => wasm_invoke0(self)
    1 => wasm_invoke1(self, args[0])
    2 => wasm_invoke2(self, args[0], args[1])
    3 => wasm_invoke3(self, args[0], args[1], args[2])
    4 => wasm_invoke4(self, args[0], args[1], args[2], args[3])
    _ => abort("_invoke: too many arguments (max 4)")
  }
}

///|
/// JS: new cls(...args)
pub fn new(cls : Any, args : Array[Any]) -> Any {
  match args.length() {
    0 => wasm_new0(cls)
    1 => wasm_new1(cls, args[0])
    2 => wasm_new2(cls, args[0], args[1])
    3 => wasm_new3(cls, args[0], args[1], args[2])
    4 => wasm_new4(cls, args[0], args[1], args[2], args[3])
    _ => abort("new: too many arguments (max 4)")
  }
}

// ============================================
// Global values
// ============================================

///|
pub fn global_this() -> Any {
  wasm_global_this()
}

///|
pub fn undefined() -> Any {
  wasm_undefined()
}

///|
pub fn null() -> Any {
  wasm_null()
}

// ============================================
// Type checks
// ============================================

///|
pub fn is_object(v : Any) -> Bool {
  wasm_is_object(v)
}

///|
pub fn is_nullish(v : Any) -> Bool {
  wasm_is_nullish(v)
}

///|
pub fn is_null(v : Any) -> Bool {
  wasm_is_null(v)
}

///|
pub fn is_undefined(v : Any) -> Bool {
  wasm_is_undefined(v)
}

///|
pub fn is_array(v : Any) -> Bool {
  wasm_is_array(v)
}

///|
pub fn typeof_(v : Any) -> String {
  wasm_typeof(v)
}

///|
pub fn instanceof_(v : Any, cls : Any) -> Bool {
  wasm_instanceof(v, cls)
}

///|
pub fn equal(a : Any, b : Any) -> Bool {
  wasm_equal(a, b)
}

// ============================================
// Object operations
// ============================================

///|
pub fn new_object() -> Any {
  wasm_new_object()
}

///|
pub fn new_array() -> Any {
  wasm_new_array()
}

///|
/// Convert JS array (Any) to MoonBit Array[String]
fn js_array_to_strings(js_arr : Any) -> Array[String] {
  let len = wasm_array_length(js_arr)
  let result : Array[String] = []
  for i = 0; i < len; i = i + 1 {
    result.push(wasm_get_by_index(js_arr, i) |> Any::cast())
  }
  result
}

///|
/// Convert JS array (Any) to MoonBit Array[Any]
fn js_array_to_any_array(js_arr : Any) -> Array[Any] {
  let len = wasm_array_length(js_arr)
  let result : Array[Any] = []
  for i = 0; i < len; i = i + 1 {
    result.push(wasm_get_by_index(js_arr, i))
  }
  result
}

///|
/// Convert MoonBit Array[Any] to JS array (Any)
/// This iterates the array and pushes each element to a new JS array
pub fn array_to_js(arr : Array[Any]) -> Any {
  let js_arr = wasm_new_array()
  for i = 0; i < arr.length(); i = i + 1 {
    wasm_call1(js_arr, "push", arr[i]) |> ignore
  }
  js_arr
}

///|
/// Convert MoonBit Bytes to JS Uint8Array (Any)
/// Bytes are converted element by element using index access
pub fn bytes_to_js(bytes : Bytes) -> Any {
  let global = wasm_global_this()
  let uint8_class = wasm_get(global, "Uint8Array")
  let js_arr = wasm_new1(uint8_class, wasm_from_int(bytes.length()))
  for i = 0; i < bytes.length(); i = i + 1 {
    // Use index access: js_arr[i] = bytes[i]
    wasm_set_by_index(js_arr, i, wasm_from_int(bytes[i].to_int()))
  }
  js_arr
}

///|
pub fn object_keys(obj : Any) -> Array[String] {
  js_array_to_strings(wasm_object_keys_any(obj))
}

///|
pub fn object_values(obj : Any) -> Array[Any] {
  js_array_to_any_array(wasm_object_values_any(obj))
}

///|
pub fn array_from(v : Any) -> Array[Any] {
  js_array_to_any_array(wasm_array_from_any(v))
}

///|
pub fn object_assign(target : Any, source : Any) -> Any {
  wasm_object_assign(target, source)
}

///|
pub fn object_has_own(obj : Any, key : String) -> Bool {
  wasm_object_has_own(obj, key)
}

// ============================================
// JSON
// ============================================

///|
pub fn json_stringify(value : Any) -> String {
  wasm_json_stringify(value)
}

///|
pub fn json_stringify_pretty(value : Any, space : Int) -> String {
  wasm_json_stringify_pretty(value, space)
}

///|
pub fn json_parse(text : String) -> Any {
  wasm_json_parse(text)
}

// ============================================
// Utility
// ============================================

///|
pub fn Any::to_string(self : Any) -> String {
  wasm_to_string(self)
}

///|
pub fn log(message : Any) -> Unit {
  wasm_log(message)
}

///|
pub fn throw_(value : Any) -> Unit {
  wasm_throw(value)
}

// ============================================
// Type-specific from functions for ToAny trait
// These allow type-safe conversion from MoonBit primitives to externref
// ============================================

///|
pub extern "wasm" fn wasm_from_int(v : Int) -> Any = "jscore" "from_int"

///|
pub extern "wasm" fn wasm_from_uint(v : UInt) -> Any = "jscore" "from_uint"

///|
pub extern "wasm" fn wasm_from_int64(v : Int64) -> Any = "jscore" "from_int64"

///|
pub extern "wasm" fn wasm_from_uint64(v : UInt64) -> Any = "jscore" "from_uint64"

///|
pub extern "wasm" fn wasm_from_float(v : Float) -> Any = "jscore" "from_float"

///|
pub extern "wasm" fn wasm_from_double(v : Double) -> Any = "jscore" "from_double"

///|
pub extern "wasm" fn wasm_from_string(v : String) -> Any = "jscore" "from_string"

///|
pub extern "wasm" fn wasm_from_bool(v : Bool) -> Any = "jscore" "from_bool"

// BigInt conversion via Int64
// WASM i64 is automatically converted to/from JS BigInt

///|
/// Convert Int64 to JS BigInt (Any)
/// WASM i64 is natively compatible with JS BigInt
pub extern "wasm" fn int64_to_bigint(v : Int64) -> Any = "jscore" "int64_to_bigint"

///|
/// Convert UInt64 to JS BigInt (Any)
pub extern "wasm" fn uint64_to_bigint(v : UInt64) -> Any = "jscore" "int64_to_bigint"

// Note: Array[T] cannot be passed directly to WASM FFI due to generic type parameter
// Use array_to_js() for Array[Any] conversion