<template>
  <div class="post-page-container">
    <div v-if="isWaitingFetchingPost" class="loading-container">
      <l-hatch size="28" stroke="4" speed="3.5" color="var(--primary-color)"></l-hatch>
    </div>
    <div v-else class="post-page-main-container" ref="mainContainer" @scroll="onScroll">
      <div class="article-title-container">
        <div class="article-title">{{ title }}</div>
        <div class="article-info-container">
          <ArticleCategory :category="category" />
          <ArticleTags :tags="tags" />
        </div>
      </div>

      <div class="info-content-container author-info-container">
        <div class="user-avatar-container" @click="gotoProfile">
          <div class="user-avatar-item">
            <img class="user-avatar-item-img" :src="author.avatar" alt="avatar">
          </div>
        </div>

        <div class="info-content">
          <div class="info-content-header">
            <div class="user-name">{{ author.username }}</div>
            <div class="post-time">{{ postTime }}</div>
          </div>
          <div class="info-content-text" v-html="renderMarkdown(postContent)"></div>

          <div class="article-footer-container">
            <ReactionsGroup v-model="postReactions" content-type="post" :content-id="postId">
              <div class="make-reaction-item copy-link" @click="copyPostLink">
                <i class="fas fa-link"></i>
              </div>
            </ReactionsGroup>
          </div>
        </div>
      </div>

      <CommentEditor @submit="postComment" :loading="isWaitingPostingComment" />

      <div class="comments-container">
        <BaseTimeline :items="comments">
          <template #item="{ item }">
            <CommentItem :key="item.id" :comment="item" :level="level + 1" :default-show-replies="item.openReplies" />
          </template>
        </BaseTimeline>
        <!-- <CommentItem
          v-for="comment in comments"
          :key="comment.id"
          :comment="comment"
          :level="0"
          :default-show-replies="comment.openReplies"
          ref="postItems"
        /> -->
      </div>
    </div>

    <div class="post-page-scroller-container">
      <div class="scroller">
        <div v-if="isWaitingFetchingPost" class="scroller-time">loading...</div>
        <div v-else class="scroller-time">{{ postTime }}</div>
        <div class="scroller-middle">
          <input type="range" class="scroller-range" :max="totalPosts" :min="1" v-model.number="currentIndex"
            @input="onSliderInput" />
          <div class="scroller-index">{{ currentIndex }}/{{ totalPosts }}</div>
        </div>
        <div v-if="isWaitingFetchingPost" class="scroller-time">loading...</div>
        <div v-else class="scroller-time">{{ lastReplyTime }}</div>
      </div>
    </div>
  </div>
</template>

<script>
import { ref, computed, onMounted, nextTick, watch } from 'vue'
import { useRoute } from 'vue-router'
import CommentItem from '../components/CommentItem.vue'
import CommentEditor from '../components/CommentEditor.vue'
import BaseTimeline from '../components/BaseTimeline.vue'
import ArticleTags from '../components/ArticleTags.vue'
import ArticleCategory from '../components/ArticleCategory.vue'
import ReactionsGroup from '../components/ReactionsGroup.vue'
import { renderMarkdown } from '../utils/markdown'
import { API_BASE_URL, toast } from '../main'
import { getToken } from '../utils/auth'
import TimeManager from '../utils/time'
import { hatch } from 'ldrs'
import { useRouter } from 'vue-router'
hatch.register()

