/* eslint-disable no-undef */
import { NativeModules } from 'react-native';
const QuickSQLiteModule = NativeModules.QuickSQLite;

if (QuickSQLiteModule) {
  if (typeof QuickSQLiteModule.install === 'function') {
    QuickSQLiteModule.install();
  }
}
/**
 * JSI BINDINGS DO NOT WORK WHEN CONNECTED TO THE CHROME DEBUGGER
 * Use flipper to debug your RN apps from now on
 */

/**
 * Object returned by SQL Query executions {
 *  status: 0 or undefined for correct execution, 1 for error
 *  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
 */


//   _______ _____            _   _  _____         _____ _______ _____ ____  _   _  _____
//  |__   __|  __ \     /\   | \ | |/ ____|  /\   / ____|__   __|_   _/ __ \| \ | |/ ____|
//     | |  | |__) |   /  \  |  \| | (___   /  \ | |       | |    | || |  | |  \| | (___
//     | |  |  _  /   / /\ \ | . ` |\___ \ / /\ \| |       | |    | || |  | | . ` |\___ \
//     | |  | | \ \  / ____ \| |\  |____) / ____ \ |____   | |   _| || |__| | |\  |____) |
//     |_|  |_|  \_\/_/    \_\_| \_|_____/_/    \_\_____|  |_|  |_____\____/|_| \_|_____/
const locks = {}; // Enhance some host functions
// Add 'item' function to result object to allow the sqlite-storage typeorm driver to work

const enhanceQueryResult = result => {
  // Add 'item' function to result object to allow the sqlite-storage typeorm driver to work
  if (result.rows != null) {
    result.rows.item = idx => result.rows._array[idx];
  }
};

const _open = sqlite.open;

sqlite.open = (dbName, location) => {
  const res = _open(dbName, location);

  if (res.status === 0) {
    locks[dbName] = {
      queue: [],
      inProgress: false
    };
  }

  return res;
};

const _close = sqlite.close;

sqlite.close = dbName => {
  const res = _close(dbName);

  if (res.status === 0) {
    setImmediate(() => {
      delete locks[dbName];
    });
  }

  return res;
};

const _executeSql = sqlite.executeSql;

sqlite.executeSql = (dbName, query, params) => {
  const result = _executeSql(dbName, query, params);

  enhanceQueryResult(result);
  return result;
};

const _asyncExecuteSql = sqlite.asyncExecuteSql;

sqlite.asyncExecuteSql = (dbName, query, params, cb) => {
  const localCB = res => {
    enhanceQueryResult(res);
    cb(res);
  };

  _asyncExecuteSql(dbName, query, params, localCB);
};

sqlite.transaction = (dbName, callback) => {
  if (!locks[dbName]) {
    throw Error(`No lock found on db: ${dbName}`);
  } // Local transaction context object implementation


  const executeSql = (query, params) => {
    return sqlite.executeSql(dbName, query, params);
  };

  const tx = {
    start: () => {
      try {
        sqlite.executeSql(dbName, 'BEGIN TRANSACTION', null);
        const result = callback({
          executeSql
        });

        if (result === true) {
          sqlite.executeSql(dbName, 'COMMIT', null);
        } else {
          sqlite.executeSql(dbName, 'ROLLBACK', null);
        }
      } catch (e) {
        sqlite.executeSql(dbName, 'ROLLBACK', null);
        throw e;
      } finally {
        locks[dbName].inProgress = false;
        startNextTransaction(dbName);
      }
    }
  };
  locks[dbName].queue.push(tx);
  startNextTransaction(dbName);
};

const startNextTransaction = dbName => {
  if (locks[dbName].inProgress) {
    // Transaction is already in process bail out
    return;
  }

  setImmediate(() => {
    if (!locks[dbName]) {
      throw Error(`Lock not found for db ${dbName}`);
    }

    if (locks[dbName].queue.length) {
      locks[dbName].inProgress = true;
      locks[dbName].queue.shift().start();
    }
  });
}; //   _________     _______  ______ ____  _____  __  __            _____ _____
//  |__   __\ \   / /  __ \|  ____/ __ \|  __ \|  \/  |     /\   |  __ \_   _|
//     | |   \ \_/ /| |__) | |__ | |  | | |__) | \  / |    /  \  | |__) || |
//     | |    \   / |  ___/|  __|| |  | |  _  /| |\/| |   / /\ \ |  ___/ | |
//     | |     | |  | |    | |___| |__| | | \ \| |  | |  / ____ \| |    _| |_
//     |_|     |_|  |_|    |______\____/|_|  \_\_|  |_| /_/    \_\_|   |_____|


export const openDatabase = (options, ok, fail) => {
  try {
    sqlite.open(options.name, options.location);
    const connection = {
      executeSql: (sql, params, ok, fail) => {
        try {
          let response = sqlite.executeSql(options.name, sql, params);
          enhanceQueryResult(response);
          ok(response);
        } catch (e) {
          fail(e);
        }
      },
      asyncExecuteSql: (sql, params, cb) => {
        try {
          sqlite.asyncExecuteSql(options.name, sql, params, response => {
            enhanceQueryResult(response);
            cb(response);
          });
        } catch (e) {
          fail(e);
        }
      },
      executeSqlBatch: (commands, callback) => {
        const response = sqlite.executeSqlBatch(options.name, commands);
        if (callback) callback(response);
      },
      asyncExecuteSqlBatch: (commands, cb) => {
        sqlite.asyncExecuteSqlBatch(options.name, commands, cb);
      },
      transaction: fn => {
        sqlite.transaction(options.name, fn);
      },
      close: (ok, fail) => {
        try {
          sqlite.close(options.name);
          ok();
        } catch (e) {
          fail(e);
        }
      },
      loadSqlFile: (location, callback) => {
        const result = sqlite.loadSqlFile(options.name, location);

        if (callback) {
          callback(result);
        }
      },
      asyncLoadSqlFile: (location, callback) => {
        sqlite.asyncLoadSqlFile(options.name, location, callback);
      }
    };
    ok(connection);
    return connection;
  } catch (e) {
    fail(e);
  }
};
//# sourceMappingURL=index.js.map