import test from "ava";
import { BridgeIDBCursor, BridgeIDBKeyRange } from "../bridge-idb.js";
import {
  createDatabase,
  createdb,
  initTestIndexedDB,
  promiseForRequest,
  promiseForTransaction,
} from "./wptsupport.js";

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

// IDBCursor.update() - index - modify a record in the object store
test("WPT test idbcursor_update_index.htm", (t) => {
  return new Promise((resolve, reject) => {
    var db: any,
      records = [
        { pKey: "primaryKey_0", iKey: "indexKey_0" },
        { pKey: "primaryKey_1", iKey: "indexKey_1" },
      ];

    var open_rq = createdb(t);
    open_rq.onupgradeneeded = function (e: any) {
      db = e.target.result;

      var objStore = db.createObjectStore("test", { keyPath: "pKey" });
      objStore.createIndex("index", "iKey");

      for (var i = 0; i < records.length; i++) objStore.add(records[i]);

      // XXX: Gecko doesn't like this
      //e.target.transaction.oncomplete = t.step_func(CursorUpdateRecord);
    };

    open_rq.onsuccess = CursorUpdateRecord;

    function CursorUpdateRecord(e: any) {
      var txn = db.transaction("test", "readwrite"),
        cursor_rq = txn.objectStore("test").index("index").openCursor();
      cursor_rq.onsuccess = function (e: any) {
        var cursor = e.target.result;

        cursor.value.iKey += "_updated";
        cursor.update(cursor.value);
      };

      txn.oncomplete = VerifyRecordWasUpdated;
    }

    function VerifyRecordWasUpdated(e: any) {
      var cursor_rq = db.transaction("test").objectStore("test").openCursor();

      cursor_rq.onsuccess = function (e: any) {
        var cursor = e.target.result;

        t.deepEqual(cursor.value.iKey, records[0].iKey + "_updated");
        resolve();
      };
    }
  });
});

// IDBCursor.update() - index - attempt to modify a record in a read-only transaction
test("WPT test idbcursor_update_index2.htm", (t) => {
  return new Promise((resolve, reject) => {
    var db: any,
      records = [
        { pKey: "primaryKey_0", iKey: "indexKey_0" },
        { pKey: "primaryKey_1", iKey: "indexKey_1" },
      ];

    var open_rq = createdb(t);
    open_rq.onupgradeneeded = function (e: any) {
      db = e.target.result;

      var objStore = db.createObjectStore("test", { keyPath: "pKey" });
      objStore.createIndex("index", "iKey");

      for (var i = 0; i < records.length; i++) objStore.add(records[i]);
    };

    open_rq.onsuccess = function (e: any) {
      var cursor_rq = db
        .transaction("test")
        .objectStore("test")
        .index("index")
        .openCursor();

      cursor_rq.onsuccess = function (e: any) {
        var cursor = e.target.result;
        t.throws(
          function () {
            cursor.update(cursor.value);
          },
          { name: "ReadOnlyError" },
        );
        resolve();
      };
    };
  });
});

//IDBCursor.update() - index - attempt to modify a record in an inactive transaction
test("WPT test idbcursor_update_index3.htm", (t) => {
  return new Promise((resolve, reject) => {
    var db: any,
      records = [
        { pKey: "primaryKey_0", iKey: "indexKey_0" },
        { pKey: "primaryKey_1", iKey: "indexKey_1" },
      ];

    var open_rq = createdb(t);
    open_rq.onupgradeneeded = function (e: any) {
      db = e.target.result;
      var objStore = db.createObjectStore("test", { keyPath: "pKey" });
      var index = objStore.createIndex("index", "iKey");

      for (var i = 0; i < records.length; i++) objStore.add(records[i]);

      var cursor_rq = index.openCursor();

      const window: any = {};

      cursor_rq.onsuccess = function (e: any) {
        var cursor = e.target.result;
        t.true(cursor instanceof BridgeIDBCursor, "cursor exist");
        window.cursor = cursor;
        window.record = cursor.value;
      };

      e.target.transaction.oncomplete = function (e: any) {
        t.throws(
          function () {
            window.cursor.update(window.record);
          },
          { name: "TransactionInactiveError" },
        );
        resolve();
      };
    };
  });
});

// IDBCursor.update() - index - attempt to modify a record when object store been deleted
test("WPT test idbcursor_update_index4.htm", (t) => {
  return new Promise((resolve, reject) => {
    var db: any,
      records = [
        { pKey: "primaryKey_0", iKey: "indexKey_0" },
        { pKey: "primaryKey_1", iKey: "indexKey_1" },
      ];

    var open_rq = createdb(t);
    open_rq.onupgradeneeded = function (event: any) {
      db = event.target.result;
      var objStore = db.createObjectStore("store", { keyPath: "pKey" });
      objStore.createIndex("index", "iKey");
      for (var i = 0; i < records.length; i++) {
        objStore.add(records[i]);
      }
      var rq = objStore.index("index").openCursor();
      rq.onsuccess = function (event: any) {
        var cursor = event.target.result;
        t.true(cursor instanceof BridgeIDBCursor);

        db.deleteObjectStore("store");
        cursor.value.iKey += "_updated";
        t.throws(
          function () {
            cursor.update(cursor.value);
          },
          { name: "InvalidStateError" },
          "If the cursor's source or effective object store has been deleted, the implementation MUST throw a DOMException of type InvalidStateError",
        );

        resolve();
      };
    };
  });
});

