"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.open = exports.OPSQLite = exports.IOS_LIBRARY_PATH = exports.IOS_DOCUMENT_PATH = exports.ANDROID_FILES_PATH = exports.ANDROID_EXTERNAL_FILES_PATH = exports.ANDROID_DATABASE_PATH = void 0;
var _reactNative = require("react-native");
if (global.__OPSQLiteProxy == null) {
  const OPSQLiteModule = _reactNative.NativeModules.OPSQLite;
  if (OPSQLiteModule == null) {
    throw new Error('Base module not found. Maybe try rebuilding the app.');
  }

  // Check if we are running on-device (JSI)
  if (global.nativeCallSyncHook == null || OPSQLiteModule.install == null) {
    throw new Error('Failed to install op-sqlite: React Native is not running on-device. OPSQLite can only be used when synchronous method invocations (JSI) are possible. If you are using a remote debugger (e.g. Chrome), switch to an on-device debugger (e.g. Flipper) instead.');
  }

  // Call the synchronous blocking install() function
  const result = OPSQLiteModule.install();
  if (result !== true) {
    throw new Error(`Failed to install op-sqlite: The native OPSQLite Module could not be installed! Looks like something went wrong when installing JSI bindings: ${result}`);
  }

  // Check again if the constructor now exists. If not, throw an error.
  if (global.__OPSQLiteProxy == null) {
    throw new Error('Failed to install op-sqlite, the native initializer function does not exist. Are you trying to use OPSQLite from different JS Runtimes?');
  }
}
const proxy = global.__OPSQLiteProxy;
const OPSQLite = proxy;
exports.OPSQLite = OPSQLite;
const {
  IOS_DOCUMENT_PATH,
  IOS_LIBRARY_PATH,
  ANDROID_DATABASE_PATH,
  ANDROID_FILES_PATH,
  ANDROID_EXTERNAL_FILES_PATH
} = _reactNative.NativeModules.OPSQLite;

/**
 * Object returned by SQL Query executions {
 *  insertId: Represent the auto-generated row id if applicable
 *  rowsAffected: Number of affected rows if result of a update query
 *  message: if status === 1, here you will find error description
 *  rows: if status is undefined or 0 this object will contain the query results
 * }
 *
 * @interface QueryResult
 */
exports.ANDROID_EXTERNAL_FILES_PATH = ANDROID_EXTERNAL_FILES_PATH;
exports.ANDROID_FILES_PATH = ANDROID_FILES_PATH;
exports.ANDROID_DATABASE_PATH = ANDROID_DATABASE_PATH;
exports.IOS_LIBRARY_PATH = IOS_LIBRARY_PATH;
exports.IOS_DOCUMENT_PATH = IOS_DOCUMENT_PATH;
const locks = {};

// Enhance some host functions

