<script>
  import $ from 'jquery';
  import { mapActions, mapGetters, mapState } from 'vuex';
  import _ from 'underscore';
  import Autosize from 'autosize';
  import { __, sprintf } from '~/locale';
  import Flash from '../../flash';
  import Autosave from '../../autosave';
  import TaskList from '../../task_list';
  import { capitalizeFirstCharacter, convertToCamelCase } from '../../lib/utils/text_utility';
  import * as constants from '../constants';
  import eventHub from '../event_hub';
  import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
  import markdownField from '../../vue_shared/components/markdown/field.vue';
  import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
  import loadingButton from '../../vue_shared/components/loading_button.vue';
  import noteSignedOutWidget from './note_signed_out_widget.vue';
  import discussionLockedWidget from './discussion_locked_widget.vue';
  import issuableStateMixin from '../mixins/issuable_state';

  export default {
    name: 'CommentForm',
    components: {
      issueWarning,
      noteSignedOutWidget,
      discussionLockedWidget,
      markdownField,
      userAvatarLink,
      loadingButton,
    },
    mixins: [
      issuableStateMixin,
    ],
    props: {
      noteableType: {
        type: String,
        required: true,
      },
    },
    data() {
      return {
        note: '',
        noteType: constants.COMMENT,
        isSubmitting: false,
        isSubmitButtonDisabled: true,
      };
    },
    computed: {
      ...mapGetters([
        'getCurrentUserLastNote',
        'getUserData',
        'getNoteableData',
        'getNotesData',
        'openState',
      ]),
      ...mapState([
        'isToggleStateButtonLoading',
      ]),
      noteableDisplayName() {
        return this.noteableType.replace(/_/g, ' ');
      },
      isLoggedIn() {
        return this.getUserData.id;
      },
      commentButtonTitle() {
        return this.noteType === constants.COMMENT ? 'Comment' : 'Start discussion';
      },
      isOpen() {
        return this.openState === constants.OPENED || this.openState === constants.REOPENED;
      },
      canCreateNote() {
        return this.getNoteableData.current_user.can_create_note;
      },
      issueActionButtonTitle() {
        const openOrClose = this.isOpen ? 'close' : 'reopen';

        if (this.note.length) {
          return sprintf(
            __('%{actionText} & %{openOrClose} %{noteable}'),
            {
              actionText: this.commentButtonTitle,
              openOrClose,
              noteable: this.noteableDisplayName,
            },
          );
        }

        return sprintf(
          __('%{openOrClose} %{noteable}'),
          {
            openOrClose: capitalizeFirstCharacter(openOrClose),
            noteable: this.noteableDisplayName,
          },
        );
      },
      actionButtonClassNames() {
        return {
          'btn-reopen': !this.isOpen,
          'btn-close': this.isOpen,
          'js-note-target-close': this.isOpen,
          'js-note-target-reopen': !this.isOpen,
        };
      },
      markdownDocsPath() {
        return this.getNotesData.markdownDocsPath;
      },
      quickActionsDocsPath() {
        return this.getNotesData.quickActionsDocsPath;
      },
      markdownPreviewPath() {
        return this.getNoteableData.preview_note_path;
      },
      author() {
        return this.getUserData;
      },
      canUpdateIssue() {
        return this.getNoteableData.current_user.can_update;
      },
      endpoint() {
        return this.getNoteableData.create_note_path;
      },
    },
    watch: {
      note(newNote) {
        this.setIsSubmitButtonDisabled(newNote, this.isSubmitting);
      },
      isSubmitting(newValue) {
        this.setIsSubmitButtonDisabled(this.note, newValue);
      },
    },
    mounted() {
      // jQuery is needed here because it is a custom event being dispatched with jQuery.
      $(document).on('issuable:change', (e, isClosed) => {
        this.toggleIssueLocalState(isClosed ? constants.CLOSED : constants.REOPENED);
      });

      this.initAutoSave();
      this.initTaskList();
    },
    methods: {
      ...mapActions([
        'saveNote',
        'stopPolling',
        'restartPolling',
        'removePlaceholderNotes',
        'closeIssue',
        'reopenIssue',
        'toggleIssueLocalState',
        'toggleStateButtonLoading',
      ]),
      setIsSubmitButtonDisabled(note, isSubmitting) {
        if (!_.isEmpty(note) && !isSubmitting) {
          this.isSubmitButtonDisabled = false;
        } else {
          this.isSubmitButtonDisabled = true;
        }
      },
      handleSave(withIssueAction) {
        this.isSubmitting = true;

        if (this.note.length) {
          const noteData = {
            endpoint: this.endpoint,
            flashContainer: this.$el,
            data: {
              note: {
                noteable_type: this.noteableType,
                noteable_id: this.getNoteableData.id,
                note: this.note,
              },
            },
          };

          if (this.noteType === constants.DISCUSSION) {
            noteData.data.note.type = constants.DISCUSSION_NOTE;
          }

          this.note = ''; // Empty textarea while being requested. Repopulate in catch
          this.resizeTextarea();
          this.stopPolling();

          this.saveNote(noteData)
            .then((res) => {
              this.enableButton();
              this.restartPolling();

              if (res.errors) {
                if (res.errors.commands_only) {
                  this.discard();
                } else {
                  Flash(
                    'Something went wrong while adding your comment. Please try again.',
                    'alert',
                    this.$refs.commentForm,
                  );
                }
              } else {
                this.discard();
              }

              if (withIssueAction) {
                this.toggleIssueState();
              }
            })
            .catch(() => {
              this.enableButton();
              this.discard(false);
              const msg =
                `Your comment could not be submitted!
Please check your network connection and try again.`;
              Flash(msg, 'alert', this.$el);
              this.note = noteData.data.note.note; // Restore textarea content.
              this.removePlaceholderNotes();
            });
        } else {
          this.toggleIssueState();
        }
      },
      enableButton() {
        this.isSubmitting = false;
      },
      toggleIssueState() {
        if (this.isOpen) {
          this.closeIssue()
            .then(() => this.enableButton())
            .catch(() => {
              this.enableButton();
              this.toggleStateButtonLoading(false);
              Flash(
                sprintf(
                  __('Something went wrong while closing the %{issuable}. Please try again later'),
                  { issuable: this.noteableDisplayName },
                ),
              );
            });
        } else {
          this.reopenIssue()
            .then(() => this.enableButton())
            .catch(() => {
              this.enableButton();
              this.toggleStateButtonLoading(false);
              Flash(
                sprintf(
                  __('Something went wrong while reopening the %{issuable}. Please try again later'),
                  { issuable: this.noteableDisplayName },
                ),
              );
            });
        }
      },
      discard(shouldClear = true) {
        // `blur` is needed to clear slash commands autocomplete cache if event fired.
        // `focus` is needed to remain cursor in the textarea.
        this.$refs.textarea.blur();
        this.$refs.textarea.focus();

        if (shouldClear) {
          this.note = '';
          this.resizeTextarea();
          this.$refs.markdownField.previewMarkdown = false;
        }

        this.autosave.reset();
      },
      setNoteType(type) {
        this.noteType = type;
      },
      editCurrentUserLastNote() {
        if (this.note === '') {
          const lastNote = this.getCurrentUserLastNote;

          if (lastNote) {
            eventHub.$emit('enterEditMode', {
              noteId: lastNote.id,
            });
          }
        }
      },
      initAutoSave() {
        if (this.isLoggedIn) {
          const noteableType = capitalizeFirstCharacter(convertToCamelCase(this.noteableType));

          this.autosave = new Autosave(
            $(this.$refs.textarea),
            ['Note', noteableType, this.getNoteableData.id],
          );
        }
      },
      initTaskList() {
        return new TaskList({
          dataType: 'note',
          fieldName: 'note',
          selector: '.notes',
        });
      },
      resizeTextarea() {
        this.$nextTick(() => {
          Autosize.update(this.$refs.textarea);
        });
      },
    },
  };
