import { addReducer, getGlobal, setGlobal } from '../../../lib/teact/teactn';

import { MAIN_THREAD_ID } from '../../../api/types';
import { FocusDirection } from '../../../types';

import {
  enterMessageSelectMode,
  toggleMessageSelection,
  exitMessageSelectMode,
  replaceThreadParam,
  updateFocusDirection,
  updateFocusedMessage,
} from '../../reducers';
import {
  selectCurrentChat,
  selectViewportIds,
  selectIsRightColumnShown,
  selectCurrentMessageList,
  selectChat,
  selectThreadInfo,
  selectChatMessages,
  selectAllowedMessageActions,
  selectMessageIdsByGroupId,
  selectForwardedMessageIdsByGroupId, selectIsViewportNewest, selectReplyingToId,
} from '../../selectors';
import { findLast } from '../../../util/iteratees';
import { IS_TOUCH_ENV } from '../../../util/environment';

const FOCUS_DURATION = 2000;
const POLL_RESULT_OPEN_DELAY_MS = 450;

let blurTimeout: number | undefined;

addReducer('setScrollOffset', (global, actions, payload) => {
  const { chatId, threadId, scrollOffset } = payload!;

  return replaceThreadParam(global, chatId, threadId, 'scrollOffset', scrollOffset);
});

addReducer('setReplyingToId', (global, actions, payload) => {
  const { messageId } = payload!;
  const currentMessageList = selectCurrentMessageList(global);
  if (!currentMessageList) {
    return undefined;
  }
  const { chatId, threadId } = currentMessageList;

  return replaceThreadParam(global, chatId, threadId, 'replyingToId', messageId);
});

addReducer('setEditingId', (global, actions, payload) => {
  const { messageId } = payload!;
  const currentMessageList = selectCurrentMessageList(global);
  if (!currentMessageList) {
    return undefined;
  }

  const { chatId, threadId, type } = currentMessageList;
  const paramName = type === 'scheduled' ? 'editingScheduledId' : 'editingId';

  return replaceThreadParam(global, chatId, threadId, paramName, messageId);
});

addReducer('editLastMessage', (global) => {
  const { chatId, threadId } = selectCurrentMessageList(global) || {};
  if (!chatId || !threadId) {
    return undefined;
  }

  const chatMessages = selectChatMessages(global, chatId);
  const viewportIds = selectViewportIds(global, chatId, threadId);
  if (!chatMessages || !viewportIds) {
    return undefined;
  }

  const lastOwnEditableMessageId = findLast(viewportIds, (id) => {
    return Boolean(chatMessages[id] && selectAllowedMessageActions(global, chatMessages[id], threadId).canEdit);
  });

  if (!lastOwnEditableMessageId) {
    return undefined;
  }

  return replaceThreadParam(global, chatId, threadId, 'editingId', lastOwnEditableMessageId);
});

addReducer('replyToNextMessage', (global, actions, payload) => {
  const { targetIndexDelta } = payload;
  const { chatId, threadId } = selectCurrentMessageList(global) || {};
  if (!chatId || !threadId) {
    return;
  }

  const chatMessages = selectChatMessages(global, chatId);
  const viewportIds = selectViewportIds(global, chatId, threadId);
  if (!chatMessages || !viewportIds) {
    return;
  }

  const replyingToId = selectReplyingToId(global, chatId, threadId);
  const isLatest = selectIsViewportNewest(global, chatId, threadId);

  let messageId: number | undefined;

  if (!isLatest || !replyingToId) {
    if (threadId === MAIN_THREAD_ID) {
      const chat = selectChat(global, chatId);

      messageId = chat && chat.lastMessage ? chat.lastMessage.id : undefined;
    } else {
      const threadInfo = selectThreadInfo(global, chatId, threadId);

      messageId = threadInfo ? threadInfo.lastMessageId : undefined;
    }
  } else {
    const chatMessageKeys = Object.keys(chatMessages);
    const indexOfCurrent = chatMessageKeys.indexOf(replyingToId.toString());
    const newIndex = indexOfCurrent + targetIndexDelta;
    messageId = newIndex <= chatMessageKeys.length + 1 && newIndex >= 0
      ? Number(chatMessageKeys[newIndex])
      : undefined;
  }
  actions.setReplyingToId({ messageId });
  actions.focusMessage({
    chatId, threadId, messageId,
  });
});