// Add 'item' function to result object to allow the sqlite-storage typeorm driver to work
function enhanceQueryResult(result) {
  // Add 'item' function to result object to allow the sqlite-storage typeorm driver to work
  if (result.rows == null) {
    result.rows = {
      _array: [],
      length: 0,
      item: idx => {
        var _result$rows;
        return (_result$rows = result.rows) === null || _result$rows === void 0 ? void 0 : _result$rows._array[idx];
      }
    };
  } else {
    result.rows.item = idx => {
      var _result$rows2;
      return (_result$rows2 = result.rows) === null || _result$rows2 === void 0 ? void 0 : _result$rows2._array[idx];
    };
  }
}
const _open = OPSQLite.open;
OPSQLite.open = (dbName, location, encryptionKey) => {
  _open(dbName, location, encryptionKey);
  locks[dbName] = {
    queue: [],
    inProgress: false
  };
};
const _close = OPSQLite.close;
OPSQLite.close = dbName => {
  _close(dbName);
  delete locks[dbName];
};
const _execute = OPSQLite.execute;
OPSQLite.execute = (dbName, query, params) => {
  const sanitizedParams = params === null || params === void 0 ? void 0 : params.map(p => {
    if (ArrayBuffer.isView(p)) {
      return p.buffer;
    }
    return p;
  });
  const result = _execute(dbName, query, sanitizedParams);
  enhanceQueryResult(result);
  return result;
};
const _executeAsync = OPSQLite.executeAsync;
OPSQLite.executeAsync = async (dbName, query, params) => {
  const sanitizedParams = params === null || params === void 0 ? void 0 : params.map(p => {
    if (ArrayBuffer.isView(p)) {
      return p.buffer;
    }
    return p;
  });
  const res = await _executeAsync(dbName, query, sanitizedParams);
  enhanceQueryResult(res);
  return res;
};
OPSQLite.transaction = async (dbName, fn) => {
  if (!locks[dbName]) {
    throw Error(`SQLite Error: No lock found on db: ${dbName}`);
  }
  let isFinalized = false;

  // Local transaction context object implementation
  const execute = (query, params) => {
    if (isFinalized) {
      throw Error(`SQLite Error: Cannot execute query on finalized transaction: ${dbName}`);
    }
    return OPSQLite.execute(dbName, query, params);
  };
  const executeAsync = (query, params) => {
    if (isFinalized) {
      throw Error(`SQLite Error: Cannot execute query on finalized transaction: ${dbName}`);
    }
    return OPSQLite.executeAsync(dbName, query, params);
  };
  const commit = () => {
    if (isFinalized) {
      throw Error(`SQLite Error: Cannot execute commit on finalized transaction: ${dbName}`);
    }
    const result = OPSQLite.execute(dbName, 'COMMIT');
    isFinalized = true;
    return result;
  };
  const rollback = () => {
    if (isFinalized) {
      throw Error(`SQLite Error: Cannot execute rollback on finalized transaction: ${dbName}`);
    }
    const result = OPSQLite.execute(dbName, 'ROLLBACK');
    isFinalized = true;
    return result;
  };
  async function run() {
    try {
      await OPSQLite.executeAsync(dbName, 'BEGIN TRANSACTION');
      await fn({
        commit,
        execute,
        executeAsync,
        rollback
      });
      if (!isFinalized) {
        commit();
      }
    } catch (executionError) {
      if (!isFinalized) {
        try {
          rollback();
        } catch (rollbackError) {
          throw rollbackError;
        }
      }
      throw executionError;
    } finally {
      locks[dbName].inProgress = false;
      isFinalized = false;
      startNextTransaction(dbName);
    }
  }
  return await new Promise((resolve, reject) => {
    const tx = {
      start: () => {
        run().then(resolve).catch(reject);
      }
    };
    locks[dbName].queue.push(tx);
    startNextTransaction(dbName);
  });
};
const startNextTransaction = dbName => {
  if (!locks[dbName]) {
    throw Error(`Lock not found for db: ${dbName}`);
  }
  if (locks[dbName].inProgress) {
    // Transaction is already in process bail out
    return;
  }
  if (locks[dbName].queue.length) {
    locks[dbName].inProgress = true;
    const tx = locks[dbName].queue.shift();
    if (!tx) {
      throw new Error('Could not get a operation on datebase');
    }
    setImmediate(() => {
      tx.start();
    });
  }
};
const open = _ref => {
  let {
    name,
    location,
    encryptionKey = ''
  } = _ref;
  OPSQLite.open(name, location, encryptionKey);
  return {
    close: () => OPSQLite.close(name),
    delete: () => OPSQLite.delete(name, location),
    attach: (dbNameToAttach, alias, location) => OPSQLite.attach(name, dbNameToAttach, alias, location),
    detach: alias => OPSQLite.detach(name, alias),
    transaction: fn => OPSQLite.transaction(name, fn),
    execute: (query, params) => OPSQLite.execute(name, query, params),
    executeAsync: (query, params) => OPSQLite.executeAsync(name, query, params),
    executeBatch: commands => OPSQLite.executeBatch(name, commands),
    executeBatchAsync: commands => OPSQLite.executeBatchAsync(name, commands),
    loadFile: location => OPSQLite.loadFile(name, location),
    updateHook: callback => OPSQLite.updateHook(name, callback),
    commitHook: callback => OPSQLite.commitHook(name, callback),
    rollbackHook: callback => OPSQLite.rollbackHook(name, callback)
  };
};
exports.open = open;
//# sourceMappingURL=index.js.map