///|
/// | A type representing an obscure JavaScript value.
/// | Its internal structure is unknown and should be inspected before use.
#external
pub type JsObscure

///|
/// | Converts any Moonbit value to `JsObscure`.
/// | This is a low-level operation and should be used with caution.
pub fn[T] v_to_js_obscure(v : T) -> JsObscure = "%identity"

///|
/// | Converts `JsObscure` to any Moonbit value.
/// | This is a low-level operation and should be used with caution.
pub fn[T] js_obscure_to_v(v : JsObscure) -> T = "%identity"

///|
/// | Returns the lowercased type string of a JavaScript value.
/// | It uses `Object.prototype.toString.call` internally.
/// | For example, it returns "string", "object", "function".
pub extern "js" fn type_of(v : JsObscure) -> String =
  #| (v) => Object.prototype.toString.call(v).slice(8, -1).toLowerCase()

///|
/// | Checks if the value is `null`.
pub extern "js" fn JsObscure::is_nil(self : JsObscure) -> Bool =
  #| (self) => self === null

///|
/// | Checks if the value is either JavaScript `null` or `undefined`.
pub extern "js" fn JsObscure::is_nullish(self : JsObscure) -> Bool =
  #| (self) => self == null

///|
/// | Checks if the value is `undefined`.
pub extern "js" fn JsObscure::is_undefined(self : JsObscure) -> Bool =
  #| (self) => self === undefined

///|
/// | Checks if the value is a function.
pub extern "js" fn JsObscure::is_function(self : JsObscure) -> Bool =
  #| (self) => typeof self === 'function'

///|
/// | Checks if the value is a non-null JavaScript object.
pub extern "js" fn JsObscure::is_object(self : JsObscure) -> Bool =
  #| (self) => self !== null && typeof self === 'object'

///|
/// | Checks if the value is a string.
pub extern "js" fn JsObscure::is_string(self : JsObscure) -> Bool =
  #| (self) => typeof self === 'string'

///|
/// | Checks if the value is a number.
pub extern "js" fn JsObscure::is_number(self : JsObscure) -> Bool =
  #| (self) => typeof self === 'number'

///|
/// | Checks if the value is a boolean.
pub extern "js" fn JsObscure::is_boolean(self : JsObscure) -> Bool =
  #| (self) => typeof self === 'boolean'

///|
/// | Checks if the value is a symbol.
pub extern "js" fn JsObscure::is_symbol(self : JsObscure) -> Bool =
  #| (self) => typeof self === 'symbol'

///|
/// | Checks if the value is an array.
pub extern "js" fn JsObscure::is_array(self : JsObscure) -> Bool =
  #| (self) => Array.isArray(self)

///|
/// | Checks if the value is a Date object.
pub extern "js" fn JsObscure::is_date(self : JsObscure) -> Bool =
  #| (self) => self instanceof Date

///|
/// | Checks if the value is a RegExp object.
pub extern "js" fn JsObscure::is_regexp(self : JsObscure) -> Bool =
  #| (self) => self instanceof RegExp

///|
/// | Checks if the value is an Error object.
pub extern "js" fn JsObscure::is_error(self : JsObscure) -> Bool =
  #| (self) => self instanceof Error

///|
/// | Checks if the value is a WeakMap object.
pub extern "js" fn JsObscure::is_weakmap(self : JsObscure) -> Bool =
  #| (self) => self instanceof WeakMap

///|
/// | Checks if the value is a WeakSet object.
pub extern "js" fn JsObscure::is_weakset(self : JsObscure) -> Bool =
  #| (self) => self instanceof WeakSet

///|
/// | Checks if the value is a Map object.
pub extern "js" fn JsObscure::is_map(self : JsObscure) -> Bool =
  #| (self) => self instanceof Map

///|
/// | Checks if the value is a Set object.
pub extern "js" fn JsObscure::is_set(self : JsObscure) -> Bool =
  #| (self) => self instanceof Set

///|
/// | Checks if the value is a TypedArray.
pub extern "js" fn JsObscure::is_typed_array(self : JsObscure) -> Bool =
  #| (self) =>
  #|   ArrayBuffer.isView(self) &&
  #|   Object.prototype.toString.call(self) !== "[object DataView]"

///|
/// | Checks if the value is an `arguments` object.
pub extern "js" fn JsObscure::is_arguments(self : JsObscure) -> Bool =
  #| (self) => Object.prototype.toString.call(self) === '[object Arguments]'

// get and set object

///|
/// | Gets a property from a JavaScript object.
pub extern "js" fn JsObscure::get(self : JsObscure, prop : String) -> JsObscure =
  #| (self, prop) => self[prop]

///|
/// | Sets a property on a JavaScript object.
pub extern "js" fn JsObscure::set(
  self : JsObscure,
  prop : String,
  value : JsObscure,
) -> Unit =
  #| (self, prop, value) => self[prop] = value

///|
/// | Converts a `String` to `JsObscure`.
pub fn JsObscure::from_string(s : String) -> JsObscure = "%identity"

