<script>
import { GlTooltipDirective, GlIcon, GlLoadingIcon, GlLink } from '@gitlab/ui';
import { ApolloMutation } from 'vue-apollo';
import createFlash from '~/flash';
import { s__ } from '~/locale';
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
import { updateGlobalTodoCount } from '~/vue_shared/components/sidebar/todo_toggle/utils';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import DesignNotePin from '~/vue_shared/components/design_management/design_note_pin.vue';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { ACTIVE_DISCUSSION_SOURCE_TYPES } from '../../constants';
import createNoteMutation from '../../graphql/mutations/create_note.mutation.graphql';
import toggleResolveDiscussionMutation from '../../graphql/mutations/toggle_resolve_discussion.mutation.graphql';
import activeDiscussionQuery from '../../graphql/queries/active_discussion.query.graphql';
import getDesignQuery from '../../graphql/queries/get_design.query.graphql';
import allVersionsMixin from '../../mixins/all_versions';
import { hasErrors } from '../../utils/cache_update';
import { extractDesign } from '../../utils/design_management_utils';
import { ADD_DISCUSSION_COMMENT_ERROR } from '../../utils/error_messages';
import DesignNote from './design_note.vue';
import DesignNoteSignedOut from './design_note_signed_out.vue';
import DesignReplyForm from './design_reply_form.vue';
import ToggleRepliesWidget from './toggle_replies_widget.vue';

export default {
  components: {
    ApolloMutation,
    DesignNote,
    DesignNoteSignedOut,
    ReplyPlaceholder,
    DesignReplyForm,
    GlIcon,
    GlLoadingIcon,
    GlLink,
    ToggleRepliesWidget,
    TimeAgoTooltip,
    DesignNotePin,
  },
  directives: {
    GlTooltip: GlTooltipDirective,
  },
  mixins: [allVersionsMixin],
  props: {
    discussion: {
      type: Object,
      required: true,
    },
    noteableId: {
      type: String,
      required: true,
    },
    designId: {
      type: String,
      required: true,
    },
    markdownPreviewPath: {
      type: String,
      required: false,
      default: '',
    },
    registerPath: {
      type: String,
      required: true,
    },
    signInPath: {
      type: String,
      required: true,
    },
    resolvedDiscussionsExpanded: {
      type: Boolean,
      required: true,
    },
    discussionWithOpenForm: {
      type: String,
      required: true,
    },
  },
  apollo: {
    activeDiscussion: {
      query: activeDiscussionQuery,
      result({ data }) {
        if (this.discussion.resolved && !this.resolvedDiscussionsExpanded) {
          return;
        }

        this.$nextTick(() => {
          // We watch any changes to the active discussion from the design pins and scroll to this discussion if it exists.
          // We don't want scrollIntoView to be triggered from the discussion click itself.
          if (this.$el && this.shouldScrollToDiscussion(data.activeDiscussion)) {
            this.$el.scrollIntoView({
              behavior: 'smooth',
              inline: 'start',
            });
          }
        });
      },
    },
  },
  data() {
    return {
      discussionComment: '',
      isFormRendered: false,
      activeDiscussion: {},
      isResolving: false,
      shouldChangeResolvedStatus: false,
      areRepliesCollapsed: this.discussion.resolved,
      isLoggedIn: isLoggedIn(),
    };
  },
  computed: {
    mutationPayload() {
      return {
        noteableId: this.noteableId,
        body: this.discussionComment,
        discussionId: this.discussion.id,
      };
    },
    designVariables() {
      return {
        fullPath: this.projectPath,
        iid: this.issueIid,
        filenames: [this.$route.params.id],
        atVersion: this.designsVersion,
      };
    },
    isDiscussionActive() {
      return this.discussion.notes.some(({ id }) => id === this.activeDiscussion.id);
    },
    resolveCheckboxText() {
      return this.discussion.resolved
        ? s__('DesignManagement|Unresolve thread')
        : s__('DesignManagement|Resolve thread');
    },
    firstNote() {
      return this.discussion.notes[0];
    },
    discussionReplies() {
      return this.discussion.notes.slice(1);
    },
    areRepliesShown() {
      return !this.discussion.resolved || !this.areRepliesCollapsed;
    },
    resolveIconName() {
      return this.discussion.resolved ? 'check-circle-filled' : 'check-circle';
    },
    isRepliesWidgetVisible() {
      return this.discussion.resolved && this.discussionReplies.length > 0;
    },
    isReplyPlaceholderVisible() {
      return this.areRepliesShown || !this.discussionReplies.length;
    },
    isFormVisible() {
      return this.isFormRendered && this.discussionWithOpenForm === this.discussion.id;
    },
  },
  methods: {
    onDone({ data: { createNote } }) {
      if (hasErrors(createNote)) {
        createFlash({ message: ADD_DISCUSSION_COMMENT_ERROR });
      }
      this.discussionComment = '';
      this.hideForm();
      if (this.shouldChangeResolvedStatus) {
        this.toggleResolvedStatus();
      }
    },
    onCreateNoteError(err) {
      this.$emit('create-note-error', err);
    },
    hideForm() {
      this.isFormRendered = false;
      this.discussionComment = '';
    },
    showForm() {
      this.$emit('open-form', this.discussion.id);
      this.isFormRendered = true;
    },
    toggleResolvedStatus() {
      this.isResolving = true;

      /**
       * Get previous todo count
       */
      const { defaultClient: client } = this.$apollo.provider.clients;
      const sourceData = client.readQuery({
        query: getDesignQuery,
        variables: this.designVariables,
      });

      const design = extractDesign(sourceData);
      const prevTodoCount = design.currentUserTodos?.nodes?.length || 0;

      this.$apollo
        .mutate({
          mutation: toggleResolveDiscussionMutation,
          variables: { id: this.discussion.id, resolve: !this.discussion.resolved },
        })
        .then(({ data }) => {
          if (data.errors?.length > 0) {
            this.$emit('resolve-discussion-error', data.errors[0]);
          }
          const newTodoCount =
            data?.discussionToggleResolve?.discussion?.noteable?.currentUserTodos?.nodes?.length ||
            0;
          updateGlobalTodoCount(newTodoCount - prevTodoCount);
        })
        .catch((err) => {
          this.$emit('resolve-discussion-error', err);
        })
        .finally(() => {
          this.isResolving = false;
        });
    },
    shouldScrollToDiscussion(activeDiscussion) {
      const ALLOWED_ACTIVE_DISCUSSION_SOURCES = [
        ACTIVE_DISCUSSION_SOURCE_TYPES.pin,
        ACTIVE_DISCUSSION_SOURCE_TYPES.url,
      ];
      const { source } = activeDiscussion;
      return ALLOWED_ACTIVE_DISCUSSION_SOURCES.includes(source) && this.isDiscussionActive;
    },
  },
  createNoteMutation,
};
</script>