</script>

<template>
  <div>
    <note-signed-out-widget v-if="!isLoggedIn" />
    <discussion-locked-widget
      issuable-type="issue"
      v-else-if="!canCreateNote"
    />
    <ul
      v-else
      class="notes notes-form timeline">
      <li class="timeline-entry">
        <div class="timeline-entry-inner">
          <div class="flash-container error-alert timeline-content"></div>
          <div class="timeline-icon hidden-xs hidden-sm">
            <user-avatar-link
              v-if="author"
              :link-href="author.path"
              :img-src="author.avatar_url"
              :img-alt="author.name"
              :img-size="40"
            />
          </div>
          <div class="timeline-content timeline-content-form">
            <form
              ref="commentForm"
              class="new-note common-note-form gfm-form js-main-target-form"
            >

              <div class="error-alert"></div>

              <issue-warning
                v-if="hasWarning(getNoteableData)"
                :is-locked="isLocked(getNoteableData)"
                :is-confidential="isConfidential(getNoteableData)"
              />

              <markdown-field
                :markdown-preview-path="markdownPreviewPath"
                :markdown-docs-path="markdownDocsPath"
                :quick-actions-docs-path="quickActionsDocsPath"
                :add-spacing-classes="false"
                ref="markdownField">
                <textarea
                  id="note-body"
                  name="note[note]"
                  class="note-textarea js-vue-comment-form
