/* global
  _,
  Backbone,
  i18n,
  MessageController,
  moment,
  Whisper
*/

// eslint-disable-next-line func-names
(function() {
  'use strict';

  window.Whisper = window.Whisper || {};

  async function destroyExpiredMessages() {
    try {
      window.log.info('destroyExpiredMessages: Loading messages...');
      const messages = await window.Signal.Data.getExpiredMessages({
        MessageCollection: Whisper.MessageCollection,
      });

      await Promise.all(
        messages.map(async fromDB => {
          const message = MessageController.register(fromDB.id, fromDB);

          window.log.info('Message expired', {
            sentAt: message.get('sent_at'),
          });

          // We delete after the trigger to allow the conversation time to process
          //   the expiration before the message is removed from the database.
          await window.Signal.Data.removeMessage(message.id, {
            Message: Whisper.Message,
          });

          Whisper.events.trigger(
            'messageExpired',
            message.id,
            message.conversationId
          );

          const conversation = message.getConversation();
          if (conversation) {
            conversation.trigger('expired', message);
          }
        })
      );
    } catch (error) {
      window.log.error(
        'destroyExpiredMessages: Error deleting expired messages',
        error && error.stack ? error.stack : error
      );
    }

    window.log.info('destroyExpiredMessages: complete');
    checkExpiringMessages();
  }

  let timeout;
  async function checkExpiringMessages() {
    // Look up the next expiring message and set a timer to destroy it
    const messages = await window.Signal.Data.getNextExpiringMessage({
      MessageCollection: Whisper.MessageCollection,
    });

    const next = messages.at(0);
    if (!next) {
      return;
    }

    const expiresAt = next.get('expires_at');
    Whisper.ExpiringMessagesListener.nextExpiration = expiresAt;
    window.log.info('next message expires', new Date(expiresAt).toISOString());

    let wait = expiresAt - Date.now();

    // In the past
    if (wait < 0) {
      wait = 0;
    }

    // Too far in the future, since it's limited to a 32-bit value
    if (wait > 2147483647) {
      wait = 2147483647;
    }

    clearTimeout(timeout);
    timeout = setTimeout(destroyExpiredMessages, wait);
  }
  const throttledCheckExpiringMessages = _.throttle(
    checkExpiringMessages,
    1000
  );

  Whisper.ExpiringMessagesListener = {
    nextExpiration: null,
    init(events) {
      checkExpiringMessages();
      events.on('timetravel', throttledCheckExpiringMessages);
    },
    update: throttledCheckExpiringMessages,
  };

  const TimerOption = Backbone.Model.extend({
    getName() {
      return (
        i18n(['timerOption', this.get('time'), this.get('unit')].join('_')) ||
        moment.duration(this.get('time'), this.get('unit')).humanize()
      );
    },
    getAbbreviated() {
      return i18n(
        ['timerOption', this.get('time'), this.get('unit'), 'abbreviated'].join(
          '_'
        )
      );
    },
  });
  Whisper.ExpirationTimerOptions = new (Backbone.Collection.extend({
    model: TimerOption,
    getName(seconds = 0) {
      const o = this.findWhere({ seconds });
      if (o) {
        return o.getName();
      }
      return [seconds, 'seconds'].join(' ');
    },
    getAbbreviated(seconds = 0) {
      const o = this.findWhere({ seconds });
      if (o) {
        return o.getAbbreviated();
      }
      return [seconds, 's'].join('');
    },
  }))(
    [
      [0, 'seconds'],
      [5, 'seconds'],
      [10, 'seconds'],
      [30, 'seconds'],
      [1, 'minute'],
      [5, 'minutes'],
      [30, 'minutes'],
      [1, 'hour'],
      [6, 'hours'],
      [12, 'hours'],
      [1, 'day'],
      [1, 'week'],
    ].map(o => {
      const duration = moment.duration(o[0], o[1]); // 5, 'seconds'
      return {
        time: o[0],
        unit: o[1],
        seconds: duration.asSeconds(),
      };
    })
  );
})();