/* @flow */
import { createSelector } from 'reselect';

import type { Narrow } from '../types';
import {
  getAllMessages,
  getSubscriptions,
  getMute,
  getUsers,
  getStreams,
  getOutbox,
  getNonActiveUsers,
  getCrossRealmBots,
} from '../directSelectors';
import { getCaughtUpForActiveNarrow } from '../caughtup/caughtUpSelectors';
import { getIsFetching } from './fetchingSelectors';
import {
  isAllPrivateNarrow,
  isPrivateOrGroupNarrow,
  isStreamNarrow,
  isHomeNarrow,
  isPrivateNarrow,
  canSendToNarrow,
  isStreamOrTopicNarrow,
} from '../utils/narrow';
import { groupItemsById } from '../utils/misc';
import { shouldBeMuted } from '../utils/message';
import { NULL_ARRAY, NULL_MESSAGE, NULL_USER, NULL_SUBSCRIPTION } from '../nullObjects';

const getMessagesFromChatState = (narrow: Narrow) => state =>
  state.messages[JSON.stringify(narrow)] || NULL_ARRAY;

export const outboxMessagesForCurrentNarrow = (narrow: Narrow) =>
  createSelector(getCaughtUpForActiveNarrow(narrow), getOutbox, (caughtUp, outboxMessages) => {
    if (!caughtUp.newer) {
      return [];
    }

    if (isHomeNarrow(narrow)) {
      return outboxMessages;
    }

    return outboxMessages.filter(item => {
      if (isAllPrivateNarrow(narrow) && isPrivateOrGroupNarrow(item.narrow)) return true;
      if (isStreamNarrow(narrow) && item.narrow[0].operand === narrow[0].operand) return true;
      return JSON.stringify(item.narrow) === JSON.stringify(narrow);
    });
  });

export const getFetchedMessagesForNarrow = (narrow: Narrow) =>
  createSelector(getAllMessages, allMessages => allMessages[JSON.stringify(narrow)] || NULL_ARRAY);

export const getMessagesForNarrow = (narrow: Narrow) =>
  createSelector(
    getFetchedMessagesForNarrow(narrow),
    outboxMessagesForCurrentNarrow(narrow),
    (fetchedMessages, outboxMessages) => {
      if (outboxMessages.length === 0) {
        return fetchedMessages;
      }

      return [...fetchedMessages, ...outboxMessages].sort((a, b) => a.id - b.id);
    },
  );

export const getShownMessagesForNarrow = (narrow: Narrow) =>
  createSelector(
    getMessagesForNarrow(narrow),
    getSubscriptions,
    getMute,
    (messagesForNarrow, subscriptions, mute) =>
      messagesForNarrow.filter(item => !shouldBeMuted(item, narrow, subscriptions, mute)),
  );

export const getFirstMessageId = (narrow: Narrow) =>
  createSelector(
    getFetchedMessagesForNarrow(narrow),
    messages => (messages.length > 0 ? messages[0].id : undefined),
  );

export const getLastMessageId = (narrow: Narrow) =>
  createSelector(
    getFetchedMessagesForNarrow(narrow),
    messages => (messages.length > 0 ? messages[messages.length - 1].id : undefined),
  );

export const getLastTopicForNarrow = (narrow: Narrow) =>
  createSelector(getMessagesForNarrow(narrow), messagesForNarrow => {
    const reversedMessages = messagesForNarrow.slice().reverse();
    const lastMessageWithSubject = reversedMessages.find(msg => msg.subject) || NULL_MESSAGE;
    return lastMessageWithSubject.subject;
  });

export const getUserInPmNarrow = (narrow: Narrow) =>
  createSelector(getUsers, users => users.find(x => x.email === narrow[0].operand) || NULL_USER);

export const getRecipientsInGroupNarrow = (narrow: Narrow) =>
  createSelector(
    getUsers,
    users =>
      !narrow || narrow.length === 0
        ? []
        : narrow[0].operand.split(',').map(r => users.find(x => x.email === r) || []),
  );