js-gfm-input js-autosize markdown-area js-vue-textarea"
                  data-supports-quick-actions="true"
                  aria-label="Description"
                  v-model="note"
                  ref="textarea"
                  slot="textarea"
                  :disabled="isSubmitting"
                  placeholder="Write a comment or drag your files here..."
                  @keydown.up="editCurrentUserLastNote()"
                  @keydown.meta.enter="handleSave()"
                  @keydown.ctrl.enter="handleSave()">
                </textarea>
              </markdown-field>
              <div class="note-form-actions">
                <div
                  class="pull-left btn-group
append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown">
                  <button
                    @click.prevent="handleSave()"
                    :disabled="isSubmitButtonDisabled"
                    class="btn btn-create comment-btn js-comment-button js-comment-submit-button"
                    type="submit">
                    {{ __(commentButtonTitle) }}
                  </button>
                  <button
                    :disabled="isSubmitButtonDisabled"
                    name="button"
                    type="button"
                    class="btn comment-btn note-type-toggle js-note-new-discussion dropdown-toggle"
                    data-toggle="dropdown"
                    aria-label="Open comment type dropdown">
                    <i
                      aria-hidden="true"
                      class="fa fa-caret-down toggle-icon">
                    </i>
                  </button>

                  <ul class="note-type-dropdown dropdown-open-top dropdown-menu">
                    <li :class="{ 'droplab-item-selected': noteType === 'comment' }">
                      <button
                        type="button"
                        class="btn btn-transparent"
                        @click.prevent="setNoteType('comment')">
                        <i
                          aria-hidden="true"
                          class="fa fa-check icon">
                        </i>
                        <div class="description">
                          <strong>Comment</strong>
                          <p>
                            Add a general comment to this {{ noteableDisplayName }}.
                          </p>
                        </div>
                      </button>
                    </li>
                    <li class="divider droplab-item-ignore"></li>
                    <li :class="{ 'droplab-item-selected': noteType === 'discussion' }">
                      <button
                        type="button"
                        class="btn btn-transparent"
                        @click.prevent="setNoteType('discussion')">
                        <i
                          aria-hidden="true"
                          class="fa fa-check icon">
                        </i>
                        <div class="description">
                          <strong>Start discussion</strong>
                          <p>
                            Discuss a specific suggestion or question.
                          </p>
                        </div>
                      </button>
                    </li>
                  </ul>
                </div>

                <loading-button
                  v-if="canUpdateIssue"
                  :loading="isToggleStateButtonLoading"
                  @click="handleSave(true)"
                  :container-class="[
                    actionButtonClassNames,
                    'btn btn-comment btn-comment-and-close js-action-button'
                  ]"
                  :disabled="isToggleStateButtonLoading || isSubmitting"
                  :label="issueActionButtonTitle"
                />

                <button
                  type="button"
                  v-if="note.length"
                  @click="discard"
                  class="btn btn-cancel js-note-discard">
                  Discard draft
                </button>
              </div>
            </form>
          </div>
        </div>
      </li>
    </ul>
  </div>
</template>
