/* global
  Backbone,
  Whisper,
  MessageController,
  ConversationController
*/

/* eslint-disable more/no-then */

// eslint-disable-next-line func-names
(function() {
  window.Whisper = window.Whisper || {};
  Whisper.Reactions = new (Backbone.Collection.extend({
    forMessage(message) {
      if (message.isOutgoing()) {
        const outgoingReactions = this.filter({
          targetTimestamp: message.get('sent_at'),
        });

        if (outgoingReactions.length > 0) {
          window.log.info('Found early reaction for outgoing message');
          this.remove(outgoingReactions);
          return outgoingReactions;
        }
      }

      const reactionsBySource = this.filter(re => {
        const mcid = message.get('conversationId');
        const recid = ConversationController.ensureContactIds({
          e164: re.get('targetAuthorE164'),
          uuid: re.get('targetAuthorUuid'),
        });
        const mTime = message.get('sent_at');
        const rTime = re.get('targetTimestamp');
        return mcid === recid && mTime === rTime;
      });

      if (reactionsBySource.length > 0) {
        window.log.info('Found early reaction for message');
        this.remove(reactionsBySource);
        return reactionsBySource;
      }

      return [];
    },
    async onReaction(reaction) {
      try {
        const targetConversation = await ConversationController.getConversationForTargetMessage(
          ConversationController.ensureContactIds({
            e164: reaction.get('targetAuthorE164'),
            uuid: reaction.get('targetAuthorUuid'),
          }),
          reaction.get('targetTimestamp')
        );
        if (!targetConversation) {
          window.log.info(
            'No contact for reaction',
            reaction.get('targetAuthorE164'),
            reaction.get('targetAuthorUuid'),
            reaction.get('targetTimestamp')
          );
          return;
        }

        // awaiting is safe since `onReaction` is never called from inside the queue
        await targetConversation.queueJob(async () => {
          window.log.info(
            'Handling reaction for',
            reaction.get('targetTimestamp')
          );

          const messages = await window.Signal.Data.getMessagesBySentAt(
            reaction.get('targetTimestamp'),
            {
              MessageCollection: Whisper.MessageCollection,
            }
          );
          // Message is fetched inside the conversation queue so we have the
          // most recent data
          const targetMessage = messages.find(m => {
            const contact = m.getContact();

            if (!contact) {
              return false;
            }

            const mcid = contact.get('id');
            const recid = ConversationController.ensureContactIds({
              e164: reaction.get('targetAuthorE164'),
              uuid: reaction.get('targetAuthorUuid'),
            });
            return mcid === recid;
          });

          if (!targetMessage) {
            window.log.info(
              'No message for reaction',
              reaction.get('targetAuthorE164'),
              reaction.get('targetAuthorUuid'),
              reaction.get('targetTimestamp')
            );

            // Since we haven't received the message for which we are removing a
            // reaction, we can just remove those pending reactions
            if (reaction.get('remove')) {
              this.remove(reaction);
              const oldReaction = this.where({
                targetAuthorE164: reaction.get('targetAuthorE164'),
                targetAuthorUuid: reaction.get('targetAuthorUuid'),
                targetTimestamp: reaction.get('targetTimestamp'),
                emoji: reaction.get('emoji'),
              });
              oldReaction.forEach(r => this.remove(r));
            }

            return;
          }

          const message = MessageController.register(
            targetMessage.id,
            targetMessage
          );

          await message.handleReaction(reaction);

          this.remove(reaction);
        });
      } catch (error) {
        window.log.error(
          'Reactions.onReaction error:',
          error && error.stack ? error.stack : error
        );
      }
    },
  }))();
})();