<template>
  <div class="annotation-view" :class="{ pb: !['view', 'sign', 'document'].includes(toolMode) }">
    <template v-if="annotationsContainers.annotationsCount > 0">
      <div v-for="(pageAnnotations, pageNumber) in annotationsContainers.annotations">
        <template v-if="pageAnnotations.pageAnnotationsCount > 0">
          <div class="page-title">
            <span>{{ $t('leftPanel.whichPage[0]') }}{{ pageNumber * 1 + 1 }}{{ $t('leftPanel.whichPage[1]') }}</span>
            <span>{{ pageAnnotations.pageAnnotationsCount }}</span>
          </div>
          <template v-for="(item, index) in pageAnnotations.annotations">
            <div v-if="!['link', 'reply'].includes(item.type) && !item.isDelete && !item.isHidden" class="annotation-item" :class="{ 'selected': selectedAnnot && selectedAnnot.name === item.name }" :id="item.name ? `annotationList-${item.name}` : null">
              <div class="item-header">
                <Highlight v-if="item.type === 'highlight'" />
                <Squiggle v-else-if="item.type === 'squiggly'" />
                <Strikeout v-else-if="item.type === 'strikeout'" />
                <Underline v-else-if="item.type === 'underline'" />
                <Ink v-else-if="item.type === 'ink'" />
                <LineTool
                  v-else-if="item.type === 'line' && (((item.tail === 'None' || item.tail === 'Unknown') && (item.head === 'None' || item.head === 'Unknown')) || (!item.tail && !item.head))" />
                <ArrowTool
                  v-else-if="item.type === 'line' && (item.tail === 'OpenArrow' || item.head === 'OpenArrow' || item.arrow)" />
                <RectangleTool v-else-if="item.type === 'square'" />
                <EllipseTool v-else-if="item.type === 'circle'" />
                <Text v-else-if="item.type === 'freetext'" />
                <Note v-else-if="item.type === 'text'" />
                <Stamp v-else-if="item.type === 'image' || item.type === 'stamp'" />

                <div class="info" @click="goToPage(item)">
                  <p class="name">{{ item.author || item.title || 'Guest' }}</p>
                  <p class="date">{{ dayjs(item.date).format('DD/MM/YYYY HH:mm:ss') }}</p>
                  <div v-if="item.contents" class="content">{{ item.contents }}</div>
                </div>
                <n-popover trigger="hover" placement="bottom" class="mark-popover">
                  <template #trigger>
                    <div class="mark-box" :class="{ 'marked': item.markedAnnotState === 'MARKED' || item.markedAnnotState?.state === 'MARKED' }" @click.stop="setMarkedState(item)"></div>
                  </template>
                  <span>{{ item.markedAnnotState === 'MARKED' || item.markedAnnotState?.state === 'MARKED' ? $t('leftPanel.marked') : $t('leftPanel.unmarked') }}</span>
                </n-popover>
              </div>

              <div class="item-reply" v-if="selectedAnnot && selectedAnnot.name === item.name && (showReplyInput || item.replies?.length)">
                <div v-if="showReplyInput">
                  <textarea :placeholder="$t('leftPanel.replyPlaceholder')" v-model="replyText" class="annotReplyInput"></textarea>
                  <div class="buttons">
                    <span @click.stop="cancel">{{ $t('cancel') }}</span>
                    <button :class="{ 'active': replyText, disabled: !replyText }" @click="addReply">{{ $t('leftPanel.reply') }}</button>
                  </div>
                </div>

                <div class="replies">
                  <template v-for="(reply, index) in item.replies" :key="index">
                    <div v-if="reply.contents" class="info">
                      <div class="info-header">
                        <div>
                          <p class="name">{{ reply.author || reply.title || annotator }}</p>
                          <p class="date">{{ dayjs(reply.date).format('DD/MM/YYYY HH:mm:ss') }}</p>
                        </div>
                        <Button class="more" :class="{ active: showReplyPopover && reply.name === selectedReply?.name }" @click="handleReplyPopoverShow($event, reply)">
                          <MoreA />
                        </Button>
                      </div>
                      <div class="content" v-if="editing && reply.name === selectedReply?.name">
                        <textarea v-model="editingReplyText" class="replyEditInput"></textarea>
                        <div class="buttons">
                          <span @click="cancelEdit">{{ $t('cancel') }}</span>
                          <button :class="[editingReplyText && editingReplyText !== reply.contents ? 'active' : 'disabled']" @click="editReply">{{ $t('leftPanel.confirm') }}</button>
                        </div>
                      </div>
                      <div class="content" v-else>{{ reply.contents }}</div>
                    </div>
                  </template>
                </div>

                <n-popover
                  placement="bottom-start"
                  trigger="manual"
                  :show-arrow="false"
                  to="#outerContainer"
                  :raw="true"
                  :z-index="96"
                  class="reply-popover"
                  :show="showReplyPopover"
                  :x="x"
                  :y="y"
                  @clickoutside="onOutsideReplyPopover"
                >
                  <div class="drop-down">
                    <div class="drop-item" @click="editReply">{{ $t('leftPanel.edit') }}</div>
                    <div class="drop-item" @click="deleteReply">{{ $t('leftPanel.delete') }}</div>
                  </div>
                </n-popover>
              </div>

              <div class="item-footer">
                <n-popover
                  placement="bottom"
                  trigger="click"
                  :show-arrow="false"
                  to="#outerContainer"
                  :raw="true"
                  :z-index="96"
                  class="state-popover"
                  :show="showPopover === item.name"
                  @clickoutside="onOutsidePopover"
                >
                  <template #trigger>
                    <Button class="state" :class="{ active: showPopover === item.name }" @click.stop="handlePopoverShow(item)">
                      <Accepted v-if="item.reviewAnnotState === 'ACCEPTED' || item.reviewAnnotState?.state === 'ACCEPTED'" />
                      <Rejected v-else-if="item.reviewAnnotState === 'REJECTED' || item.reviewAnnotState?.state === 'REJECTED'" />
                      <Cancelled v-else-if="item.reviewAnnotState === 'CANCELLED' || item.reviewAnnotState?.state === 'CANCELLED'" />
                      <Completed v-else-if="item.reviewAnnotState === 'COMPLETED' || item.reviewAnnotState?.state === 'COMPLETED'" />
                      <None v-else />
                    </Button>
                  </template>
                  <div class="drop-down">
                    <div class="drop-item" @click="setReviewState(item, 'ACCEPTED')"><Accepted />{{ $t('leftPanel.accepted') }}</div>
                    <div class="drop-item" @click="setReviewState(item, 'REJECTED')"><Rejected />{{ $t('leftPanel.rejected') }}</div>
                    <div class="drop-item" @click="setReviewState(item, 'CANCELLED')"><Cancelled />{{ $t('leftPanel.cancelled') }}</div>
                    <div class="drop-item" @click="setReviewState(item, 'COMPLETED')"><Completed />{{ $t('leftPanel.completed') }}</div>
                    <div class="drop-item" @click="setReviewState(item, 'NONE')"><None />{{ $t('leftPanel.none') }}</div>
                  </div>
                </n-popover>
                <div class="re">
                  <Reply />
                  <span v-if="item?.replies?.filter(reply => reply.contents)?.length">{{ item.replies.filter(reply => reply.contents).length }}</span>
                  <Return class="return" @click="selectReturn(item)" />
                </div>
              </div>
            </div>
          </template>
        </template>
      </div>
    </template>
    <div v-else class="no-annotations">{{ $t('leftPanel.noAnnotations') }}</div>
  </div>
