/* global window, Whisper, textsecure */

const { isFunction } = require('lodash');

const MessageDataMigrator = require('./messages_data_migrator');
const {
  run,
  getLatestVersion,
  getDatabase,
} = require('./migrations/migrations');

const MESSAGE_MINIMUM_VERSION = 7;

module.exports = {
  doesDatabaseExist,
  mandatoryMessageUpgrade,
  MESSAGE_MINIMUM_VERSION,
  migrateAllToSQLCipher,
  removeDatabase,
  runMigrations,
};

async function runMigrations() {
  window.log.info('Run migrations on database with attachment data');
  await run({
    Backbone: window.Backbone,
    logger: window.log,
  });

  Whisper.Database.migrations[0].version = getLatestVersion();
}

async function mandatoryMessageUpgrade({ upgradeMessageSchema } = {}) {
  if (!isFunction(upgradeMessageSchema)) {
    throw new Error(
      'mandatoryMessageUpgrade: upgradeMessageSchema must be a function!'
    );
  }

  const NUM_MESSAGES_PER_BATCH = 10;
  window.log.info(
    'upgradeMessages: Mandatory message schema upgrade started.',
    `Target version: ${MESSAGE_MINIMUM_VERSION}`
  );

  let isMigrationWithoutIndexComplete = false;
  while (!isMigrationWithoutIndexComplete) {
    const database = getDatabase();
    // eslint-disable-next-line no-await-in-loop
    const batchWithoutIndex = await MessageDataMigrator.processNextBatchWithoutIndex(
      {
        databaseName: database.name,
        minDatabaseVersion: database.version,
        numMessagesPerBatch: NUM_MESSAGES_PER_BATCH,
        upgradeMessageSchema,
        maxVersion: MESSAGE_MINIMUM_VERSION,
        BackboneMessage: Whisper.Message,
        saveMessage: window.Signal.Data.saveLegacyMessage,
      }
    );
    window.log.info(
      'upgradeMessages: upgrade without index',
      batchWithoutIndex
    );
    isMigrationWithoutIndexComplete = batchWithoutIndex.done;
  }
  window.log.info('upgradeMessages: upgrade without index complete!');

  let isMigrationWithIndexComplete = false;
  while (!isMigrationWithIndexComplete) {
    // eslint-disable-next-line no-await-in-loop
    const batchWithIndex = await MessageDataMigrator.processNext({
      BackboneMessage: Whisper.Message,
      BackboneMessageCollection: Whisper.MessageCollection,
      numMessagesPerBatch: NUM_MESSAGES_PER_BATCH,
      upgradeMessageSchema,
      getMessagesNeedingUpgrade:
        window.Signal.Data.getLegacyMessagesNeedingUpgrade,
      saveMessage: window.Signal.Data.saveLegacyMessage,
      maxVersion: MESSAGE_MINIMUM_VERSION,
    });
    window.log.info('upgradeMessages: upgrade with index', batchWithIndex);
    isMigrationWithIndexComplete = batchWithIndex.done;
  }
  window.log.info('upgradeMessages: upgrade with index complete!');

  window.log.info('upgradeMessages: Message schema upgrade complete');
}

async function migrateAllToSQLCipher({ writeNewAttachmentData, Views } = {}) {
  if (!isFunction(writeNewAttachmentData)) {
    throw new Error(
      'migrateAllToSQLCipher: writeNewAttachmentData must be a function'
    );
  }
  if (!Views) {
    throw new Error('migrateAllToSQLCipher: Views must be provided!');
  }

  let totalMessages;
  const db = await Whisper.Database.open();

  function showMigrationStatus(current) {
    const status = `${current}/${totalMessages}`;
    Views.Initialization.setMessage(
      window.i18n('migratingToSQLCipher', [status])
    );
  }

  try {
    totalMessages = await MessageDataMigrator.getNumMessages({
      connection: db,
    });
  } catch (error) {
    window.log.error(
      'background.getNumMessages error:',
      error && error.stack ? error.stack : error
    );
    totalMessages = 0;
  }

  if (totalMessages) {
    window.log.info(`About to migrate ${totalMessages} messages`);
    showMigrationStatus(0);
  } else {
    window.log.info('About to migrate non-messages');
  }

  await window.Signal.migrateToSQL({
    db,
    clearStores: Whisper.Database.clearStores,
    handleDOMException: Whisper.Database.handleDOMException,
    arrayBufferToString: textsecure.MessageReceiver.arrayBufferToStringBase64,
    countCallback: count => {
      window.log.info(`Migration: ${count} messages complete`);
      showMigrationStatus(count);
    },
    writeNewAttachmentData,
  });

  db.close();
}

async function doesDatabaseExist() {
  return new Promise((resolve, reject) => {
    const { id } = Whisper.Database;
    const req = window.indexedDB.open(id);

    let existed = true;

    req.onerror = reject;
    req.onsuccess = () => {
      req.result.close();
      resolve(existed);
    };
    req.onupgradeneeded = () => {
      if (req.result.version === 1) {
        existed = false;
        window.indexedDB.deleteDatabase(id);
      }
    };
  });
}

function removeDatabase() {
  window.log.info(`Deleting IndexedDB database '${Whisper.Database.id}'`);
  window.indexedDB.deleteDatabase(Whisper.Database.id);
}