export default {
  name: 'PostPageView',
  components: { CommentItem, CommentEditor, BaseTimeline, ArticleTags, ArticleCategory, ReactionsGroup },
  setup() {
    const route = useRoute()
    const postId = route.params.id
    const router = useRouter()

    const title = ref('')
    const author = ref('')
    const postContent = ref('')
    const category = ref('')
    const tags = ref([])
    const postReactions = ref([])
    const comments = ref([])
    const isWaitingFetchingPost = ref(false);
    const isWaitingPostingComment = ref(false);
    const postTime = ref('')
    const postItems = ref([])
    const mainContainer = ref(null)
    const currentIndex = ref(1)

    const gatherPostItems = () => {
      const items = []
      if (mainContainer.value) {
        const main = mainContainer.value.querySelector('.info-content-container')
        if (main) items.push({ el: main, top: 0 })

        for (const c of comments.value) {
          const el = document.getElementById('comment-' + c.id)
          if (el) {
            items.push({ el, top: getTopRelativeTo(el, mainContainer.value) })
          }
        }
        // 根据 top 排序，防止评论异步插入后顺序错乱
        items.sort((a, b) => a.top - b.top)
        postItems.value = items.map(i => i.el)
      }
    }

    const mapComment = c => ({
      id: c.id,
      userName: c.author.username,
      time: TimeManager.format(c.createdAt),
      avatar: c.author.avatar,
      text: c.content,
      reactions: c.reactions || [],
      reply: (c.replies || []).map(mapComment),
      openReplies: false,
      src: c.author.avatar,
      iconClick: () => router.push(`/users/${c.author.id}`)
    })

    const getTopRelativeTo = (el, container) => {
      const elRect = el.getBoundingClientRect()
      const parentRect = container.getBoundingClientRect()
      // 加上 scrollTop，得到相对于 container 内部顶部的距离
      return elRect.top - parentRect.top + container.scrollTop
    }

    const findCommentPath = (id, list) => {
      for (const item of list) {
        if (item.id === Number(id) || item.id === id) {
          return [item]
        }
        if (item.reply && item.reply.length) {
          const sub = findCommentPath(id, item.reply)
          if (sub) return [item, ...sub]
        }
      }
      return null
    }

    const expandCommentPath = (id) => {
      const path = findCommentPath(id, comments.value)
      if (!path) return
      for (let i = 0; i < path.length - 1; i++) {
        path[i].openReplies = true
      }
    }

    const fetchPost = async () => {
      try {
        isWaitingFetchingPost.value = true;
        const res = await fetch(`${API_BASE_URL}/api/posts/${postId}`)
        isWaitingFetchingPost.value = false;
        if (!res.ok) return
        const data = await res.json()
        postContent.value = data.content
        author.value = data.author
        title.value = data.title
        category.value = data.category
        tags.value = data.tags || []
        postReactions.value = data.reactions || []
        comments.value = (data.comments || []).map(mapComment)
        postTime.value = TimeManager.format(data.createdAt)
        await nextTick()
        gatherPostItems()
      } catch (e) {
        console.error(e)
      }
    }

    const totalPosts = computed(() => comments.value.length + 1)
    const lastReplyTime = computed(() =>
      comments.value.length ? comments.value[comments.value.length - 1].time : postTime.value
    )

    watch(
      () => comments.value.length,
      async () => {
        await nextTick()
        gatherPostItems()
      }
    )

    const updateCurrentIndex = () => {
      const container = mainContainer.value
      if (!container) return

      const scrollTop = container.scrollTop

      for (let i = 0; i < postItems.value.length; i++) {
        const el = postItems.value[i]
        // 计算元素相对 container 顶部的 top
        const top = getTopRelativeTo(el, container)
        const bottom = top + el.offsetHeight

        if (bottom > scrollTop) {
          currentIndex.value = i + 1
          break
        }
      }
    }

    const onSliderInput = () => {
      const target = postItems.value[currentIndex.value - 1]
      if (target && mainContainer.value) {
        const top = getTopRelativeTo(target, mainContainer.value)
        mainContainer.value.scrollTo({ top, behavior: 'instant' })
      }
    }

    const postComment = async (text) => {
      if (!text.trim()) return
      isWaitingPostingComment.value = true
      const token = getToken()
      if (!token) {
        toast.error('请先登录')
        isWaitingPostingComment.value = false
        return
      }
      try {
        const res = await fetch(`${API_BASE_URL}/api/posts/${postId}/comments`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
          body: JSON.stringify({ content: text })
        })
        if (res.ok) {
          const data = await res.json()
          comments.value.push(mapComment(data))
          await nextTick()
          gatherPostItems()
          toast.success('评论成功')
        } else {
          toast.error('评论失败')
        }
      } catch (e) {
        toast.error('评论失败')
      } finally {
        isWaitingPostingComment.value = false
      }
    }

    const copyPostLink = () => {
      navigator.clipboard.writeText(location.href.split('#')[0]).then(() => {
        toast.success('已复制')
      })
    }

    const jumpToHashComment = async () => {
      const hash = location.hash
      if (hash.startsWith('#comment-')) {
        const id = hash.substring('#comment-'.length)
        await nextTick()
        const el = document.getElementById('comment-' + id)
        if (el && mainContainer.value) {
          mainContainer.value.scrollTo({ top: getTopRelativeTo(el, mainContainer.value), behavior: 'instant' })
          el.classList.add('comment-highlight')
          setTimeout(() => el.classList.remove('comment-highlight'), 2000)
        }
      }
    }

    const gotoProfile = () => {
      router.push(`/users/${author.value.id}`)
    }

    onMounted(async () => {
      const hash = location.hash
      const id = hash.startsWith('#comment-') ? hash.substring('#comment-'.length) : null
      await fetchPost()
      gatherPostItems()
      if (id) expandCommentPath(id)
      updateCurrentIndex()
      await jumpToHashComment()
    })

    return {
      postContent,
      author,
      title,
      category,
      tags,
      comments,
      postTime,
      lastReplyTime,
      postItems,
      mainContainer,
      currentIndex,
      totalPosts,
      postReactions,
      postId,
      postComment,
      onSliderInput,
      onScroll: updateCurrentIndex,
      copyPostLink,
      renderMarkdown,
      isWaitingFetchingPost,
      isWaitingPostingComment,
      gotoProfile
    }
  }
}
</script>
<style>
.post-page-container {
  background-color: var(--background-color);
  display: flex;
  flex-direction: row;
  height: calc(100vh - var(--header-height));
}

