<script setup lang="ts">
import EventCard from './components/EventCard.vue'
import PasswordCard from './components/PasswordCard.vue'
import FeatureUpdateCard from './components/FeatureUpdateCard.vue'
import { useNotificationMessage } from '@/stores/modules/notificationMessage'
import { cloneDeep } from 'lodash'
// import { DynamicScroller, DynamicScrollerItem } from 'vue3-virtual-scroller'
import 'vue3-virtual-scroller/dist/vue3-virtual-scroller.css'
import { formatTimezone } from '@/utils/tools'

const notificationMsgStore = useNotificationMessage()
const props = withDefaults(
  defineProps<{
    data: any
    isObserver?: boolean // 是否开启监听
    isScrollPadding?: boolean // 是否有padding
    updateReadCardsOnChange?: boolean // 是否在数据变化时请求接口更新已读卡片
    topOffset?: number // 应减去高度
    isShowInsertionTime?: boolean // 是否显示插入时间
  }>(),
  {
    isObserver: true,
    isScrollPadding: false,
    updateReadCardsOnChange: true,
    topOffset: 220
  }
)
const pageData = ref<any[]>([])

const emit = defineEmits<{
  seeAll: []
  hasCardRead: []
  viewMore: []
  jumpTracking: []
  loading: []
}>()
const handleSeeAll = () => {
  emit('seeAll')
}

// 创建一个新的 IntersectionObserver 实例
const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      const cardId = entry.target?.dataset?.cardId
      if (entry.isIntersecting) {
        // 将卡片设置为已经展示
        notificationMsgStore.setReadCardMap(cardId)
        const index = pageData.value.findIndex((item) => item.info.id === cardId)
        if (index > -1) {
          //  在1秒钟后将消息卡片设置为已读
          setTimeout(() => {
            pageData.value[index].info.isRead = true
            emit('hasCardRead')
          }, 400)
        }
      }
    })
  },
  {
    // 配置选项
    root: null, // 使用视窗作为根元素
    threshold: 0.1 // 当至少10%的元素进入视图时触发回调
  }
)
const curPageAllCards = ref<any>([])
// 监听元素是否在可视区域内
const watchCards = () => {
  curPageAllCards.value = document?.querySelectorAll('.notification-message-card')
  curPageAllCards.value.forEach((card: any) => {
    // const index = notificationMsgStore.notificationMsgList.indexOf(card.dataset.cardId)
    // index < 0 &&
    if (card.dataset.cardIsread === 'false' && card.dataset.cardId) {
      notificationMsgStore.concatNotificationMsgList([card.dataset.cardId])

      observer.observe(card)
    }
  })
}

const clearReadData = (data) => {
  const curData = data || []
  // 将当前页面剩余未读消息id从监听列表中移除
  const idsToRemove = curData.map((item: any) => item.info.id)
  notificationMsgStore.removeNotificationMsgList(idsToRemove)

  // 清除当前页面的所有卡片的监听
  curPageAllCards.value.forEach((card: any) => {
    observer.unobserve(card)
  })
}

const compareIdsInArrays = (arr1, arr2) => {
  // 如果两个数组长度不同，则它们不可能有相同的id集合
  if (arr1.length !== arr2.length) {
    return false
  }

  // 提取两个数组中的所有有效id并转换为Set
  const ids1 = new Set(arr1.filter((item) => item.info && item.info.id).map((item) => item.info.id))
  const ids2 = new Set(arr2.filter((item) => item.info && item.info.id).map((item) => item.info.id))

  // 比较两个Set的大小是否相同
  if (ids1.size !== ids2.size) {
    return false
  }

  // 检查ids1中的每个id是否都存在于ids2中
  for (let id of ids1) {
    if (!ids2.has(id)) {
      return false
    }
  }

  return true
}