</template>

<script setup>
import { computed, ref, getCurrentInstance, watch, nextTick } from 'vue'
import dayjs from 'dayjs'
import core from '@/core'
import { useDocumentStore } from '@/stores/modules/document'
import { useViewerStore } from '@/stores/modules/viewer'
import { NPopover } from 'naive-ui'
import { isMobile } from '@/helpers/device'

const useDocument = useDocumentStore()
const useViewer = useViewerStore()

let annotationsContainers = computed(() => useDocument.getAllAnnotations)
const activePanelTab = computed(() => useViewer.getActiveElementTab('leftPanel'))
const isOpen = computed(() => useViewer.isElementOpen('leftPanel'))
const annotator = computed(() => useDocument.getAnnotator)
const toolMode = computed(() => useViewer.getToolMode)

const markup = ['highlight', 'underline', 'squiggly', 'strikeout']

const selectedAnnot = ref(null)
const selectedReply = ref(null)
const showReplyInput = ref(false)
const replyText = ref('')
const editing = ref(false)
const editingReplyText = ref('')

const showPopover = ref('')
const showReplyPopover = ref(false)
const x = ref(0)
const y = ref(0)
let timeout = null
let oldAnnotations = null
let newAnnotations = null

watch([activePanelTab, isOpen], (oldValue, newValue) => {
  if (newValue[0] === 'ANNOTATION' && newValue[1]) cancel()
})

