// core_js.mbt - JS target implementation

// ============================================
// Core operations
// ============================================

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

///|
/// Get by index: obj[index]
pub extern "js" fn Any::_get_by_index(self : Any, key : Int) -> Any =
  #| (obj, key) => obj[key]

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

///|
/// Call method: obj._call("method", [arg1, arg2])
pub extern "js" fn Any::_call(
  self : Any,
  key : String,
  args : Array[Any],
) -> Any =
  #| (obj, key, args) => obj[key](...args)

///|
/// Call function: func._invoke([arg1, arg2])
pub extern "js" fn Any::_invoke(self : Any, args : Array[Any]) -> Any =
  #| (func, args) => func(...args)

///|
/// JS: new cls(...args)
pub extern "js" fn new(cls : Any, args : Array[Any]) -> Any =
  #|(cls, args) => new cls(...args)

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

///|
pub extern "js" fn global_this() -> Any =
  #| () => globalThis

///|
pub extern "js" fn undefined() -> Any =
  #| () => undefined

///|
pub extern "js" fn null() -> Any =
  #| () => null

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

///|
pub extern "js" fn is_object(v : Any) -> Bool =
  #| (v) => typeof v === "object" && v !== null

///|
pub extern "js" fn is_nullish(v : Any) -> Bool =
  #|(v) => v == null

///|
pub extern "js" fn is_null(v : Any) -> Bool =
  #|(v) => v === null

///|
pub extern "js" fn is_undefined(v : Any) -> Bool =
  #|(v) => v === undefined

///|
pub extern "js" fn is_array(v : Any) -> Bool =
  #|(v) => Array.isArray(v)

///|
pub extern "js" fn typeof_(v : Any) -> String =
  #|(v) => typeof v

///|
pub extern "js" fn instanceof_(v : Any, cls : Any) -> Bool =
  #|(v, cls) => v instanceof cls

///|
pub extern "js" fn equal(a : Any, b : Any) -> Bool =
  #|(a, b) => a === b

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

///|
pub extern "js" fn new_object() -> Any =
  #| () => ({})

///|
pub extern "js" fn new_array() -> Any =
  #| () => []

///|
pub extern "js" fn object_keys(obj : Any) -> Array[String] =
  #| (obj) => Object.keys(obj)

///|
pub extern "js" fn object_values(obj : Any) -> Array[Any] =
  #| (obj) => Object.values(obj)

///|
pub extern "js" fn object_assign(target : Any, source : Any) -> Any =
  #| (target, source) => Object.assign(target, source)

///|
pub extern "js" fn object_has_own(obj : Any, key : String) -> Bool =
  #| (obj, key) => Object.hasOwn(obj, key)

///|
pub extern "js" fn from_entries(entries : Array[(String, Any)]) -> Any =
  #| (entries) => Object.fromEntries(entries.map(e => [e._0, e._1]))

///|
pub extern "js" fn array_from(v : Any) -> Array[Any] =
  #| (v) => Array.from(v)

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

///|
pub extern "js" fn json_stringify(value : Any) -> String =
  #| (value) => JSON.stringify(value)

///|
pub extern "js" fn json_stringify_pretty(value : Any, space : Int) -> String =
  #| (value, space) => JSON.stringify(value, null, space)

///|
pub extern "js" fn json_parse(text : String) -> Any =
  #| (text) => JSON.parse(text)

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

///|
pub extern "js" fn Any::to_string(self : Any) -> String =
  #| (self) => self == null ? String(self) : self.toString()

///|
pub extern "js" fn as_any(opt : Any?) -> Any =
  #| (opt) => opt?._0

///|
pub extern "js" fn log(message : Any) -> Unit =
  #| (message) => { console.log(message); }

///|
pub extern "js" fn throw_(value : Any) -> Unit =
  #| (value) => { throw value }