<template>
  <div
    class="no-mouse pa-2"
    :style="`${$vuetify.breakpoint.xs ? 'width: 90vw;' : 'width: 300px;'} xxx-background: rgba(0.5, 0.5, 0.5, 0.5)`"
  >
    <div v-if="$vuetify.breakpoint.xs" class="text-right mb-5 mouse">
      <v-btn icon small class="background ml-2 elevation-10" @click="minimise = !minimise">
        <v-icon v-if="!minimise" small>mdi-minus</v-icon>
        <v-icon v-else small>mdi-plus</v-icon>
      </v-btn>
      <v-btn
        icon
        small
        class="primary dark white--text ml-2 elevation-10"
        @click="$emit('close', comment)"
      >
        <v-icon small>mdi-close</v-icon>
      </v-btn>
    </div>
    <div v-show="!minimise" style="width: 100%" class="mouse">
      <div v-if="!isComplete" class="warning rounded-xl py-2 caption mb-2 text-center" dense>
        <v-icon x-small>mdi-alert-circle-outline</v-icon>
        This comment is targeting other resources.
        <v-btn x-small @click="addMissingResources()">View in full context</v-btn>
      </div>
      <div class="px-2" v-show="$apollo.loading">
          <v-progress-linear indeterminate/>
        </div>
      <template v-for="(reply, index) in thread">
        <div v-if="showTime(index)" :key="index + 'date'" class="d-flex justify-center mouse">
          <div class="d-inline px-2 py-0 caption text-center mb-2 rounded-lg background grey--text">
            {{ new Date(reply.createdAt).toLocaleString() }}
            <timeago :datetime="reply.createdAt" class="font-italic ma-1"></timeago>
          </div>
        </div>
        <div
          :key="index"
          :class="`d-flex px-2 py-1 mb-2 align-center rounded-xl elevation-2 ${
            $userId() === reply.authorId ? 'primary white--text' : 'background'
          }`"
        >
          <div :class="`${$userId() === reply.authorId ? 'order-last' : ''}`">
            <user-avatar :id="reply.authorId" :size="30" />
          </div>
          <div
            :class="`mx-2 px-4 py-2 flex-grow-1 float-left caption`"
            style="overflow-wrap: break-word"
          >
            {{ reply.text }}
          </div>
        </div>
      </template>
      <div v-if="$loggedIn()" class="px-0 mb-4">
        <v-slide-y-transition>
          <div class="px-4 py-2 caption mb-2 background rounded-xl" v-show="whoIsTyping.length > 0">
            {{typingStatusText}}
          </div>
        </v-slide-y-transition>
        <div >
          <v-textarea
            :disabled="loadingReply"
            v-model="replyText"
            solo
            hide-details
            auto-grow
            rows="1"
            placeholder="Reply (press enter to send)"
            class="rounded-xl mb-2 caption"
            append-icon="mdi-send"
            @input="debTypingUpdate"
            @click:append="addReply"
            @keydown.enter.exact.prevent="addReply()"
          ></v-textarea>
        </div>
        <div class="px-2" v-show="loadingReply">
          <v-progress-linear indeterminate/>
        </div>
        <div class="text-right" ref="replyinput">
          <v-btn
            v-show="canArchiveThread"
            v-tooltip="'Marks this thread as archived.'"
            class="white--text mt-2 mr-2"
            small
            icon
            depressed
            color="error"
            @click="showArchiveDialog = true"
          >
            <v-icon small>mdi-delete-outline</v-icon>
          </v-btn>
          <v-btn
            v-tooltip="'Share this comment as a link!'"
            class="white--text mt-2 mr-2 rounded-xl elevation-4"
            small
            depressed
            color="primary"
            @click="copyCommentLinkToClip()"
          >
            <v-icon small class="mr-2">mdi-share-variant</v-icon>
            share
          </v-btn>
        </div>
        <v-dialog v-model="showArchiveDialog" max-width="500">
          <v-card>
            <v-toolbar color="error" dark flat>
              <v-app-bar-nav-icon style="pointer-events: none">
                <v-icon>mdi-pencil</v-icon>
              </v-app-bar-nav-icon>
              <v-toolbar-title>Archive Comment Thread</v-toolbar-title>
              <v-spacer></v-spacer>
              <v-btn icon @click="showArchiveDialog = false"><v-icon>mdi-close</v-icon></v-btn>
            </v-toolbar>
            <v-card-text class="mt-4">
              This comment thread will be archived. Are you sure?
            </v-card-text>
            <v-card-actions>
              <v-spacer></v-spacer>
              <v-btn text @click="showArchiveDialog = false">Cancel</v-btn>
              <v-btn color="error" text @click="archiveComment()">Archive</v-btn>
            </v-card-actions>
          </v-card>
        </v-dialog>
      </div>
      <div v-else>
        <v-btn block depressed color="primary" class="rounded-xl" @click="$loginAndSetRedirect()">
          <v-icon small class="mr-1">mdi-account</v-icon>
          Sign in to reply
        </v-btn>
      </div>
    </div>
  </div>
</template>
<script>
import gql from 'graphql-tag'
import debounce from 'lodash.debounce'

export default {
  components: {
    UserAvatar: () => import('@/main/components/common/UserAvatar')
  },
  props: {
    comment: { type: Object, default: () => null }
  },
  apollo: {
    user: {
      query: gql`
        query{
          user {
            name
            id
          }
        }
      `
    },
    stream: {
      query: gql`
        query($streamId: String!) {
          stream(id: $streamId){
            id
            role
          }
        }
      `,
      variables() {
        return { streamId: this.$route.params.streamId }
      }
    },
    replyQuery: {
      query: gql`
        query($streamId: String!, $id: String!) {
          comment(streamId: $streamId, id: $id) {
            id
            viewedAt
            archived
            replies(limit: 1000) {
              totalCount
              cursor
              items {
                id
                text
                authorId
                createdAt
              }
            }
          }
        }
      `,
      fetchPolicy: 'cache-and-network',
      variables() {
        return {
          streamId: this.$route.params.streamId,
          id: this.comment.id
        }
      },
      result({ data }) {
        if(!data) return
        data.comment.replies.items.forEach((item) => {
          if (this.localReplies.findIndex((c) => c.id === item.id) === -1)
            this.localReplies.push(item)
        })
        // this.localReplies.push(...data.comment.replies.items)
      },
      update: (data) => data.comment
    },
    $subscribe: {
      commentThreadActivity: {
        query: gql`
          subscription($streamId: String!, $commentId: String!) {
            commentThreadActivity(streamId: $streamId, commentId: $commentId)
          }
        `,
        variables() {
          return {
            streamId: this.$route.params.streamId,
            commentId: this.comment.id
          }
        },
        skip() {
          return !this.$loggedIn()
        },
        result({ data }) {
          if(!data || !data.commentThreadActivity) return
          if (data.commentThreadActivity.eventType === 'reply-added') {
            if (!this.comment.expanded) return this.$emit('bounce', this.comment.id)
            else {
              setTimeout(() => {
                this.$emit('refresh-layout') // needed for layout reshuffle in parent
              }, 100)
            }
            this.localReplies.push({ ...data.commentThreadActivity })
            this.$refs.replyinput.scrollIntoView({behaviour: 'smooth', block: 'end' })
            return
          }
          if (data.commentThreadActivity.eventType === 'comment-archived') {
            this.$emit('deleted', this.comment)
          }
          if(data.commentThreadActivity.eventType === 'reply-typing-status') {
            let state = data.commentThreadActivity.data
            if(state.userId === this.$userId()) return
            let existingUser = this.whoIsTyping.find( u => u.userId === state.userId)
            if(state.isTyping && existingUser) {
              existingUser.lastSeenAt = Date.now()
              return
            }
            if(!state.isTyping) {
              let indx = this.whoIsTyping.findIndex( u => u.userId === state.userId)
              if(indx!==-1) this.whoIsTyping.splice(indx, 1)
              return
            }
            if(state.isTyping && !existingUser) {
              state.lastSeenAt = Date.now()
              this.whoIsTyping.push(state)
            }
          }
        }
      }
    }
  },
  data: function () {
    return {
      replyText: null,
      localReplies: [],
      minimise: false,
      showArchiveDialog: false,
      loadingReply: false,
      whoIsTyping: [],
      isTyping: true
    }
  },
  computed: {
    canArchiveThread() {
      if(!this.comment || !this.stream ) return false
      if(!this.stream.role) return false
      if(this.comment.authorId === this.$userId() || this.stream.role ==='stream:owner') return true
    },
    thread() {
      let sorted = [...this.localReplies].sort(
        (a, b) => new Date(a.createdAt) - new Date(b.createdAt)
      )
      return [this.comment, ...sorted]
    },
    isComplete() {
      let res = [this.$route.params.resourceId]
      if (this.$route.query.overlay) res.push(...this.$route.query.overlay.split(','))
      let commRes = this.comment.resources
        .filter((r) => r.resourceType !== 'stream')
        .map((r) => r.resourceId)

      for (let r of commRes) {
        if (res.indexOf(r) === -1) return false
      }
      return true
    },
    link() {
      if (!this.comment) return
      let res = this.comment.resources.filter((r) => r.resourceType !== 'stream')
      let first = res.shift()
      let route = `/streams/${this.$route.params.streamId}/${first.resourceType}s/${first.resourceId}?cId=${this.comment.id}`
      if (res.length !== 0) {
        route += `&overlay=${res.map((r) => r.resourceId).join(',')}`
      }
      return route
    },
    typingStatusText() {
      if(this.whoIsTyping.length === 0) return null
      if(this.whoIsTyping.length > 1) {
        return `${this.whoIsTyping.map(u=>u.userName).join(', ')} are typing...`
      } else {
        return `${this.whoIsTyping[0].userName} is typing...`
      }
    },
  },
  watch: {
    'comment.expanded': {
      deep: true,
      async handler(newVal) {
        await this.$apollo.mutate({
          mutation: gql`
            mutation commentView($streamId: String!, $commentId: String!) {
              commentView(streamId: $streamId, commentId: $commentId)
            }
          `,
          variables: { streamId: this.$route.params.streamId, commentId: this.comment.id }
        })

        // eslint-disable-next-line vue/no-mutating-props
        this.comment.viewedAt = Date.now()
        if (!newVal) return
        this.localReplies = []
        this.$apollo.queries.replyQuery.refetch()
        this.$mixpanel.track('Comment Action', { type: 'action', name: 'open' })
      }
    }
  },
  mounted(){
    window.addEventListener('beforeunload', async (e) => {
      await this.sendTypingUpdate( false )
    })
    setInterval(()=>{
      let now = Date.now()
      for(let i = this.whoIsTyping.length-1; i >= 0; i--) {
        if(Math.abs(now - this.whoIsTyping[i].lastSeenAt) > 10000) this.whoIsTyping.splice(i, 1)
      }
    }, 5000)
  },
  methods: {
    debTypingUpdate: debounce( async function() { 
      if(!this.$loggedIn()) return
      await this.sendTypingUpdate(this.isTyping)
      this.isTyping = !this.isTyping
    }, 7000, { leading: true }),

    async sendTypingUpdate( state = true) {
      if(!this.$loggedIn()) return
      await this.$apollo.mutate({
        mutation: gql`
          mutation typingUpdate($sId: String!, $cId: String!, $d: JSONObject ) {
            userCommentThreadActivityBroadcast(streamId: $sId, commentId: $cId, data: $d)
          }
        `,
        variables:{
          sId: this.$route.params.streamId,
          cId: this.comment.id,
          d: {
            userId: this.$userId(),
            userName: this.user.name,
            isTyping: state
          }
        }
      })
    },
    copyCommentLinkToClip() {
      let res = this.comment.resources.filter((r) => r.resourceType !== 'stream')
      let first = res.shift()
      let route = `${window.origin}/streams/${this.$route.params.streamId}/${first.resourceType}s/${first.resourceId}?cId=${this.comment.id}`
      if (res.length !== 0) {
        route += `&overlay=${res.map((r) => r.resourceId).join(',')}`
      }
      navigator.clipboard.writeText(route)
      this.$mixpanel.track('Comment Action', { type: 'action', name: 'share' })
      this.$eventHub.$emit('notification', {
        text: 'Comment link copied to clipboard - paste away!'
      })
    },
    addMissingResources() {
      let res = [this.$route.params.resourceId]
      if (this.$route.query.overlay) res.push(...this.$route.query.overlay.split(','))
      let commRes = this.comment.resources
        .filter((r) => r.resourceType !== 'stream')
        .map((r) => r.resourceId)

      let missing = []
      for (let r of commRes) {
        if (res.indexOf(r) === -1) missing.push(r)
      }
      this.$emit('add-resources', missing)
    },
    showTime(index) {
      if (index === 0) return true
      let curr = new Date(this.thread[index].createdAt)
      let prev = new Date(this.thread[index - 1].createdAt)
      let delta = Math.abs(prev - curr)
      return delta > 450000
    },
    async addReply() {
      if (!this.replyText || this.replyText.length < 1) {
        this.$eventHub.$emit('notification', {
          text: `Cannot post an empty reply.`
        })
        return
      }

      let replyInput = {
        streamId: this.$route.params.streamId,
        parentComment: this.comment.id,
        text: this.replyText
      }
      
      this.loadingReply = true
      
      try {
        this.replyText = null
        await this.$apollo.mutate({
          mutation: gql`
            mutation commentReply($input: ReplyCreateInput!) {
              commentReply(input: $input)
            }
          `,
          variables: { input: replyInput }
        })
        await this.sendTypingUpdate( false )
        this.$mixpanel.track('Comment Action', { type: 'action', name: 'reply' })
      } catch (e) {
        this.$eventHub.$emit('notification', {
          text: e.message
        })
      }

      this.loadingReply = false

      setTimeout(() => {
        // Shhh.
        // eslint-disable-next-line vue/no-mutating-props
        this.comment.replies.totalCount++
        // eslint-disable-next-line vue/no-mutating-props
        this.comment.updatedAt = Date.now()
        this.$emit('refresh-layout') // needed for layout reshuffle in parent
      }, 100)
    },
    async archiveComment() {
      try {
        await this.$apollo.mutate({
          mutation: gql`
            mutation commentArchive($streamId: String!, $commentId: String!) {
              commentArchive(streamId: $streamId, commentId: $commentId)
            }
          `,
          variables: {
            streamId: this.$route.params.streamId,
            commentId: this.comment.id
          }
        })
        this.replyText = null
        this.showArchiveDialog = false
        this.$emit('deleted', this.comment)
        this.$mixpanel.track('Comment Action', { type: 'action', name: 'archive' })
        this.$eventHub.$emit('notification', {
          text: 'Thread archived.'
        })
      } catch (e) {
        this.$eventHub.$emit('notification', {
          text: e.message
        })
      }
    }
  }
}
</script>
<style scoped>
.no-mouse {
  pointer-events: none;
}
.mouse {
  pointer-events: auto;
}
</style>