// IDBCursor.update() - index - throw DataCloneError
test("WPT test idbcursor_update_index5.htm", (t) => {
  return new Promise((resolve, reject) => {
    var db: any,
      records = [
        { pKey: "primaryKey_0", iKey: "indexKey_0" },
        { pKey: "primaryKey_1", iKey: "indexKey_1" },
      ];

    var open_rq = createdb(t);
    open_rq.onupgradeneeded = function (e: any) {
      db = e.target.result;

      var objStore = db.createObjectStore("test", { keyPath: "pKey" });
      objStore.createIndex("index", "iKey");

      for (var i = 0; i < records.length; i++) objStore.add(records[i]);
    };

    open_rq.onsuccess = function (e: any) {
      var cursor_rq = db
        .transaction("test", "readwrite")
        .objectStore("test")
        .index("index")
        .openCursor();

      cursor_rq.onsuccess = function (e: any) {
        var cursor = e.target.result;
        t.true(cursor instanceof BridgeIDBCursor);

        var record = cursor.value;
        // Original test uses different uncloneable value
        record.data = { foo: () => {} };
        t.throws(
          function () {
            cursor.update(record);
          },
          { name: "DataCloneError" },
        );
        resolve();
      };
    };
  });
});

// IDBCursor.update() - index - no argument
test("WPT test idbcursor_update_index6.htm", (t) => {
  return new Promise((resolve, reject) => {
    var db: any,
      records = [
        { pKey: "primaryKey_0", iKey: "indexKey_0" },
        { pKey: "primaryKey_1", iKey: "indexKey_1" },
      ];

    var open_rq = createdb(t);
    open_rq.onupgradeneeded = function (e: any) {
      db = e.target.result;

      var objStore = db.createObjectStore("test", { keyPath: "pKey" });
      objStore.createIndex("index", "iKey");

      for (var i = 0; i < records.length; i++) objStore.add(records[i]);
    };

    open_rq.onsuccess = function (e: any) {
      var cursor_rq = db
        .transaction("test")
        .objectStore("test")
        .index("index")
        .openCursor();

      cursor_rq.onsuccess = function (e: any) {
        var cursor = e.target.result;
        t.true(cursor instanceof BridgeIDBCursor);

        t.throws(
          function () {
            cursor.update();
          },
          {
            instanceOf: TypeError,
          },
        );
        resolve();
      };
    };
  });
});

// IDBCursor.update() - index - throw DataError
test("WPT test idbcursor_update_index7.htm", (t) => {
  return new Promise((resolve, reject) => {
    var db: any,
      records = [
        { pKey: "primaryKey_0", iKey: "indexKey_0" },
        { pKey: "primaryKey_1", iKey: "indexKey_1" },
      ];

    var open_rq = createdb(t);
    open_rq.onupgradeneeded = function (e: any) {
      db = e.target.result;

      var objStore = db.createObjectStore("test", { keyPath: "pKey" });
      objStore.createIndex("index", "iKey");

      for (var i = 0; i < records.length; i++) objStore.add(records[i]);
    };

    open_rq.onsuccess = function (e: any) {
      var cursor_rq = db
        .transaction("test", "readwrite")
        .objectStore("test")
        .index("index")
        .openCursor();

      cursor_rq.onsuccess = function (e: any) {
        var cursor = e.target.result;
        t.true(cursor instanceof BridgeIDBCursor);

        t.throws(
          function () {
            cursor.update(null);
          },
          { name: "DataError" },
        );
        resolve();
      };
    };
  });
});

// IDBCursor.update() - index - throw InvalidStateError when the cursor is being iterated
test("WPT test idbcursor_update_index8.htm", (t) => {
  return new Promise((resolve, reject) => {
    var db: any,
      records = [
        { pKey: "primaryKey_0", iKey: "indexKey_0" },
        { pKey: "primaryKey_1", iKey: "indexKey_1" },
      ];

    var open_rq = createdb(t);
    open_rq.onupgradeneeded = function (e: any) {
      db = e.target.result;

      var objStore = db.createObjectStore("store", { keyPath: "pKey" });
      objStore.createIndex("index", "iKey");

      for (var i = 0; i < records.length; i++) objStore.add(records[i]);
    };

    open_rq.onsuccess = function (e: any) {
      var cursor_rq = db
        .transaction("store", "readwrite")
        .objectStore("store")
        .index("index")
        .openCursor();

      cursor_rq.onsuccess = function (e: any) {
        var cursor = e.target.result;
        t.true(cursor instanceof BridgeIDBCursor, "cursor exists");

        cursor.continue();
        t.throws(
          function () {
            cursor.update({ pKey: "primaryKey_0", iKey: "indexKey_0_updated" });
          },
          {
            name: "InvalidStateError",
          },
        );

        resolve();
      };
    };
  });
});

// Index cursor - indexed values updated during iteration
test("WPT test idbcursor_update_index9.any.js", async (t) => {
  const db = await createDatabase(t, (db) => {
    const store = db.createObjectStore("store");
    store.createIndex("index", "value");
    store.put({ value: 1 }, 1);
    store.put({ value: 2 }, 2);
    store.put({ value: 3 }, 3);
  });

  {
    // Iterate over all index entries until an upper bound is reached.
    // On each record found, increment the value used as the index
    // key, which will make it show again up later in the iteration.
    const tx = db.transaction("store", "readwrite");
    const range = BridgeIDBKeyRange.upperBound(9);
    const index = tx.objectStore("store").index("index");
    const request = index.openCursor(range);
    request.onsuccess = (e: any) => {
      const cursor = e.target.result;
      if (!cursor) return;

      const record = cursor.value;
      record.value += 1;
      cursor.update(record);

      cursor.continue();
    };

    await promiseForTransaction(t, tx);
  }

  {
    const tx = db.transaction("store", "readonly");
    const results = await promiseForRequest(
      t,
      tx.objectStore("store").getAll(),
    );
    t.deepEqual(
      results.map((record) => record.value),
      [10, 10, 10],
      "Values should all be incremented until bound reached",
    );
  }
});