addReducer('openMediaViewer', (global, actions, payload) => {
  const {
    chatId, threadId, messageId, avatarOwnerId, profilePhotoIndex, origin,
  } = payload!;

  return {
    ...global,
    mediaViewer: {
      chatId,
      threadId,
      messageId,
      avatarOwnerId,
      profilePhotoIndex,
      origin,
    },
    forwardMessages: {},
  };
});

addReducer('closeMediaViewer', (global) => {
  return {
    ...global,
    mediaViewer: {},
  };
});

addReducer('openAudioPlayer', (global, actions, payload) => {
  const {
    chatId, threadId, messageId,
  } = payload!;

  return {
    ...global,
    audioPlayer: {
      chatId,
      threadId,
      messageId,
    },
  };
});

addReducer('closeAudioPlayer', (global) => {
  return {
    ...global,
    audioPlayer: {},
  };
});

addReducer('openPollResults', (global, actions, payload) => {
  const { chatId, messageId } = payload!;

  const shouldOpenInstantly = selectIsRightColumnShown(global);

  if (!shouldOpenInstantly) {
    window.setTimeout(() => {
      const newGlobal = getGlobal();

      setGlobal({
        ...newGlobal,
        pollResults: {
          chatId,
          messageId,
          voters: {},
        },
      });
    }, POLL_RESULT_OPEN_DELAY_MS);
  } else if (chatId !== global.pollResults.chatId || messageId !== global.pollResults.messageId) {
    setGlobal({
      ...global,
      pollResults: {
        chatId,
        messageId,
        voters: {},
      },
    });
  }
});

addReducer('closePollResults', (global) => {
  setGlobal({
    ...global,
    pollResults: {},
  });
});

addReducer('focusLastMessage', (global, actions) => {
  const currentMessageList = selectCurrentMessageList(global);
  if (!currentMessageList) {
    return;
  }

  const { chatId, threadId } = currentMessageList;

  let lastMessageId: number | undefined;
  if (threadId === MAIN_THREAD_ID) {
    const chat = selectChat(global, chatId);

    lastMessageId = chat && chat.lastMessage ? chat.lastMessage.id : undefined;
  } else {
    const threadInfo = selectThreadInfo(global, chatId, threadId);

    lastMessageId = threadInfo ? threadInfo.lastMessageId : undefined;
  }

  if (!lastMessageId) {
    return;
  }

  actions.focusMessage({
    chatId, threadId, messageId: lastMessageId, noHighlight: true,
  });
});

