/* @flow */
import parseMarkdown from 'zulip-markdown-parser';

import { logErrorRemotely } from '../utils/logging';
import type {
  Dispatch,
  GetState,
  GlobalState,
  NamedUser,
  Narrow,
  Outbox,
  User,
  DeleteOutboxMessageAction,
  MessageSendStartAction,
  MessageSendCompleteAction,
  ToggleOutboxSendingAction,
} from '../types';
import {
  MESSAGE_SEND_START,
  TOGGLE_OUTBOX_SENDING,
  DELETE_OUTBOX_MESSAGE,
  MESSAGE_SEND_COMPLETE,
} from '../actionConstants';
import { getAuth } from '../selectors';
import { sendMessage } from '../api';
import { getSelfUserDetail } from '../users/userSelectors';
import { getUserByEmail, getUsersAndWildcards } from '../users/userHelpers';
import { isStreamNarrow, isPrivateOrGroupNarrow } from '../utils/narrow';

export const messageSendStart = (outbox: Outbox): MessageSendStartAction => ({
  type: MESSAGE_SEND_START,
  outbox,
});

export const toggleOutboxSending = (sending: boolean): ToggleOutboxSendingAction => ({
  type: TOGGLE_OUTBOX_SENDING,
  sending,
});

export const deleteOutboxMessage = (localMessageId: number): DeleteOutboxMessageAction => ({
  type: DELETE_OUTBOX_MESSAGE,
  local_message_id: localMessageId,
});

export const messageSendComplete = (localMessageId: number): MessageSendCompleteAction => ({
  type: MESSAGE_SEND_COMPLETE,
  local_message_id: localMessageId,
});

export const trySendMessages = () => (dispatch: Dispatch, getState: GetState) => {
  const state = getState();
  if (state.outbox.length > 0 && !state.session.outboxSending) {
    dispatch(toggleOutboxSending(true));
    const auth = getAuth(state);
    state.outbox.forEach(async item => {
      try {
        await sendMessage(
          auth,
          item.type,
          isPrivateOrGroupNarrow(item.narrow) ? item.narrow[0].operand : item.display_recipient,
          item.subject,
          item.markdownContent,
          item.timestamp,
          state.session.eventQueueId,
        );
        dispatch(messageSendComplete(item.timestamp));
      } catch (e) {
        logErrorRemotely(e, 'error caught while sending');
      }
    });
    dispatch(toggleOutboxSending(false));
  }
};

const mapEmailsToUsers = (users, narrow, selfDetail) =>
  narrow[0].operand
    .split(',')
    .map(item => {
      const user = getUserByEmail(users, item);
      return { email: item, id: user.user_id, full_name: user.full_name };
    })
    .concat({ email: selfDetail.email, id: selfDetail.user_id, full_name: selfDetail.full_name });

// TODO type: `string | NamedUser[]` is a bit confusing.
const extractTypeToAndSubjectFromNarrow = (
  narrow: Narrow,
  users: User[],
  selfDetail: { email: string, user_id: number, full_name: string },
): { type: 'private' | 'stream', display_recipient: string | NamedUser[], subject: string } => {
  if (isPrivateOrGroupNarrow(narrow)) {
    return {
      type: 'private',
      display_recipient: mapEmailsToUsers(users, narrow, selfDetail),
      subject: '',
    };
  } else if (isStreamNarrow(narrow)) {
    return { type: 'stream', display_recipient: narrow[0].operand, subject: '(no topic)' };
  }
  return { type: 'stream', display_recipient: narrow[0].operand, subject: narrow[1].operand };
};

const getContentPreview = (content: string, state: GlobalState): string => {
  try {
    return parseMarkdown(
      content,
      getUsersAndWildcards(state.users),
      state.streams,
      getAuth(state),
      state.realm.filters,
      state.realm.emoji,
    );
  } catch (e) {
    return content;
  }
};

export const addToOutbox = (narrow: Narrow, content: string) => async (
  dispatch: Dispatch,
  getState: GetState,
) => {
  const state = getState();
  const userDetail = getSelfUserDetail(state);

  const localTime = Math.round(new Date().getTime() / 1000);
  dispatch(
    messageSendStart({
      narrow,
      ...extractTypeToAndSubjectFromNarrow(narrow, state.users, userDetail),
      markdownContent: content,
      content: getContentPreview(content, state),
      timestamp: localTime,
      id: localTime,
      sender_full_name: userDetail.full_name,
      sender_email: userDetail.email,
      avatar_url: userDetail.avatar_url,
      isOutbox: true,
    }),
  );
  dispatch(trySendMessages());
};

@@ -1488,16 +1488,17 @@
 gth 
-%3E 0 && !
+=== 0 %7C%7C 
 stat
@@ -1526,16 +1526,30 @@
 ng) %7B%0A  
+  return;%0A  %7D%0A
   dispat
@@ -1581,18 +1581,16 @@
 ue));%0A  
- 
- 
 const au
@@ -1610,18 +1610,16 @@
 state);%0A
-  
   state.
@@ -1653,26 +1653,22 @@
 %3E %7B%0A    
- 
- 
 try %7B%0A
-  
       aw
@@ -1696,18 +1696,14 @@
     
- 
- 
 auth,%0A
-  
     
@@ -1717,18 +1717,16 @@
 m.type,%0A
-  
         
@@ -1816,26 +1816,24 @@
 nt,%0A        
-  
 item.subject
@@ -1838,26 +1838,24 @@
 ct,%0A        
- 
- 
 item.markdow
@@ -1868,26 +1868,24 @@
 nt,%0A        
-  
 item.timesta
@@ -1892,26 +1892,24 @@
 mp,%0A        
- 
- 
 state.sessio
@@ -1930,18 +1930,16 @@
 ,%0A      
-  
 );%0A     
@@ -1935,26 +1935,24 @@
    );%0A      
- 
- 
 dispatch(mes
@@ -1990,18 +1990,16 @@
 ));%0A    
-  
 %7D catch 
@@ -2010,18 +2010,16 @@
 %7B%0A      
- 
- 
 logError
@@ -2069,22 +2069,16 @@
     
-  
 %7D%0A  
- 
- 
 %7D);%0A
-  
   di
@@ -2113,20 +2113,16 @@
 alse));%0A
-  %7D%0A
 %7D;%0A%0Acons

Simplify with an early `return`