///|
/// | Converts an `Int` to `JsObscure`.
pub fn JsObscure::from_number(n : Int) -> JsObscure = "%identity"

///|
/// | Converts a `Float` to `JsObscure`.
pub fn JsObscure::from_float(n : Float) -> JsObscure = "%identity"

///|
/// | Converts a `Bool` to `JsObscure`.
pub fn JsObscure::from_bool(b : Bool) -> JsObscure = "%identity"

///|
/// | Converts an `Array[JsObscure]` to `JsObscure`.
pub fn JsObscure::from_array(a : Array[JsObscure]) -> JsObscure = "%identity"

// JsObject

///|
/// | A type representing an obscure JavaScript object.
#external
pub type JsObjectObscure

///|
/// | Creates a new empty JavaScript object.
pub extern "js" fn JsObjectObscure::new() -> JsObjectObscure =
  #| () => ({})

///|
/// | Returns an array of a given object's own enumerable property names.
extern "js" fn JsObjectObscure::js_keys(
  self : JsObjectObscure,
) -> FixedArray[JsObscure] =
  #| (self) => Object.keys(self)

///|
/// | Returns the object's own enumerable property names as a MoonBit array.
pub fn JsObjectObscure::keys(self : JsObjectObscure) -> Array[JsObscure] {
  Array::from_fixed_array(self.js_keys())
}

///|
/// | Returns an array of a given object's own enumerable property values.
extern "js" fn JsObjectObscure::js_values(
  self : JsObjectObscure,
) -> FixedArray[JsObscure] =
  #| (self) => Object.values(self)

///|
/// | Returns the object's own enumerable property values as a MoonBit array.
pub fn JsObjectObscure::values(self : JsObjectObscure) -> Array[JsObscure] {
  Array::from_fixed_array(self.js_values())
}

///|
/// | Returns an array of a given object's own enumerable string-keyed property `[key, value]` pairs.
extern "js" fn JsObjectObscure::js_entries(
  self : JsObjectObscure,
) -> FixedArray[JsObscure] =
  #| (self) => Object.entries(self)

///|
/// | Returns enumerable `[key, value]` entries as a MoonBit array.
pub fn JsObjectObscure::entries(self : JsObjectObscure) -> Array[JsObscure] {
  Array::from_fixed_array(self.js_entries())
}

// get/set

///|
/// | Gets a property from a JavaScript object.
pub extern "js" fn JsObjectObscure::get(
  self : JsObjectObscure,
  prop : String,
) -> JsObscure =
  #| (self, prop) => self[prop]

///|
/// | Sets a property on a JavaScript object.
pub extern "js" fn JsObjectObscure::set(
  self : JsObjectObscure,
  prop : String,
  value : JsObscure,
) -> Unit =
  #| (self, prop, value) => self[prop] = value

///|
/// | Checks if a property is in an object.
pub extern "js" fn JsObjectObscure::has(
  self : JsObjectObscure,
  prop : String,
) -> Bool =
  #| (self, prop) => prop in self

///|
/// | Deletes a property from an object.
pub extern "js" fn JsObjectObscure::delete(
  self : JsObjectObscure,
  prop : String,
) -> Bool =
  #| (self, prop) => delete self[prop]

///|
/// | Returns the number of enumerable properties in an object.
pub extern "js" fn JsObjectObscure::size(self : JsObjectObscure) -> Int =
  #| (self) => Object.keys(self).length

///|
/// | Converts `JsObjectObscure` to `JsObscure`.
pub fn JsObjectObscure::to_js_obscure(self : JsObjectObscure) -> JsObscure = "%identity"

///|
/// Call method with arguments
extern "js" fn JsObjectObscure::js_call_method_args(
  self : Self,
  method_ : String,
  args : FixedArray[JsObscure],
) -> JsObscure =
  #| (self, method_, args) => self[method_](...args)

///|
/// Calls a named JavaScript method with MoonBit array arguments.
pub fn JsObjectObscure::call_method_args(
  self : Self,
  method_ : String,
  args : Array[JsObscure],
) -> JsObscure {
  self.js_call_method_args(method_, FixedArray::from_array(args[:]))
}

///|
/// | Evaluates JavaScript code and returns the result.
pub extern "js" fn eval_js_code(code : String) -> JsObscure =
  #| (code) => eval(code)

///|
/// | Creates a JavaScript `null` value.
pub extern "js" fn JsObscure::null() -> JsObscure =
  #| () => null

///|
/// | Creates a JavaScript `undefined` value.
pub extern "js" fn JsObscure::undefined() -> JsObscure =
  #| () => undefined

///|
#external
pub type JsFnObscure

///|
pub fn JsFnObscure::to_obscure(self : JsFnObscure) -> JsObscure = "%identity"

///|
/// Perform detection and conversion
pub fn[T] JsFnObscure::from(t : T) -> JsFnObscure {
  let js_t = v_to_js_obscure(t)
  if !js_t.is_function() {
    js_obscure_to_v(js_t)
    warn_log("JsFnObscure::from: not a function")
  }
  js_obscure_to_v(js_t)
}