<template>
  <div class="design-discussion-wrapper">
    <design-note-pin :is-resolved="discussion.resolved" :label="discussion.index" />
    <ul
      class="design-discussion bordered-box gl-relative gl-p-0 gl-list-style-none"
      data-qa-selector="design_discussion_content"
    >
      <design-note
        :note="firstNote"
        :markdown-preview-path="markdownPreviewPath"
        :is-resolving="isResolving"
        :class="{ 'gl-bg-blue-50': isDiscussionActive }"
        @error="$emit('update-note-error', $event)"
      >
        <template v-if="isLoggedIn && discussion.resolvable" #resolve-discussion>
          <button
            v-gl-tooltip
            :class="{ 'is-active': discussion.resolved }"
            :title="resolveCheckboxText"
            :aria-label="resolveCheckboxText"
            class="line-resolve-btn note-action-button gl-mr-3"
            data-testid="resolve-button"
            @click.stop="toggleResolvedStatus"
          >
            <gl-icon v-if="!isResolving" :name="resolveIconName" data-testid="resolve-icon" />
            <gl-loading-icon v-else size="sm" inline />
          </button>
        </template>
        <template v-if="discussion.resolved" #resolved-status>
          <p class="gl-text-gray-500 gl-font-sm gl-m-0 gl-mt-5" data-testid="resolved-message">
            {{ __('Resolved by') }}
            <gl-link
              class="gl-text-gray-500 gl-text-decoration-none gl-font-sm link-inherit-color"
              :href="discussion.resolvedBy.webUrl"
              target="_blank"
              >{{ discussion.resolvedBy.name }}</gl-link
            >
            <time-ago-tooltip :time="discussion.resolvedAt" tooltip-placement="bottom" />
          </p>
        </template>
      </design-note>
      <toggle-replies-widget
        v-if="isRepliesWidgetVisible"
        :collapsed="areRepliesCollapsed"
        :replies="discussionReplies"
        @toggle="areRepliesCollapsed = !areRepliesCollapsed"
      />
      <design-note
        v-for="note in discussionReplies"
        v-show="areRepliesShown"
        :key="note.id"
        :note="note"
        :markdown-preview-path="markdownPreviewPath"
        :is-resolving="isResolving"
        :class="{ 'gl-bg-blue-50': isDiscussionActive }"
        @error="$emit('update-note-error', $event)"
      />
      <li
        v-show="isReplyPlaceholderVisible"
        class="reply-wrapper discussion-reply-holder"
        :class="{ 'gl-bg-gray-10': !isLoggedIn }"
      >
        <template v-if="!isLoggedIn">
          <design-note-signed-out :register-path="registerPath" :sign-in-path="signInPath" />
        </template>
        <template v-else>
          <reply-placeholder
            v-if="!isFormVisible"
            class="qa-discussion-reply"
            :placeholder-text="__('Reply…')"
            @focus="showForm"
          />
          <apollo-mutation
            v-else
            #default="{ mutate, loading }"
            :mutation="$options.createNoteMutation"
            :variables="{
              input: mutationPayload,
            }"
            @done="onDone"
            @error="onCreateNoteError"
          >
            <design-reply-form
              v-model="discussionComment"
              :is-saving="loading"
              :markdown-preview-path="markdownPreviewPath"
              @submit-form="mutate"
              @cancel-form="hideForm"
            >
              <template v-if="discussion.resolvable" #resolve-checkbox>
                <label data-testid="resolve-checkbox">
                  <input v-model="shouldChangeResolvedStatus" type="checkbox" />
                  {{ resolveCheckboxText }}
                </label>
              </template>
            </design-reply-form>
          </apollo-mutation>
        </template>
      </li>
    </ul>
  </div>
</template>
