// Copyright 2018-2026 the Deno authors. MIT license.

import { Buffer } from "node:buffer";
import { assert, assertEquals, loadTestLibrary } from "./common.js";

const objectWrap = loadTestLibrary();

Deno.test("napi object wrap new", function () {
  const obj = new objectWrap.NapiObject(0);
  assertEquals(obj.get_value(), 0);
  obj.set_value(10);
  assertEquals(obj.get_value(), 10);
  obj.increment();
  assertEquals(obj.get_value(), 11);
  obj.increment();
  obj.set_value(10);
  assertEquals(obj.get_value(), 10);
  assertEquals(objectWrap.NapiObject.factory(), 64);
});

Deno.test("napi bind finalizer", function () {
  const obj = {};
  objectWrap.test_bind_finalizer(obj);
});

Deno.test("napi external finalizer", function () {
  let obj = objectWrap.test_external_finalizer();
  assert(obj);
  obj = null;
});

Deno.test("napi external buffer", function () {
  let buf = objectWrap.test_external_buffer();
  assertEquals(buf, Buffer.from([1, 2, 3]));
  buf = null;
});

Deno.test("napi external arraybuffer", function () {
  let buf = objectWrap.test_external_arraybuffer();
  assertEquals(new Uint8Array(buf), new Uint8Array([1, 2, 3]));
  buf = null;
});

Deno.test("napi class accessor property is writable", function () {
  // Accessor properties (getter/setter) defined via napi_define_class
  // should be writable via the setter even when napi_writable is not
  // set in the attributes. napi_writable only applies to data properties.
  const obj = new objectWrap.NapiAccessorObject();
  assertEquals(obj.value, 0);
  obj.value = 42;
  assertEquals(obj.value, 42);
});

Deno.test("napi object wrap userland owned", function () {
  let obj = new objectWrap.NapiObjectOwned(1);
  assertEquals(obj.get_value(), 1);
  obj = null;
  // force finalize callback to get called
  globalThis.gc();
});

Deno.test("napi remove_wrap", function () {
  const obj = {};
  const result = objectWrap.test_remove_wrap(obj);
  assertEquals(result, true);
});

// Regression test for #33924: `napi_new_instance` must produce a
// properly wrapped instance.
Deno.test("napi new_instance via napi_new_instance", function () {
  const obj = objectWrap.test_call_new_instance(objectWrap.NapiObject, 7);
  assert(obj instanceof objectWrap.NapiObject);
  assertEquals(obj.get_value(), 7);
  obj.increment();
  assertEquals(obj.get_value(), 8);
});

Deno.test("napi new_instance allows JS in native constructor", function () {
  const obj = objectWrap.test_new_instance_constructor_can_call_js();
  assert(Object.isSealed(obj));
});

// Regression test for #33924: napi_resolve_deferred must not leave the isolate
// in V8's Auto microtask policy. The runtime uses the Explicit policy, so a
// microtask queued while resolving a deferred must NOT auto-drain mid-way
// through a subsequent napi_new_instance call running at call-depth-zero on the
// event loop. Pre-fix the isolate was flipped to Auto, the microtask drained
// during construction, and napi-rs's factory wrapping leaked across classes.
Deno.test("napi_resolve_deferred preserves Explicit microtask policy", async () => {
  const drainedDuringConstruction = await new Promise((resolve) => {
    objectWrap.test_resolve_deferred_keeps_microtask_policy(
      objectWrap.NapiObject,
      (drained) => resolve(drained),
    );
  });
  assertEquals(drainedDuringConstruction, false);
});