.loading-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 200px;
  width: 85%;
}

.post-page-main-container {
  overflow-y: auto;
  scrollbar-width: none;
  padding: 20px;
  height: calc(100% - 40px);
  width: calc(85% - 40px);
}

.post-page-scroller-container {
  display: flex;
  flex-direction: column;
  width: 15%;
}

.scroller {
  margin-top: 20px;
  margin-left: 20px;
}

.scroller-time {
  font-size: 14px;
  opacity: 0.5;
}

.scroller-middle {
  margin: 10px 0;
  margin-left: 10px;
  display: flex;
  flex-direction: row;
  gap: 8px;
}

.scroller-range {
  writing-mode: vertical-rl;
  direction: ltr;
  height: 300px;
  width: 2px;
  -webkit-appearance: none;
  background: transparent;
}

.scroller-range::-webkit-slider-runnable-track {
  width: 1px;
  height: 100%;
  background-color: var(--scroller-background-color);
}

.scroller-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 6px;
  height: 60px;
  right: 2px;
  border-radius: 3px;
  background-color: var(--scroller-background-color);
  cursor: pointer;
}

.author-info-container {
  margin-top: 20px;
}

.scroller-range::-moz-range-track {
  width: 2px;
  height: 100%;
  background-color: #ccc;
  border-radius: 1px;
}

.scroller-range::-moz-range-thumb {
  width: 10px;
  height: 10px;
  background-color: #333;
  border-radius: 50%;
  cursor: pointer;
}

.scroller-index {
  font-size: 17px;
  font-weight: bold;
  margin-top: 10px;
}

.article-title-container {
  display: flex;
  flex-direction: column;
}

.article-title {
  font-size: 30px;
  font-weight: bold;
}

.article-info-container {
  display: flex;
  flex-direction: row;
  margin-top: 10px;
  gap: 10px;
  align-items: center;
}

.info-content-container {
  display: flex;
  flex-direction: row;
  gap: 10px;
  padding: 0px;
  border-bottom: 1px solid #e2e2e2;
}

.user-avatar-container {
  cursor: pointer;
}

.user-avatar-item {
  width: 50px;
  height: 50px;
}

.user-avatar-item-img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
}

.info-content {
  display: flex;
  width: 100%;
  flex-direction: column;
  gap: 10px;
}

.info-content-header {
  display: flex;
  flex-direction: row;
  gap: 10px;
  align-items: center;
  justify-content: space-between;
}

.user-name {
  font-size: 16px;
  font-weight: bold;
  opacity: 0.7;
}

.post-time {
  font-size: 14px;
  opacity: 0.5;
}

.info-content-text {
  font-size: 16px;
  line-height: 1.8;
  opacity: 0.7;
  width: 100%;
}

.article-footer-container {
  display: flex;
  flex-direction: row;
  gap: 10px;
  margin-top: 60px;
}

.reactions-container {
  display: flex;
  flex-direction: row;
  gap: 10px;
  align-items: center;
  width: 100%;
  justify-content: space-between;
}

.reactions-viewer {
  display: flex;
  flex-direction: row;
  gap: 20px;
  align-items: center;
}

.reactions-viewer-item-container {
  display: flex;
  flex-direction: row;
  gap: 2px;
  align-items: center;
}

.reactions-viewer-item {
  font-size: 16px;
}

.reactions-count {
  font-size: 16px;
  opacity: 0.5;
}

.make-reaction-container {
  display: flex;
  flex-direction: row;
  gap: 10px;
}

.make-reaction-item {
  cursor: pointer;
  padding: 10px;
  border-radius: 50%;
  opacity: 0.5;
  font-size: 20px;
}

.like-reaction {
  color: #ff0000;
}

.like-reaction:hover {
  background-color: #ffe2e2;
}

.copy-link:hover {
  background-color: #e2e2e2;
}
</style>