const setAnnotationList = ({ annotations }) => {
  oldAnnotations = newAnnotations
  newAnnotations = JSON.parse(JSON.stringify(annotations))

  useDocument.initAnnotations(annotations)
  const instance = getCurrentInstance()
  instance?.proxy?.$forceUpdate()
  // console.log(annotations)
  
  setTimeout(() => {
    const newObject = findNewObject(oldAnnotations, newAnnotations)
    if (newObject && newObject.name) {
      const targetElement = document.getElementById(`annotationList-${newObject.name}`)
      targetElement && scrollIntoView(targetElement)
    }
  })
}
core.addEvent('annotationChanged', setAnnotationList)

const goToPage = (annotation) => {
  core.jumpToAnnotation(annotation)
  toolMode.value !== 'editor' && core.selectAnnotation(annotation)

  if (selectedAnnot.value?.name !== annotation.name) {
    replyText.value = ''
    selectedAnnot.value = annotation
    showReplyInput.value = false
  }

  isMobile && useViewer.closeElement('leftPanel')
}

// 取消回复
const cancel = () => {
  showReplyInput.value = false
  replyText.value = ''
}

// 设置状态
const setReviewState = (item, state) => {
  let data
  if (item.reviewAnnotState) {
    data = {
      type: 'edit',
      annotation: item,
      reviewAnnotState: item.reviewAnnotState.annotPtr ? {
        annotPtr: item.reviewAnnotState.annotPtr,
        state
      } : state
    }
  } else {
    data = {
      type: 'add',
      annotation: item,
      reviewAnnotState: state
    }
  }
  core.handleReplyAnnotation(data)
  showPopover.value = ''
}

const handlePopoverShow = (item) => {
  if (showPopover.value) {
    showPopover.value = ''
  } else {
    showPopover.value = item.name
  }
}

const handleReplyPopoverShow = (e, reply) => {
  if (showReplyPopover.value) {
    closeReplyPopover()
    return
  }
  
  let element = e.target
  while (element.parentElement && !element.classList.contains('more')) {
    element = element.parentElement
  }
  if (element.classList.contains('more')) {
    const rect = element.getBoundingClientRect()
    x.value = rect.left
    y.value = rect.bottom
    showReplyPopover.value = true
    selectedReply.value = reply
  }
}

// 标记状态
const setMarkedState = (annotation) => {
  let data
  if (annotation.markedAnnotState) {
    data = {
      type: 'edit',
      annotation,
      markedAnnotState: annotation.markedAnnotState.annotPtr ? {
        annotPtr: annotation.markedAnnotState.annotPtr,
        state: annotation.markedAnnotState.state === 'MARKED' ? 'UNMARKED' : 'MARKED'
      } : (annotation.markedAnnotState === 'MARKED' ? 'UNMARKED' : 'MARKED')
    }
  } else {
    data = {
      type: 'add',
      annotation,
      markedAnnotState: 'MARKED'
    }
  }
  core.handleReplyAnnotation(data)
}

// 回复
const addReply = () => {
  if (replyText.value === '') return
  const data = {
    type: 'add',
    annotation: selectedAnnot.value,
    reply: {
      operate: 'add-annot',
      type: 'reply',
      author: annotator.value,
      title: annotator.value,
      contents: replyText.value,
      date: new Date(),
      pageIndex: selectedAnnot.value.pageIndex,
      index: selectedAnnot.value.replies?.length || 0
    }
  }
  core.handleReplyAnnotation(data)
  replyText.value = ''
}

