<template>
  <div :class="[{ 'has-label': showLabel }, 'comment-input-container']">
    <p v-if="showLabel">
      <strong>{{ label }}</strong>
    </p>
    <transition name="autosave-fade">
      <p
        v-if="timestamp"
        class="timestamp"
        v-text="`Autosaved at ${formattedTimestamp}`"
      />
    </transition>
    <ElForm>
      <div class="el-textarea">
        <!--
          We're using a textarea here to "solve" https://watchtower.atlassian.net/browse/SUP-166
        -->
        <textarea
          v-model="commentValue"
          :disabled="isSaving"
          data-test="edit comment"
          class="el-textarea__inner"
          placeholder="Enter your comment"
          rows="6"
        />
      </div>
    </ElForm>
    <div class="btn-group align-end">
      <TfButton
        type="secondary"
        size="medium"
        data-test="cancel comment"
        @click="onClickCancel"
      >
        Cancel
      </TfButton>
      <TfButton
        :disabled="!commentValue || disableSubmit"
        :loading="isSaving"
        data-test="send comment"
        type="primary"
        size="medium"
        @click="onClickSubmit"
      >
        Send
      </TfButton>
    </div>
    <p
      v-if="errorOnSubmit"
      class="text-error"
      v-text="errorOnSubmit"
    />
  </div>
</template>
<script>
/**
 * The Comments input component.
 *
 * @vuedoc
 * @exports Comments/Input
 * @category Components
 */

  export default {
    name: 'CommentInput',
    props: {
      applicationNamespace: {
        type: String,
        default: '',
        required: true,
      },
      autoSaveTimestamp: {
        type: String,
        default: null,
      },
      comment: {
        type: Object,
        default: () => ({}),
      },
      commentableIds: {
        type: Array,
        default: () => [],
      },
      customSessionName: {
        type: String,
        default: null,
      },
      disableSubmit: {
        type: Boolean,
        default: false,
      },
      inlineSuccess: {
        type: Boolean,
        default: true,
      },
      parentId: {
        type: Number,
        default: null,
      },
      projectId: {
        type: [Number, String],
        default: '',
        required: true,
      },
      proposalDocumentId: {
        type: Number,
        default: null,
      },
      saveSession: {
        type: Boolean,
        default: true,
      },
      showLabel: {
        type: Boolean,
        default: true,
      },
      userInfo: {
        type: Object,
        required: true,
        default: () => ({
          firstName: '',
          lastName: '',
          organization: '',
          type: 'broker',
        }),
      },
      value: {
        type: String,
        required: true,
        default: '',
      },
    },

    data: () => ({
      errorOnSubmit: null,
      timestamp: '',
      isSaving: false,
      timer: 0,
    }),

    computed: {
      /**
       * get(): returns the v-model from the parent
       * set(): emits to the parent new model.
       *
       * @returns {string}
       */
      commentValue: {
        get() {
          return this.value;
        },
        set(value) {
          this.$emit('input', value);

          if (this.saveSession) {
            this.updateSession(value);
          }
        },
      },

      /**
       * Formats this.autoSaveTimestamp to use in the template.
       *
       * @returns {string}
       */
      formattedTimestamp() {
        return this.$TF.DatesUtils.formatDateAndTime(this.timestamp);
      },

      /**
       * Formats a default label based on user info.
       *
       * @returns {string}
       */
      label() {
        const { firstName, lastName, organization } = this.userInfo;

        return `${firstName} ${lastName} at ${organization}`;
      },

      /**
       * Formats this.autoSaveTimestamp to use in the template.
       *
       * @returns {string}
       */
      sessionName() {
        return this.customSessionName || `comment-${this.comment.id}`;
      },
    },

    /**
     * The parent controls if there is an inital timestamp from session
     * so we should pass it as a prop there and set it on create here.
     */
    created() {
      if (this.autoSaveTimestamp) {
        this.timestamp = this.autoSaveTimestamp;
      }
    },

    methods: {
      /**
       * Debounce on value change and update the session storage every 3 second unless the user is typing.
       *
       * @param {string} message
       */
      updateSession(message) {
        clearTimeout(this.timer);

        this.timer = setTimeout(() => {
          if (this.isSaving) {
            return;
          }

          this.timestamp = new Date(new Date().getTime()).toISOString();

          const commentObject = JSON.stringify({
            message,
            projectId: this.projectId,
            timestamp: this.timestamp,
          });

          window.sessionStorage.setItem(this.sessionName, commentObject);
        }, 3000);
      },

      /**
       * Removes the session and emits 'cancel' to the parent.
       */
      onClickCancel() {
        if (this.saveSession) {
          window.sessionStorage.removeItem(this.sessionName);
          clearTimeout(this.timer);
        }

        this.$emit('cancel');
      },

      /**
       * Submits the comment and emits 'commentAdded' to the parent.
       */
      onClickSubmit() {
        let commentableIds = this.proposalDocumentId || this.comment.commentable_id;

        this.isSaving = true;
        this.errorOnSubmit = null;

        if (this.commentableIds.length) {
          commentableIds = this.commentableIds;
        }

        this.$TF.CommentService
          .postComment({
            applicationNamespace: this.applicationNamespace,
            content: this.commentValue,
            commentableIds,
            parentId: this.parentId,
          })
          .then((data) => {
            const newComments = Array.isArray(data.comments)
              ? data.comments
              : [data.comment];
            const orderedComments = newComments.map((comment) => ({
              ...comment,
              showSuccess: this.inlineSuccess,
            }));
            if (!this.inlineSuccess) {
              this.$message({
                showClose: true,
                message: 'Your comment has been sent to your broker.',
                type: 'success',
                duration: 5000,
              });
            }
            orderedComments.forEach(() => {
              window.sessionStorage.removeItem(this.sessionName);
            });
            this.$emit('commentAdded', orderedComments);
          })
          .catch(() => {
            this.errorOnSubmit = 'There was an error submitting the comment.';
          })
          .finally(() => {
            this.isSaving = false;
            this.errorOnSubmit = null;

            if (this.saveSession) {
              clearTimeout(this.timer);
            }
          });
      },
    },

    destroy() {
      if (this.saveSession) {
        clearTimeout(this.timer);
      }
    },
  };
</script>

<style scoped lang="scss">
  @import 'src/assets/styles/variables';
  @import 'src/assets/styles/mixins';

  p {
    margin-bottom: 0;
  }

  .autosave-fade {
    &-enter-active,
    &-leave-active {
      transition: all .75s;
      max-height: 50px;
    }

    &-enter,
    &-leave-to /* .fade-leave-active below version 2.1.8 */ {
      opacity: 0;
      max-height: 0;
    }
  }

  .timestamp {
    color: $tf-medium-gray;
    text-align: right;
  }

  .comment-input-container.has-label {
    margin-top: 12px;
  }

  .el-textarea {
    margin: 10px 0 15px;
  }

  .text-error {
    text-align: right;
  }
</style>
