text: import { isEqual } from 'lodash';
  import { isInMRPage } from '~/lib/utils/common_utils';
  import * as constants from '../constants';
  import * as types from './mutation_types';
  import * as utils from './utils';
  
  export default {
    [types.ADD_NEW_NOTE](state, data) {
      const note = data.discussion ? data.discussion.notes[0] : data;
      const { discussion_id, type } = note;
      const [exists] = state.discussions.filter((n) => n.id === note.discussion_id);
      const isDiscussion = type === constants.DISCUSSION_NOTE || type === constants.DIFF_NOTE;
  
      if (!exists) {
        let discussion = data.discussion || note.base_discussion;
  
        if (!discussion) {
          discussion = {
            expanded: true,
            id: discussion_id,
            individual_note: !isDiscussion,
            reply_id: discussion_id,
          };
  
          if (isDiscussion && isInMRPage()) {
            discussion.resolvable = note.resolvable;
            discussion.resolved = false;
            discussion.active = true;
            discussion.resolve_path = note.resolve_path;
            discussion.resolve_with_issue_path = note.resolve_with_issue_path;
            discussion.diff_discussion = false;
          }
        }
  
        if (window.gon?.features?.paginatedNotes && note.base_discussion) {
          if (discussion.diff_file) {
            discussion.file_hash = discussion.diff_file.file_hash;
  
            discussion.truncated_diff_lines = utils.prepareDiffLines(
              discussion.truncated_diff_lines || [],
            );
          }
  
          discussion.resolvable = note.resolvable;
          discussion.expanded = note.base_discussion.expanded;
          discussion.resolved = note.resolved;
        }
  
        // note.base_discussion = undefined; // No point keeping a reference to this
        delete note.base_discussion;
        discussion.notes = [note];
  
        state.discussions.push(discussion);
      }
    },
  
    [types.ADD_NEW_REPLY_TO_DISCUSSION](state, note) {
      const discussion = utils.findNoteObjectById(state.discussions, note.discussion_id);
      const existingNote = discussion && utils.findNoteObjectById(discussion.notes, note.id);
  
      if (discussion && !existingNote) {
        discussion.notes.push(note);
      }
    },
  
    [types.DELETE_NOTE](state, note) {
      const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id);
  
      if (noteObj.individual_note) {
        state.discussions.splice(state.discussions.indexOf(noteObj), 1);
      } else {
        const comment = utils.findNoteObjectById(noteObj.notes, note.id);
        noteObj.notes.splice(noteObj.notes.indexOf(comment), 1);
  
        if (!noteObj.notes.length) {
          state.discussions.splice(state.discussions.indexOf(noteObj), 1);
        }
      }
    },
  
    [types.EXPAND_DISCUSSION](state, { discussionId }) {
      const discussion = utils.findNoteObjectById(state.discussions, discussionId);
      Object.assign(discussion, { expanded: true });
    },
  
    [types.COLLAPSE_DISCUSSION](state, { discussionId }) {
      const discussion = utils.findNoteObjectById(state.discussions, discussionId);
      Object.assign(discussion, { expanded: false });
    },
  
    [types.REMOVE_PLACEHOLDER_NOTES](state) {
      const { discussions } = state;
  
      for (let i = discussions.length - 1; i >= 0; i -= 1) {
        const note = discussions[i];
        const children = note.notes;
  
        if (children.length && !note.individual_note) {
          // remove placeholder from discussions
          for (let j = children.length - 1; j >= 0; j -= 1) {
            if (children[j].isPlaceholderNote) {
              children.splice(j, 1);
            }
          }
        } else if (note.isPlaceholderNote) {
          // remove placeholders from state root
          discussions.splice(i, 1);
        }
      }
    },
  
    [types.SET_NOTES_DATA](state, data) {
      Object.assign(state, { notesData: data });
    },
  
    [types.SET_NOTEABLE_DATA](state, data) {
      Object.assign(state, { noteableData: data });
    },
  
    [types.SET_ISSUE_CONFIDENTIAL](state, data) {
      state.noteableData.confidential = data;
    },
  
    [types.SET_ISSUABLE_LOCK](state, locked) {
      state.noteableData.discussion_locked = locked;
    },
  
    [types.SET_USER_DATA](state, data) {
      Object.assign(state, { userData: data });
    },
  
    [types.CLEAR_DISCUSSIONS](state) {
      state.discussions = [];
    },
  
    [types.ADD_OR_UPDATE_DISCUSSIONS](state, discussionsData) {
      discussionsData.forEach((d) => {
        const discussion = { ...d };
        const diffData = {};
  
        if (discussion.diff_file) {
          diffData.file_hash = discussion.diff_file.file_hash;
  
          diffData.truncated_diff_lines = utils.prepareDiffLines(
            discussion.truncated_diff_lines || [],
          );
        }
  
        // To support legacy notes, should be very rare case.
        if (discussion.individual_note && discussion.notes.length > 1) {
          discussion.notes.forEach((n) => {
            const newDiscussion = {
              ...discussion,
              ...diffData,
              notes: [n], // override notes array to only have one item to mimick individual_note
            };
            const oldDiscussion = state.discussions.find(
              (existingDiscussion) =>
                existingDiscussion.id === discussion.id && existingDiscussion.notes[0].id === n.id,
            );
  
            if (oldDiscussion) {
              state.discussions.splice(state.discussions.indexOf(oldDiscussion), 1, newDiscussion);
            } else {
              state.discussions.push(newDiscussion);
            }
          });
        } else {
          const oldDiscussion = utils.findNoteObjectById(state.discussions, discussion.id);
  
          if (oldDiscussion) {
            state.discussions.splice(state.discussions.indexOf(oldDiscussion), 1, {
              ...discussion,
              ...diffData,
              expanded: oldDiscussion.expanded,
            });
          } else {
            state.discussions.push({ ...discussion, ...diffData });
          }
        }
      });
    },
  
    [types.SET_LAST_FETCHED_AT](state, fetchedAt) {
      Object.assign(state, { lastFetchedAt: fetchedAt });
    },
  
    [types.SET_TARGET_NOTE_HASH](state, hash) {
      Object.assign(state, { targetNoteHash: hash });
    },
  
    [types.SHOW_PLACEHOLDER_NOTE](state, data) {
      let notesArr = state.discussions;
  
      const existingDiscussion = utils.findNoteObjectById(notesArr, data.replyId);
      if (existingDiscussion) {
        notesArr = existingDiscussion.notes;
      }
  
      notesArr.push({
        individual_note: true,
        isPlaceholderNote: true,
        placeholderType: data.isSystemNote ? constants.SYSTEM_NOTE : constants.NOTE,
        notes: [
          {
            body: data.noteBody,
          },
        ],
      });
    },
  
    [types.TOGGLE_AWARD](state, data) {
      const { awardName, note } = data;
      const { id, name, username } = state.userData;
  
      const hasEmojiAwardedByCurrentUser = note.award_emoji.filter(
        (emoji) => `${emoji.name}` === `${data.awardName}` && emoji.user.id === id,
      );
  
      if (hasEmojiAwardedByCurrentUser.length) {
        // If current user has awarded this emoji, remove it.
        note.award_emoji.splice(note.award_emoji.indexOf(hasEmojiAwardedByCurrentUser[0]), 1);
      } else {
        note.award_emoji.push({
          name: awardName,
          user: { id, name, username },
        });
      }
    },
  
    [types.TOGGLE_DISCUSSION](state, { discussionId, forceExpanded = null }) {
      const discussion = utils.findNoteObjectById(state.discussions, discussionId);
      Object.assign(discussion, {
        expanded: forceExpanded === null ? !discussion.expanded : forceExpanded,
      });
    },
  
    [types.SET_EXPAND_DISCUSSIONS](state, { discussionIds, expanded }) {
      if (discussionIds?.length) {
        discussionIds.forEach((discussionId) => {
          const discussion = utils.findNoteObjectById(state.discussions, discussionId);
          Object.assign(discussion, { expanded });
        });
      }
    },
  
    [types.SET_RESOLVING_DISCUSSION](state, isResolving) {
      state.isResolvingDiscussion = isResolving;
    },
  
    [types.UPDATE_NOTE](state, note) {
      const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id);
  
      // Disable eslint here so we can delete the property that we no longer need
      // in the note object
      // eslint-disable-next-line no-param-reassign
      delete note.base_discussion;
  
      if (noteObj.individual_note) {
        if (note.type === constants.DISCUSSION_NOTE) {
          noteObj.individual_note = false;
        }
  
        noteObj.notes.splice(0, 1, note);
      } else {
        const comment = utils.findNoteObjectById(noteObj.notes, note.id);
  
        if (!isEqual(comment, note)) {
          noteObj.notes.splice(noteObj.notes.indexOf(comment), 1, note);
        }
      }
    },
  
    [types.APPLY_SUGGESTION](state, { noteId, discussionId, suggestionId }) {
      const noteObj = utils.findNoteObjectById(state.discussions, discussionId);
      const comment = utils.findNoteObjectById(noteObj.notes, noteId);
  
      comment.suggestions = comment.suggestions.map((suggestion) => ({
        ...suggestion,
        applied: suggestion.applied || suggestion.id === suggestionId,
        appliable: false,
      }));
    },
  
    [types.SET_APPLYING_BATCH_STATE](state, isApplyingBatch) {
      state.batchSuggestionsInfo.forEach((suggestionInfo) => {
        const { discussionId, noteId, suggestionId } = suggestionInfo;
  
        const noteObj = utils.findNoteObjectById(state.discussions, discussionId);
        const comment = utils.findNoteObjectById(noteObj.notes, noteId);
  
        comment.suggestions = comment.suggestions.map((suggestion) => ({
          ...suggestion,
          is_applying_batch: suggestion.id === suggestionId && isApplyingBatch,
        }));
      });
    },
  
    [types.ADD_SUGGESTION_TO_BATCH](state, { noteId, discussionId, suggestionId }) {
      state.batchSuggestionsInfo.push({
        suggestionId,
        noteId,
        discussionId,
      });
    },
  
    [types.REMOVE_SUGGESTION_FROM_BATCH](state, id) {
      const index = state.batchSuggestionsInfo.findIndex(({ suggestionId }) => suggestionId === id);
      if (index !== -1) {
        state.batchSuggestionsInfo.splice(index, 1);
      }
    },
  
    [types.CLEAR_SUGGESTION_BATCH](state) {
      state.batchSuggestionsInfo.splice(0, state.batchSuggestionsInfo.length);
    },
  
    [types.UPDATE_DISCUSSION](state, noteData) {
      const note = noteData;
      const selectedDiscussion = state.discussions.find((disc) => disc.id === note.id);
      note.expanded = true; // override expand flag to prevent collapse
      if (note.diff_file) {
        Object.assign(note, {
          file_hash: note.diff_file.file_hash,
        });
      }
      Object.assign(selectedDiscussion, { ...note });
    },
  
    [types.UPDATE_DISCUSSION_POSITION](state, { discussionId, position }) {
      const selectedDiscussion = state.discussions.find((disc) => disc.id === discussionId);
      if (selectedDiscussion) Object.assign(selectedDiscussion.position, { ...position });
    },
  
    [types.CLOSE_ISSUE](state) {
      Object.assign(state.noteableData, { state: constants.CLOSED });
    },
  
    [types.REOPEN_ISSUE](state) {
      Object.assign(state.noteableData, { state: constants.REOPENED });
    },
  
    [types.TOGGLE_STATE_BUTTON_LOADING](state, value) {
      Object.assign(state, { isToggleStateButtonLoading: value });
    },
  
    [types.SET_NOTES_FETCHED_STATE](state, value) {
      Object.assign(state, { isNotesFetched: value });
    },
  
    [types.SET_NOTES_LOADING_STATE](state, value) {
      state.isLoading = value;
    },
  
    [types.SET_NOTES_FETCHING_STATE](state, value) {
      state.isFetching = value;
    },
  
    [types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) {
      const discussion = utils.findNoteObjectById(state.discussions, discussionId);
  
      discussion.truncated_diff_lines = utils.prepareDiffLines(diffLines);
    },
  
    [types.SET_DISCUSSIONS_SORT](state, { direction, persist }) {
      state.discussionSortOrder = direction;
      state.persistSortOrder = persist;
    },
  
    [types.SET_TIMELINE_VIEW](state, value) {
      state.isTimelineEnabled = value;
    },
  
    [types.SET_SELECTED_COMMENT_POSITION](state, position) {
      state.selectedCommentPosition = position;
    },
  
    [types.SET_SELECTED_COMMENT_POSITION_HOVER](state, position) {
      state.selectedCommentPositionHover = position;
    },
  
    [types.DISABLE_COMMENTS](state, value) {
      state.commentsDisabled = value;
    },
    [types.UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS](state) {
      state.resolvableDiscussionsCount = state.discussions.filter(
        (discussion) => !discussion.individual_note && discussion.resolvable,
      ).length;
      state.unresolvedDiscussionsCount = state.discussions.filter(
        (discussion) =>
          !discussion.individual_note &&
          discussion.resolvable &&
          discussion.notes.some((note) => note.resolvable && !note.resolved),
      ).length;
    },
  
    [types.CONVERT_TO_DISCUSSION](state, discussionId) {
      const convertedDisscussionIds = [...state.convertedDisscussionIds, discussionId];
      Object.assign(state, { convertedDisscussionIds });
    },
  
    [types.REMOVE_CONVERTED_DISCUSSION](state, discussionId) {
      const convertedDisscussionIds = [...state.convertedDisscussionIds];
  
      convertedDisscussionIds.splice(convertedDisscussionIds.indexOf(discussionId), 1);
      Object.assign(state, { convertedDisscussionIds });
    },
  
    [types.SET_CURRENT_DISCUSSION_ID](state, discussionId) {
      state.currentDiscussionId = discussionId;
    },
  
    [types.REQUEST_DESCRIPTION_VERSION](state) {
      state.isLoadingDescriptionVersion = true;
    },
    [types.RECEIVE_DESCRIPTION_VERSION](state, { descriptionVersion, versionId }) {
      const descriptionVersions = { ...state.descriptionVersions, [versionId]: descriptionVersion };
      Object.assign(state, { descriptionVersions, isLoadingDescriptionVersion: false });
    },
    [types.RECEIVE_DESCRIPTION_VERSION_ERROR](state) {
      state.isLoadingDescriptionVersion = false;
    },
    [types.REQUEST_DELETE_DESCRIPTION_VERSION](state) {
      state.isLoadingDescriptionVersion = true;
    },
    [types.RECEIVE_DELETE_DESCRIPTION_VERSION](state, descriptionVersion) {
      state.isLoadingDescriptionVersion = false;
      Object.assign(state.descriptionVersions, descriptionVersion);
    },
    [types.RECEIVE_DELETE_DESCRIPTION_VERSION_ERROR](state) {
      state.isLoadingDescriptionVersion = false;
    },
    [types.UPDATE_ASSIGNEES](state, assignees) {
      state.noteableData.assignees = assignees;
    },
    [types.SET_FETCHING_DISCUSSIONS](state, value) {
      state.currentlyFetchingDiscussions = value;
    },
  };
  