/* global window, setTimeout, clearTimeout, textsecure, WebAPI, ConversationController */

module.exports = {
  initialize,
};

const ONE_DAY = 24 * 60 * 60 * 1000; // one day
const MINIMUM_TIME_LEFT = 2 * 60 * 60 * 1000; // two hours

let timeout = null;
let scheduledTime = null;
let scheduleNext = null;

// We need to refresh our own profile regularly to account for newly-added devices which
//   do not support unidentified delivery.
function refreshOurProfile() {
  const ourNumber = textsecure.storage.user.getNumber();
  const conversation = ConversationController.getOrCreate(ourNumber, 'private');
  conversation.getProfiles();
}

function initialize({ events, storage, navigator, logger }) {
  // We don't want to set up all of the below functions, but we do want to ensure that our
  //   refresh timer is up-to-date.
  if (scheduleNext) {
    scheduleNext();
    return;
  }

  runWhenOnline();

  events.on('timetravel', scheduleNextRotation);

  function scheduleNextRotation() {
    const now = Date.now();
    const certificate = storage.get('senderCertificate');
    if (!certificate) {
      setTimeoutForNextRun(now);

      return;
    }

    // The useful information in a SenderCertificate is all serialized, so we
    //   need to do another layer of decoding.
    const decoded = textsecure.protobuf.SenderCertificate.Certificate.decode(
      certificate.certificate
    );
    const expires = decoded.expires.toNumber();

    const time = Math.min(now + ONE_DAY, expires - MINIMUM_TIME_LEFT);

    setTimeoutForNextRun(time);
  }

  // Keeping this entrypoint around so more inialize() calls just kick the timing
  scheduleNext = scheduleNextRotation;

  async function run() {
    logger.info('refreshSenderCertificate: Getting new certificate...');
    try {
      const username = storage.get('number_id');
      const password = storage.get('password');
      const server = WebAPI.connect({ username, password });

      const { certificate } = await server.getSenderCertificate();
      const arrayBuffer = window.Signal.Crypto.base64ToArrayBuffer(certificate);
      const decoded = textsecure.protobuf.SenderCertificate.decode(arrayBuffer);

      decoded.certificate = decoded.certificate.toArrayBuffer();
      decoded.signature = decoded.signature.toArrayBuffer();
      decoded.serialized = arrayBuffer;

      storage.put('senderCertificate', decoded);
      scheduleNextRotation();
    } catch (error) {
      logger.error(
        'refreshSenderCertificate: Get failed. Trying again in two minutes...',
        error && error.stack ? error.stack : error
      );
      setTimeout(runWhenOnline, 2 * 60 * 1000);
    }

    refreshOurProfile();
  }

  function runWhenOnline() {
    if (navigator.onLine) {
      run();
    } else {
      logger.info(
        'refreshSenderCertificate: Offline. Will update certificate when online...'
      );
      const listener = () => {
        logger.info(
          'refreshSenderCertificate: Online. Now updating certificate...'
        );
        window.removeEventListener('online', listener);
        run();
      };
      window.addEventListener('online', listener);
    }
  }

  function setTimeoutForNextRun(time = Date.now()) {
    const now = Date.now();

    if (scheduledTime !== time || !timeout) {
      logger.info(
        'Next sender certificate refresh scheduled for',
        new Date(time).toISOString()
      );
    }

    scheduledTime = time;
    const waitTime = Math.max(0, time - now);

    clearTimeout(timeout);
    timeout = setTimeout(runWhenOnline, waitTime);
  }
}
