import test from "ava";
import { BridgeIDBRequest } from "../bridge-idb.js";
import {
  indexeddb_test,
  initTestIndexedDB,
  is_transaction_active,
  keep_alive,
} from "./wptsupport.js";

test.before("test DB initialization", initTestIndexedDB);

test("WPT test event-dispatch-active-flag.html (subtest 1)", async (t) => {
  // Transactions are active during success handlers
  await indexeddb_test(
    t,
    (done, db, tx) => {
      db.createObjectStore("store");
    },
    (done, db) => {
      const tx = db.transaction("store");
      const release_tx = keep_alive(t, tx, "store");

      t.assert(
        is_transaction_active(t, tx, "store"),
        "Transaction should be active after creation",
      );

      const request = tx.objectStore("store").get(4242);
      (request as BridgeIDBRequest)._debugName = "req-main";
      request.onerror = () => t.fail("request should succeed");
      request.onsuccess = () => {
        t.true(
          is_transaction_active(t, tx, "store"),
          "Transaction should be active during success handler",
        );

        let saw_handler_promise = false;
        Promise.resolve().then(() => {
          saw_handler_promise = true;
          t.true(
            is_transaction_active(t, tx, "store"),
            "Transaction should be active in handler's microtasks",
          );
        });

        setTimeout(() => {
          t.true(saw_handler_promise);
          t.false(
            is_transaction_active(t, tx, "store"),
            "Transaction should be inactive in next task",
          );
          release_tx();
          done();
        }, 0);
      };
    },
  );
});

test("WPT test event-dispatch-active-flag.html (subtest 2)", async (t) => {
  // Transactions are active during success listeners
  await indexeddb_test(
    t,
    (done, db, tx) => {
      db.createObjectStore("store");
    },
    (done, db) => {
      const tx = db.transaction("store");
      const release_tx = keep_alive(t, tx, "store");
      t.true(
        is_transaction_active(t, tx, "store"),
        "Transaction should be active after creation",
      );

      const request = tx.objectStore("store").get(0);
      request.onerror = () => t.fail("request should succeed");
      request.addEventListener("success", () => {
        t.true(
          is_transaction_active(t, tx, "store"),
          "Transaction should be active during success listener",
        );

        let saw_listener_promise = false;
        Promise.resolve().then(() => {
          saw_listener_promise = true;
          t.true(
            is_transaction_active(t, tx, "store"),
            "Transaction should be active in listener's microtasks",
          );
        });

        setTimeout(() => {
          t.true(saw_listener_promise);
          t.false(
            is_transaction_active(t, tx, "store"),
            "Transaction should be inactive in next task",
          );
          release_tx();
          done();
        }, 0);
      });
    },
  );
});

test("WPT test event-dispatch-active-flag.html (subtest 3)", async (t) => {
  // Transactions are active during error handlers
  await indexeddb_test(
    t,
    (done, db, tx) => {
      db.createObjectStore("store");
    },
    (done, db) => {
      const tx = db.transaction("store", "readwrite");
      const release_tx = keep_alive(t, tx, "store");
      t.true(
        is_transaction_active(t, tx, "store"),
        "Transaction should be active after creation",
      );

      tx.objectStore("store").put(0, 0);
      const request = tx.objectStore("store").add(0, 0);
      request.onsuccess = () => t.fail("request should fail");
      request.onerror = (e: any) => {
        e.preventDefault();

        t.true(
          is_transaction_active(t, tx, "store"),
          "Transaction should be active during error handler",
        );

        let saw_handler_promise = false;
        Promise.resolve().then(() => {
          saw_handler_promise = true;
          t.true(
            is_transaction_active(t, tx, "store"),
            "Transaction should be active in handler's microtasks",
          );
        });

        setTimeout(() => {
          t.true(saw_handler_promise);
          t.false(
            is_transaction_active(t, tx, "store"),
            "Transaction should be inactive in next task",
          );
          release_tx();
          done();
        }, 0);
      };
    },
  );
});

test("WPT test event-dispatch-active-flag.html (subtest 4)", async (t) => {
  // Transactions are active during error listeners
  await indexeddb_test(
    t,
    (done, db, tx) => {
      db.createObjectStore("store");
    },
    (done, db) => {
      const tx = db.transaction("store", "readwrite");
      const release_tx = keep_alive(t, tx, "store");
      t.true(
        is_transaction_active(t, tx, "store"),
        "Transaction should be active after creation",
      );

      tx.objectStore("store").put(0, 0);
      const request = tx.objectStore("store").add(0, 0);
      request.onsuccess = () => t.fail("request should fail");
      request.addEventListener("error", (e) => {
        e.preventDefault();

        t.true(
          is_transaction_active(t, tx, "store"),
          "Transaction should be active during error listener",
        );

        let saw_listener_promise = false;
        Promise.resolve().then(() => {
          saw_listener_promise = true;
          t.true(
            is_transaction_active(t, tx, "store"),
            "Transaction should be active in listener's microtasks",
          );
        });

        setTimeout(() => {
          t.true(saw_listener_promise);
          t.false(
            is_transaction_active(t, tx, "store"),
            "Transaction should be inactive in next task",
          );
          release_tx();
          done();
        }, 0);
      });
    },
  );
});