addReducer('focusMessage', (global, actions, payload) => {
  const {
    chatId, threadId = MAIN_THREAD_ID, messageListType = 'thread', noHighlight, groupedId, groupedChatId,
  } = payload!;

  let { messageId } = payload!;

  if (groupedId !== undefined) {
    const ids = selectForwardedMessageIdsByGroupId(global, groupedChatId, groupedId);
    if (ids && ids.length) {
      ([messageId] = ids);
    }
  }

  const currentMessageList = selectCurrentMessageList(global);
  const shouldSwitchChat = !currentMessageList || (
    chatId !== currentMessageList.chatId
    || threadId !== currentMessageList.threadId
    || messageListType !== currentMessageList.type
  );

  if (blurTimeout) {
    clearTimeout(blurTimeout);
    blurTimeout = undefined;
  }
  blurTimeout = window.setTimeout(() => {
    let newGlobal = getGlobal();
    newGlobal = updateFocusedMessage(newGlobal);
    newGlobal = updateFocusDirection(newGlobal);
    setGlobal(newGlobal);
  }, FOCUS_DURATION);

  global = updateFocusedMessage(global, chatId, messageId, noHighlight);
  global = updateFocusDirection(global, undefined);

  if (shouldSwitchChat) {
    global = updateFocusDirection(global, FocusDirection.Static);
  }

  const viewportIds = selectViewportIds(global, chatId, threadId);
  if (viewportIds && viewportIds.includes(messageId)) {
    setGlobal(global);
    actions.openChat({ id: chatId, threadId });
    return undefined;
  }

  if (shouldSwitchChat) {
    global = replaceThreadParam(global, chatId, threadId, 'viewportIds', undefined);
  }

  global = replaceThreadParam(global, chatId, threadId, 'outlyingIds', undefined);

  if (viewportIds && !shouldSwitchChat) {
    const direction = messageId > viewportIds[0] ? FocusDirection.Down : FocusDirection.Up;
    global = updateFocusDirection(global, direction);
  }

  setGlobal(global);

  actions.openChat({ id: chatId, threadId });
  actions.loadViewportMessages();
  return undefined;
});

addReducer('openForwardMenu', (global, actions, payload) => {
  const { fromChatId, messageIds, groupedId } = payload!;
  let groupedMessageIds;
  if (groupedId) {
    groupedMessageIds = selectMessageIdsByGroupId(global, fromChatId, groupedId);
  }
  return {
    ...global,
    forwardMessages: {
      fromChatId,
      messageIds: groupedMessageIds || messageIds,
      isModalShown: true,
    },
  };
});

addReducer('exitForwardMode', (global) => {
  setGlobal({
    ...global,
    forwardMessages: {},
  });
});

addReducer('setForwardChatId', (global, actions, payload) => {
  const { id } = payload!;

  setGlobal({
    ...global,
    forwardMessages: {
      ...global.forwardMessages,
      toChatId: id,
      isModalShown: false,
    },
  });

  actions.openChat({ id });
  actions.closeMediaViewer();
  actions.exitMessageSelectMode();
});

addReducer('openForwardMenuForSelectedMessages', (global, actions) => {
  if (!global.selectedMessages) {
    return;
  }

  const { chatId: fromChatId, messageIds } = global.selectedMessages;

  actions.openForwardMenu({ fromChatId, messageIds });
});

addReducer('enterMessageSelectMode', (global, actions, payload) => {
  const { messageId } = payload || {};
  const openChat = selectCurrentChat(global);
  if (!openChat) {
    return global;
  }

  return enterMessageSelectMode(global, openChat.id, messageId);
});

addReducer('toggleMessageSelection', (global, actions, payload) => {
  const {
    messageId,
    groupedId,
    childMessageIds,
    withShift,
  } = payload!;
  const currentMessageList = selectCurrentMessageList(global);
  if (!currentMessageList) {
    return;
  }

  const { chatId, threadId, type: messageListType } = currentMessageList;

  global = toggleMessageSelection(
    global, chatId, threadId, messageListType, messageId, groupedId, childMessageIds, withShift,
  );

  setGlobal(global);

  if (global.shouldShowContextMenuHint) {
    actions.disableContextMenuHint();
    actions.showNotification({
      // eslint-disable-next-line max-len
      message: `To **edit** or **reply**, close this menu. Then ${IS_TOUCH_ENV ? 'long tap' : 'right click'} on a message.`,
    });
  }
});

addReducer('disableContextMenuHint', (global) => {
  if (!global.shouldShowContextMenuHint) {
    return undefined;
  }

  return {
    ...global,
    shouldShowContextMenuHint: false,
  };
});

addReducer('exitMessageSelectMode', exitMessageSelectMode);

addReducer('openPollModal', (global) => {
  return {
    ...global,
    isPollModalOpen: true,
  };
});

addReducer('closePollModal', (global) => {
  return {
    ...global,
    isPollModalOpen: false,
  };
});