const handleData = (data) => {
  data.map((item) => {
    item.id = String(Math.random()).slice(-10)
  })
  return data
}
const initData = () => {
  const watchOptions = { deep: true, immediate: true }
  const handleDataChange = (newData, oldData) => {
    // 如果id相等，则不需要更新页面数据
    if (compareIdsInArrays(oldData || [], newData || [])) return

    pageData.value = handleData(cloneDeep(newData))

    // 清除旧数据中的卡片监听
    clearReadData(oldData)

    // 请求接口将旧数据中的新已读卡片上传服务器
    if (props.data.updateReadCardsOnChange) {
      notificationMsgStore.markMessageAsRead()
    }

    // 重新监听新数据中的卡片
    nextTick(() => {
      setTimeout(() => {
        watchCards()
      }, 500)
    })
  }
  // 需要监听卡片已读未读状态
  if (props.isObserver) {
    watch(
      () => props.data,
      (newVal, oldVal) => {
        handleDataChange(newVal, oldVal)
      },
      watchOptions
    )
  } else {
    // 不需要监听
    watch(
      () => props.data,
      (newVal) => {
        pageData.value = handleData(cloneDeep(newVal))
      },
      watchOptions
    )
  }
}
initData()

// 将数据中的消息置为已读
const setAllMessageRead = () => {
  pageData.value.forEach((item) => {
    item.info.isRead = true
  })
}

// 定时将消息卡片未读的置为已读，五分钟一次
let timer = null
onMounted(() => {
  if (props.isObserver) {
    timer = setInterval(() => {
      notificationMsgStore.markMessageAsRead()
    }, 300000)
  }
})
onUnmounted(() => {
  if (props.isObserver) {
    notificationMsgStore.markMessageAsRead()
    clearReadData(pageData.value)
    clearInterval(timer)
  }
})

const handleViewMore = () => {
  emit('viewMore')
}
const parentHeight = computed(() => {
  return (window.innerHeight || document.documentElement.clientHeight) - props.topOffset
})
const scrollParentBoxStyle = computed(() => {
  return props.topOffset ? { height: `${parentHeight.value}px` } : {}
})

const scrollContainerRef = ref<HTMLElement | null>(null)
const loading = ref(false)
const finished = ref(false)
const prevScrollTop = ref(0)

const loadMore = async () => {
  if (loading.value || finished.value) return
  loading.value = true
  emit('loading')
}

const onScroll = () => {
  const el = scrollContainerRef.value
  if (!el || loading.value || finished.value) return

  prevScrollTop.value = el.scrollTop + 50

  const threshold = 50 // 提前50px触发
  if (el.scrollHeight - el.scrollTop - el.clientHeight <= threshold) {
    loadMore()
  }
}

const sentinel = ref<HTMLElement | null>(null)

const adjustScrollTop = () => {
  const el = scrollContainerRef.value
  if (el) {
    // 如果滚动容器存在，调整其 scrollTop
    el.scrollTop = prevScrollTop.value
  }
}

defineExpose({
  loading,
  finished,
  scrollContainerRef,
  adjustScrollTop,
  setAllMessageRead
})
</script>

<template>
  <div class="scroller" :style="scrollParentBoxStyle" ref="scrollContainerRef" @scroll="onScroll">
    <template v-for="item in pageData" :key="item.id">
      <div
        :class="{ 'scroll-padding': props.isScrollPadding }"
        :item="item"
        :size-dependencies="[item.info.isRead]"
      >
        <div
          class="notification-message-card"
          :data-card-id="item.info.id"
          :data-card-isread="item.info.isRead"
        >
          <EventCard
            @seeAll="handleSeeAll"
            @jump-tracking="emit('jumpTracking')"
            v-if="item.notificationType === 'event'"
            :data="item.info"
          />
          <PasswordCard v-else-if="item.notificationType === 'password'" :data="item.info" />
          <FeatureUpdateCard
            @view-more="handleViewMore"
            v-else-if="item.notificationType === 'feature'"
            :data="item.info"
          />
          <slot></slot>
          <div class="insertion-time" v-if="isShowInsertionTime">
            {{ formatTimezone(item.info.first_notifiation_date) }}
          </div>
        </div>
      </div>
    </template>
    <div
      class="footer"
      v-if="pageData?.[0]?.notificationType !== 'feature' && pageData?.length > 0"
    >
      <el-divider v-if="loading"> loading... </el-divider>
      <el-divider v-if="finished && pageData.length > 0">
        Only display the message data within three months
      </el-divider>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.scroller {
  width: 100%;
  overflow-y: auto;
  .scroll-padding {
    padding: 0px 140px 0px 16px;
  }
  .footer {
    padding: 0 16px;
    text-align: center;
  }
}
.notification-message-card {
  position: relative;
  .insertion-time {
    position: absolute;
    right: 0;
    top: 4px;
    font-size: 12px;
    color: var(--color-neutral-2);
  }
}
</style>