// 编辑回复
const editReply = () => {
  showReplyInput.value = false
  showReplyPopover.value = false

  if (editing.value && editingReplyText.value !== selectedReply.value.contents) {
    const data = {
      type: 'edit',
      annotation: selectedAnnot.value,
      reply: {
        ...selectedReply.value,
        operate: 'mod-annot',
        contents: editingReplyText.value,
        date: new Date()
      }
    }
    core.handleReplyAnnotation(data)
    editing.value = false
  } else {
    editing.value = true
    editingReplyText.value = selectedReply.value.contents
    nextTick(() => {
      const inputElement = document.querySelector('.replyEditInput')
      inputElement.focus()
      inputElement.setSelectionRange(inputElement.value.length, inputElement.value.length)
    })
  }
}

// 删除回复
const deleteReply = () => {
  const data = {
    type: 'delete',
    annotation: selectedAnnot.value,
    reply: selectedReply.value
  }
  core.handleReplyAnnotation(data)
  closeReplyPopover()
}

const onOutsideReplyPopover = (e) => {
  const button = document.querySelector('.button.more.active')
  if (!button.contains(e.target)) {
    closeReplyPopover()
  }
}

const closeReplyPopover = () => {
  showReplyPopover.value = false
  if (!editing.value) selectedReply.value = null
}

const cancelEdit = () => {
  editing.value = false
  editingReplyText.value = ''
  selectedReply.value = null
}

const onOutsidePopover = (e) => {
  const button = document.querySelector('.button.state.active')
  if (!button.contains(e.target)) {
    showPopover.value = ''
  }
}

const selectReturn = (item, flag) => {
  showReplyInput.value = true

  if (selectedAnnot.value?.name !== item.name) {
    selectedAnnot.value = item
    replyText.value = ''
  }

  if (flag) return
  nextTick(() => {
    document.querySelector('.annotReplyInput')?.focus()
  })
}

const openAnnotationReply = (annotation) => {
  if (activePanelTab.value !== 'ANNOTATION') {
    useViewer.setActiveElementTab('leftPanel', 'ANNOTATION')
  }
  if (!isOpen.value) {
    useViewer.openElement('leftPanel')
  }

  selectReturn(annotation, true)

  if (timeout) {
    clearTimeout(timeout)
    timeout = null
  }
  setTimeout(() => {
    const div = document.querySelector('.annotation-item.selected')
    div.scrollIntoView({ behavior: 'smooth' })
    if (!div.classList.contains('flash-border')) {
      div.classList.add('flash-border')
      timeout = setTimeout(() => document.querySelector('.annotation-item.selected').classList.remove('flash-border'), 2000)
    } else {
      div.classList.remove('flash-border')
      clearTimeout(timeout)
      timeout = null
    }
  })
}
core.addEvent('openAnnotationReply', openAnnotationReply)

const scrollIntoView = (element, options = { behavior: 'smooth', block: 'start' }) => {
  element.scrollIntoView(options)
}

const findNewObject = (oldData, newData) => {
  if (!oldData || !newData) return

  for (let key in newData) {
    let newArray = newData[key] || 0
    let oldArray = oldData[key] || 0

    if (newArray && newArray.length > oldArray.length) {
      return newArray[newArray.length - 1]
    }
  }

  return null
}
</script>

