/*
  global
  $
  ConversationController,
  extension,
  ConversationController
  getConversations,
  getInboxCollection,
  i18n,
  Whisper,
  textsecure,
  Signal,
*/

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

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

  Whisper.ConversationStack = Whisper.View.extend({
    className: 'conversation-stack',
    open(conversation) {
      const id = `conversation-${conversation.cid}`;
      const container = $('#main-view .conversation-stack');

      // Has been opened since app start, but not focussed
      const conversationExists = container.children(`#${id}`).length > 0;
      // Is focussed
      const conversationOpened = container.children().first().id === id;

      // To limit the size of the DOM for speed
      const maxNumConversations = 10;
      const numConversations = container
        .children()
        .not('.conversation.placeholder').length;
      const shouldTrimConversations = numConversations > maxNumConversations;

      if (shouldTrimConversations) {
        // Removes conversation which has been hidden the longest
        container.children()[numConversations - 1].remove();
      }

      if (conversationExists) {
        // User opened conversation, move it to top of stack, rather than re-rendering
        const conversations = container
          .children()
          .not('.conversation.placeholder');
        container
          .children(`#${id}`)
          .first()
          .insertBefore(conversations.first());
        conversation.trigger('opened');

        return;
      }

      if (!conversationOpened) {
        this.$el
          .first()
          .find('video, audio')
          .each(function pauseMedia() {
            this.pause();
          });
        let $el = this.$(`#${id}`);
        if ($el === null || $el.length === 0) {
          const view = new Whisper.ConversationView({
            model: conversation,
            window: this.model.window,
          });
          view.view.resetScrollPosition();

          // eslint-disable-next-line prefer-destructuring
          $el = view.$el;
        }

        container.prepend($el);
      }
      conversation.trigger('opened');
    },
    close(conversation) {
      const $el = $(`#conversation-${conversation.cid}`);
      if ($el && $el.length > 0) {
        $el.remove();
      }
    },
    showToast({ message }) {
      window.pushToast({
        title: message,
        type: 'success',
      });
    },
    showConfirmationDialog({
      title,
      message,
      messageSub,
      onOk,
      onCancel,
      hideCancel,
    }) {
      window.confirmationDialog({
        title,
        message,
        resolve: onOk,
        reject: onCancel,
        hideCancel,
        messageSub,
      });
    },
  });

  Whisper.AppLoadingScreen = Whisper.View.extend({
    templateName: 'app-loading-screen',
    className: 'app-loading-screen',
    updateProgress() {},
    render_attributes: {
      message: i18n('loading'),
    },
  });

  Whisper.InboxView = Whisper.View.extend({
    templateName: 'two-column',
    className: 'inbox index',
    initialize(options = {}) {
      this.ready = false;
      this.render();
      this.$el.attr('tabindex', '1');

      this.conversation_stack = new Whisper.ConversationStack({
        el: this.$('.conversation-stack'),
        model: { window: options.window },
      });

      if (!options.initialLoadComplete) {
        this.appLoadingScreen = new Whisper.AppLoadingScreen();
        this.appLoadingScreen.render();
        this.appLoadingScreen.$el.prependTo(this.el);
        this.startConnectionListener();
      }

      // Inbox
      const inboxCollection = getInboxCollection();

      // ConversationCollection
      const conversations = getConversations();
      this.listenTo(conversations, 'remove', conversation => {
        if (this.conversation_stack) {
          this.conversation_stack.close(conversation);
        }
      });

      this.listenTo(inboxCollection, 'messageError', () => {
        if (this.networkStatusView) {
          this.networkStatusView.render();
        }
      });

      this.networkStatusView = new Whisper.NetworkStatusView();
      this.$el
        .find('.network-status-container')
        .append(this.networkStatusView.render().el);

      extension.expired(expired => {
        if (expired) {
          const banner = new Whisper.ExpiredAlertBanner().render();
          banner.$el.prependTo(this.$el);
          this.$el.addClass('expired');
        }
      });

      // FIXME: Fix this for new react views
      this.setupLeftPane();
    },
    render_attributes: {
      welcomeToSession: i18n('welcomeToSession'),
    },
    events: {
      click: 'onClick',
      'click .section-toggle': 'toggleSection',
    },
    setupLeftPane() {
      // Here we set up a full redux store with initial state for our LeftPane Root
      const convoCollection = getConversations();
      const conversations = convoCollection.map(
        conversation => conversation.cachedProps
      );

      const initialState = {
        conversations: {
          conversationLookup: Signal.Util.makeLookup(conversations, 'id'),
        },
        user: {
          regionCode: window.storage.get('regionCode'),
          ourNumber:
            window.storage.get('primaryDevicePubKey') ||
            textsecure.storage.user.getNumber(),
          isSecondaryDevice: !!window.storage.get('isSecondaryDevice'),
          i18n: window.i18n,
        },
      };

      this.store = Signal.State.createStore(initialState);
      window.inboxStore = this.store;
      this.leftPaneView = new Whisper.ReactWrapperView({
        JSX: Signal.State.Roots.createLeftPane(this.store),
        className: 'left-pane-wrapper',
      });

      // Enables our redux store to be updated by backbone events in the outside world
      const {
        conversationAdded,
        conversationChanged,
        conversationRemoved,
        removeAllConversations,
        messageExpired,
        openConversationExternal,
      } = Signal.State.bindActionCreators(
        Signal.State.Ducks.conversations.actions,
        this.store.dispatch
      );
      const { userChanged } = Signal.State.bindActionCreators(
        Signal.State.Ducks.user.actions,
        this.store.dispatch
      );

      this.openConversationAction = openConversationExternal;

      // In the future this listener will be added by the conversation view itself. But
      //   because we currently have multiple converations open at once, we install just
      //   one global handler.
      // $(document).on('keydown', event => {
      //   const { ctrlKey, key } = event;

      // We can add Command-E as the Mac shortcut when we add it to our Electron menus:
      //   https://stackoverflow.com/questions/27380018/when-cmd-key-is-kept-pressed-keyup-is-not-triggered-for-any-other-key
      // For now, it will stay as CTRL-E only
      //   if (key === 'e' && ctrlKey) {
      //     const state = this.store.getState();
      //     const selectedId = state.conversations.selectedConversation;
      //     const conversation = ConversationController.get(selectedId);

      //     if (conversation && !conversation.get('isArchived')) {
      //       conversation.setArchived(true);
      //       conversation.trigger('unload');
      //     }
      //   }
      // });
      this.fetchHandleMessageSentData = this.fetchHandleMessageSentData.bind(
        this
      );
      this.handleMessageSentFailure = this.handleMessageSentFailure.bind(this);
      this.handleMessageSentSuccess = this.handleMessageSentSuccess.bind(this);

      this.listenTo(convoCollection, 'remove', conversation => {
        const { id } = conversation || {};
        conversationRemoved(id);
      });
      this.listenTo(convoCollection, 'add', conversation => {
        const { id, cachedProps } = conversation || {};
        conversationAdded(id, cachedProps);
      });
      this.listenTo(convoCollection, 'change', conversation => {
        const { id, cachedProps } = conversation || {};
        conversationChanged(id, cachedProps);
      });
      this.listenTo(convoCollection, 'reset', removeAllConversations);

      window.libsession
        .getMessageQueue()
        .events.addListener('success', this.handleMessageSentSuccess);

      window.libsession
        .getMessageQueue()
        .events.addListener('fail', this.handleMessageSentFailure);

      Whisper.events.on('messageExpired', messageExpired);
      Whisper.events.on('userChanged', userChanged);

      // Finally, add it to the DOM
      this.$('.left-pane-placeholder').append(this.leftPaneView.el);
    },

    async fetchHandleMessageSentData(m) {
      // nobody is listening to this freshly fetched message .trigger calls
      const tmpMsg = await window.Signal.Data.getMessageById(m.identifier, {
        Message: Whisper.Message,
      });

      if (!tmpMsg) {
        return null;
      }

      // find the corresponding conversation of this message
      const conv = window.ConversationController.get(
        tmpMsg.get('conversationId')
      );

      // then, find in this conversation the very same message
      const msg = conv.messageCollection.models.find(
        convMsg => convMsg.id === tmpMsg.id
      );

      if (!msg) {
        return null;
      }

      return { msg };
    },

    async handleMessageSentSuccess(sentMessage) {
      const fetchedData = await this.fetchHandleMessageSentData(sentMessage);
      if (!fetchedData) {
        return;
      }
      const { msg } = fetchedData;

      msg.handleMessageSentSuccess(sentMessage);
    },

    async handleMessageSentFailure(sentMessage, error) {
      const fetchedData = await this.fetchHandleMessageSentData(sentMessage);
      if (!fetchedData) {
        return;
      }
      const { msg } = fetchedData;

      await msg.handleMessageSentFailure(sentMessage, error);
    },

    startConnectionListener() {
      this.interval = setInterval(() => {
        const status = window.getSocketStatus();
        switch (status) {
          case WebSocket.CONNECTING:
            break;
          case WebSocket.OPEN:
            clearInterval(this.interval);
            // Default to connected, but lokinet is slow so we pretend empty event
            this.onEmpty();
            this.interval = null;
            break;
          case WebSocket.CLOSING:
          case WebSocket.CLOSED:
            clearInterval(this.interval);
            this.interval = null;
            // if we failed to connect, we pretend we got an empty event
            this.onEmpty();
            break;
          default:
            // We also replicate empty here
            this.onEmpty();
            break;
        }
      }, 1000);
    },
    onEmpty() {
      const view = this.appLoadingScreen;
      if (view) {
        this.appLoadingScreen = null;
        view.remove();
      }
    },
    onProgress(count) {
      const view = this.appLoadingScreen;
      if (view) {
        view.updateProgress(count);
      }
    },
    focusConversation(e) {
      if (e && this.$(e.target).closest('.placeholder').length) {
        return;
      }

      this.$('#header, .gutter').addClass('inactive');
      this.$('.conversation-stack').removeClass('inactive');
    },
    focusHeader() {
      this.$('.conversation-stack').addClass('inactive');
      this.$('#header, .gutter').removeClass('inactive');
      this.$('.conversation:first .menu').trigger('close');
    },
    reloadBackgroundPage() {
      window.location.reload();
    },
    toggleSection(e) {
      // Expand or collapse this panel
      const $target = this.$(e.currentTarget);
      const $next = $target.next();

      // Toggle section visibility
      $next.slideToggle('fast');
      $target.toggleClass('section-toggle-visible');
    },
    async openConversation(id, messageId) {
      // If we call this to create a new conversation, it can only be private
      // (group conversations are created elsewhere)
      const conversation = await ConversationController.getOrCreateAndWait(
        id,
        'private'
      );

      if (this.openConversationAction) {
        this.openConversationAction(id, messageId);
      }

      if (conversation) {
        conversation.updateProfileName();
      }

      this.conversation_stack.open(conversation);
      this.focusConversation();
    },
    closeConversation(conversation) {
      if (conversation) {
        this.inboxListView.removeItem(conversation);
        this.conversation_stack.close(conversation);
      }
    },
    closeRecording(e) {
      if (e && this.$(e.target).closest('.capture-audio').length > 0) {
        return;
      }
      this.$('.conversation:first .recorder').trigger('close');
    },
    onClick(e) {
      this.closeRecording(e);
    },
    showToastMessageInGutter(message) {
      const toast = new Whisper.MessageToastView({
        message,
      });
      toast.$el.appendTo(this.$('.gutter'));
      toast.render();
    },
  });

  Whisper.ExpiredAlertBanner = Whisper.View.extend({
    templateName: 'expired_alert',
    className: 'expiredAlert',
    render_attributes() {
      return {
        expiredWarning: i18n('expiredWarning'),
        upgrade: i18n('upgrade'),
      };
    },
  });
})();