export const getStreamInNarrow = (narrow: Narrow) =>
  createSelector(getSubscriptions, getStreams, (subscriptions, streams) => {
    if (!isStreamOrTopicNarrow(narrow)) {
      return NULL_SUBSCRIPTION;
    }

    const subscription = subscriptions.find(x => x.name === narrow[0].operand);
    if (subscription) {
      return subscription;
    }

    const stream = streams.find(x => x.name === narrow[0].operand);
    if (stream) {
      return {
        ...stream,
        in_home_view: true,
      };
    }

    return NULL_SUBSCRIPTION;
  });

export const getIfNoMessages = (narrow: Narrow) =>
  createSelector(getShownMessagesForNarrow(narrow), messages => messages && messages.length === 0);

export const getShowMessagePlaceholders = (narrow: Narrow) =>
  createSelector(
    getIfNoMessages(narrow),
    getIsFetching(narrow),
    (noMessages, isFetching) => isFetching && noMessages,
  );

export const getMessagesById = (narrow: Narrow) =>
  createSelector(getMessagesFromChatState(narrow), groupItemsById);

export const canSendToActiveNarrow = (narrow: Narrow) => canSendToNarrow(narrow);

export const isNarrowValid = (narrow: Narrow) =>
  createSelector(
    getStreams,
    getUsers,
    getNonActiveUsers,
    getCrossRealmBots,
    (streams, users, nonActiveUsers, crossRealmBots) => {
      if (isStreamOrTopicNarrow(narrow)) {
        return streams.find(s => s.name === narrow[0].operand) !== undefined;
      }

      if (isPrivateNarrow(narrow)) {
        const allUsersAndBots = [...users, ...nonActiveUsers, ...crossRealmBots];
        return allUsersAndBots.find(u => u.email === narrow[0].operand) !== undefined;
      }

      return true;
    },
  );

@@ -190,50 +190,8 @@
 ox,%0A
-  getNonActiveUsers,%0A  getCrossRealmBots,%0A
 %7D fr
@@ -283,32 +283,93 @@
 htUpSelectors';%0A
+import %7B getAllUsersAndBots %7D from '../users/userSelectors';%0A
 import %7B getIsFe
@@ -3121,31 +3121,70 @@
 tor(
-getUsers, users =%3E user
+%0A    getAllUsersAndBots,%0A    allUsersAndBots =%3E allUsersAndBot
 s.fi
@@ -3234,16 +3234,20 @@
 ULL_USER
+,%0A  
 );%0A%0Aexpo
@@ -4663,21 +4663,16 @@
 elector(
-%0A    
 getStrea
@@ -4678,72 +4678,28 @@
 ams,
-%0A    getUsers,%0A    getNonActiveUsers,%0A    getCrossRealm
+ getAllUsersAnd
 Bots,
-%0A   
  (st
@@ -4709,41 +4709,19 @@
 ms, 
-users, nonActiveUsers, crossRealm
+allUsersAnd
 Bots
@@ -4719,34 +4719,32 @@
 rsAndBots) =%3E %7B%0A
-  
     if (isStream
@@ -4764,26 +4764,24 @@
 (narrow)) %7B%0A
-  
       return
@@ -4844,31 +4844,27 @@
 efined;%0A    
- 
- 
 %7D%0A%0A
-  
     if (isPr
@@ -4890,92 +4890,8 @@
 ) %7B%0A
-        const allUsersAndBots = %5B...users, ...nonActiveUsers, ...crossRealmBots%5D;%0A  
     
@@ -4971,31 +4971,27 @@
 efined;%0A    
-  
 %7D%0A%0A
-  
     return t
@@ -4993,22 +4993,16 @@
 urn true;%0A  
-  %7D,%0A  
+%7D
 );%0A

Use `getAllUsersAndBots` inside `isNarrowValid`