<style lang="scss">
.annotation-view {
  position: relative;
  padding: 0 8px;
  height: 100%;
  overflow: auto;

  &.pb {
    padding-bottom: 44px;
  }

  .page-title {
    display: flex;
    justify-content: space-between;
    padding: 6px 8px;
    color: var(--c-side-title);

    span {
      font-size: 14px;
      line-height: 20px;
    }
  }

  .annotation-item {
    margin-top: 4px;
    padding: 4px 8px;
    background: var(--c-side-annotation-bg);
    border-radius: 4px;
    border: 1.2px solid transparent;

    & + .annotation-item {
      margin-top: 8px;
    }

    &.selected {
      border: 1.2px solid var(--c-findbar-input-border);
    }

    .item-header {
      position: relative;
      display: flex;
      padding: 8px 22px 8px 0;
      color: var(--c-side-text);

      svg {
        margin-top: 6px;
        margin-right: 8px;
        min-width: 20px;
      }

      .mark-box {
        position: absolute;
        top: 17px;
        right: 0;
        width: 14px;
        height: 14px;
        border: 1.2px solid #D9D9D9;
        border-radius: 2px;
        background: #fff;
        cursor: pointer;

        &.marked {
          border-color: var(--c-side-annotation-icon);
          background: none;

          &::before {
            content: '';
            width: 6px;
            height: 6px;
            background: var(--c-side-annotation-icon);
            border-radius: 50%;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
          }
        }
      }
    }

    .info {
      .name {
        font-size: 12px;
        font-weight: 700;
        line-height: 16px;
      }

      .date {
        font-size: 10px;
        line-height: 16px;
        color: #999;
      }

      .content {
        padding-top: 4px;
        font-size: 14px;
        line-height: 16px;
        text-overflow: ellipsis;
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 4;
        overflow: hidden;
        color: var(--c-settings-text);
        word-break: break-all;
      }
    }

    .item-reply {
      padding: 8px 0 8px 28px;
      
      input,
      textarea {
        padding: 3px 4px 3px 8px;
        width: 100%;
        height: 24px;
        background: var(--c-right-side-content-fillbox-bg);
        border: 1px solid var(--c-right-side-content-fillbox-border);
        border-radius: 1px;
        font-size: 16px;
        line-height: 16px;
        color: var(--c-text);

        &::placeholder {
          color: #999;
        }
      }

      textarea {
        font-family: 'Helvetica';
        resize: vertical;
      }

      .buttons {
        margin-top: 4px;
        margin-bottom: 8px;
        display: flex;
        justify-content: flex-end;
        align-items: center;

        span {
          margin-right: 8px;
          font-size: 12px;
          line-height: 16px;
          cursor: pointer;
        }

        button {
          padding: 2px 6.5px;
          border: none;
          border-radius: 2px;
          font-size: 12px;
          line-height: 16px;
          color: #999;
          background: #D9D9D9;

          &.active {
            cursor: pointer;
            color: #fff;
            background: #1460F3;
          }

          &.disabled {
            cursor: not-allowed;
          }
        }
      }

      .replies {
        .info {
          & + .info {
            margin-top: 12px;
          }

          .info-header {
            display: flex;
            justify-content: space-between;
            align-items: center;

            .more {
              padding: 0;
              margin-right: 0;
              border-radius: 2px;
            }
          }
        }
      }
    }

    .item-footer {
      display: flex;
      justify-content: space-between;
      align-items: center;
      color: var(--c-side-annotation-reply-icon);

      button {
        color: var(--c-side-annotation-reply-icon);
      }

      .re {
        display: flex;
        align-items: center;

        span {
          margin-left: 4px;
        }

        .return {
          margin-left: 12px;
          cursor: pointer;
        }
      }
    }
  }

  .no-annotations {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, 50%);
    color: var(--c-side-text);
    text-align: center;
    font-weight: 700;
    font-size: 14px;
    line-height: 16px;
  }
}

.state-popover .drop-down .drop-item {
  padding: 4px 8px;
  font-size: 14px;
  line-height: 16px;

  &:hover {
    color: var(--c-text);
    background-color: var(--c-header-button-active);
  }

  svg {
    margin-right: 8px;
  }
}

.mark-popover {
  padding: 4px !important;
  color: #fff;
  background: #414960 !important;

  .n-popover-arrow {
    background-color: #414960 !important;
  }
}

.reply-popover {
  margin-top: 0 !important;

  .drop-down .drop-item {
    padding: 0 2px;
    font-size: 14px;
    line-height: 20px;

    & + .drop-item {
      margin-top: 4px;
    }

    &:hover {
      color: var(--c-text);
      background-color: var(--c-header-button-active);
    }
  }
}

@keyframes flash {

  0%,
  100% {
    border-color: transparent;
  }

  50% {
    border-color: #1460F3;
  }
}

.flash-border {
  animation: flash 1s 2;
}
</style>
