<script setup>
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { api } from '../lib/api'
import { displayInitialFrom } from '../lib/display'
import { loginPath } from '../lib/paths'
import { toApiUrl } from '../lib/runtime'

const props = defineProps({
  tierListId: {
    type: String,
    default: '',
  },
  canWrite: {
    type: Boolean,
    default: false,
  },
  currentUserId: {
    type: String,
    default: '',
  },
  title: {
    type: String,
    default: '댓글',
  },
  description: {
    type: String,
    default: '티어표에 대한 의견을 남기고 답글로 대화를 이어갈 수 있어요.',
  },
})

const route = useRoute()
const comments = ref([])
const isLoading = ref(false)
const error = ref('')
const commentDraft = ref('')
const replyDrafts = ref({})
const openedReplyComposerId = ref('')
const submittingTargetId = ref('')
const deletingCommentId = ref('')
const replyInputRefs = ref({})
const visibleRootCount = ref(10)
const visibleReplyCounts = ref({})
let activeCommentRetryTimer = 0

const totalCommentCount = computed(() =>
  comments.value.reduce((count, comment) => count + 1 + (comment.replies?.length || 0), 0)
)
const visibleComments = computed(() => comments.value.slice(0, visibleRootCount.value))
const hasMoreRootComments = computed(() => comments.value.length > visibleRootCount.value)
const loginTarget = computed(() => loginPath(route.fullPath))
const highlightedCommentId = computed(() =>
  typeof route.query.commentId === 'string' ? route.query.commentId.trim() : ''
)

function avatarUrlOf(user) {
  return user?.authorAvatarSrc ? toApiUrl(user.authorAvatarSrc) : ''
}

function avatarFallbackOf(user) {
  return displayInitialFrom(user?.authorName, user?.authorAccountName, '?')
}

function displayNameOf(user) {
  return user?.authorName || '알 수 없음'
}

function formatDate(ts) {
  return new Date(Number(ts || 0)).toLocaleString('ko-KR', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  })
}

function isOwnComment(comment) {
  return !!props.currentUserId && props.currentUserId === comment?.authorId
}

function isHighlighted(commentId) {
  return !!commentId && highlightedCommentId.value === commentId
}

function clearActiveCommentRetry() {
  if (typeof window === 'undefined' || !activeCommentRetryTimer) return
  window.clearTimeout(activeCommentRetryTimer)
  activeCommentRetryTimer = 0
}

async function scrollToHighlightedComment(attempt = 0) {
  clearActiveCommentRetry()
  if (!highlightedCommentId.value || typeof document === 'undefined') return
  await nextTick()
  const target = document.querySelector(`[data-comment-id="${highlightedCommentId.value}"]`)
  if (target) {
    target.scrollIntoView({ behavior: attempt === 0 ? 'auto' : 'smooth', block: 'center' })
    return
  }
  if (attempt >= 6 || typeof window === 'undefined') return
  activeCommentRetryTimer = window.setTimeout(() => {
    scrollToHighlightedComment(attempt + 1)
  }, 180)
}

async function loadComments() {
  if (!props.tierListId) {
    comments.value = []
    return
  }
  isLoading.value = true
  error.value = ''
  try {
    const data = await api.listTierListComments(props.tierListId)
    comments.value = Array.isArray(data.comments) ? data.comments : []
    visibleRootCount.value = 10
    visibleReplyCounts.value = {}
    scrollToHighlightedComment()
  } catch (loadError) {
    error.value = '댓글을 불러오지 못했어요.'
  } finally {
    isLoading.value = false
  }
}

function visibleRepliesOf(comment) {
  const replies = Array.isArray(comment?.replies) ? comment.replies : []
  const limit = visibleReplyCounts.value[comment?.id] || 3
  return replies.slice(0, limit)
}

function hasMoreReplies(comment) {
  const replies = Array.isArray(comment?.replies) ? comment.replies : []
  return replies.length > (visibleReplyCounts.value[comment?.id] || 3)
}

function remainingReplyCount(comment) {
  const replies = Array.isArray(comment?.replies) ? comment.replies : []
  return Math.max(0, replies.length - (visibleReplyCounts.value[comment?.id] || 3))
}

const remainingRootCount = computed(() => Math.max(0, comments.value.length - visibleRootCount.value))

function showMoreRootComments() {
  visibleRootCount.value += 10
}

function showMoreReplies(commentId) {
  if (!commentId) return
  visibleReplyCounts.value = {
    ...visibleReplyCounts.value,
    [commentId]: (visibleReplyCounts.value[commentId] || 3) + 3,
  }
}

async function submitComment(parentCommentId = '') {
  if (!props.canWrite || !props.tierListId) return
  const isReply = !!parentCommentId
  const draftValue = isReply ? replyDrafts.value[parentCommentId] || '' : commentDraft.value
  const content = String(draftValue || '').trim()
  if (!content) return

  submittingTargetId.value = parentCommentId || 'root'
  error.value = ''
  try {
    const data = await api.createTierListComment(props.tierListId, {
      content,
      parentCommentId,
    })
    comments.value = Array.isArray(data.comments) ? data.comments : []
    if (isReply) {
      replyDrafts.value = { ...replyDrafts.value, [parentCommentId]: '' }
      openedReplyComposerId.value = ''
    } else {
      commentDraft.value = ''
    }
    await nextTick()
    if (data.createdCommentId && typeof window !== 'undefined') {
      const target = document.querySelector(`[data-comment-id="${data.createdCommentId}"]`)
      if (target) target.scrollIntoView({ behavior: 'smooth', block: 'center' })
    }
  } catch (submitError) {
    const code = submitError?.data?.error
    if (code === 'comment_reply_depth_invalid') {
      error.value = '답글에는 다시 답글을 달 수 없어요.'
    } else {
      error.value = '댓글을 저장하지 못했어요.'
    }
  } finally {
    submittingTargetId.value = ''
  }
}

async function deleteComment(commentId) {
  if (!commentId || !props.tierListId) return
  deletingCommentId.value = commentId
  error.value = ''
  try {
    const data = await api.deleteTierListComment(props.tierListId, commentId)
    comments.value = Array.isArray(data.comments) ? data.comments : []
  } catch (deleteError) {
    error.value = '댓글을 삭제하지 못했어요.'
  } finally {
    deletingCommentId.value = ''
  }
}

function registerReplyInput(commentId, element) {
  if (!commentId) return
  if (element) {
    replyInputRefs.value[commentId] = element
    return
  }
  delete replyInputRefs.value[commentId]
}

async function focusReplyInput(commentId) {
  if (!commentId) return
  await nextTick()
  const target = replyInputRefs.value[commentId]
  if (target && typeof target.focus === 'function') {
    target.focus()
    const value = target.value || ''
    if (typeof target.setSelectionRange === 'function') {
      target.setSelectionRange(value.length, value.length)
    }
  }
}

async function toggleReplyComposer(commentId) {
  const nextId = openedReplyComposerId.value === commentId ? '' : commentId
  openedReplyComposerId.value = nextId
  if (!nextId) return
  await focusReplyInput(nextId)
}

watch(() => props.tierListId, loadComments, { immediate: true })
watch(highlightedCommentId, () => {
  scrollToHighlightedComment()
})

onBeforeUnmount(() => {
  clearActiveCommentRetry()
})
</script>

<template>
  <section class="commentsCard">
    <header class="commentsCard__head">
      <div class="commentsCard__headline">
        <div class="commentsCard__eyebrow">Comments</div>
        <h3 class="commentsCard__title">{{ title }}</h3>
        <p class="commentsCard__desc">{{ description }}</p>
      </div>
      <div class="commentsCard__count">{{ totalCommentCount }}개</div>
    </header>

    <div v-if="error" class="commentsCard__error">{{ error }}</div>

    <div v-if="props.canWrite" class="commentsComposer">
      <textarea
        v-model="commentDraft"
        class="commentsComposer__input"
        maxlength="2000"
        rows="3"
        placeholder="이 티어표에 대한 의견을 남겨보세요."
      />
      <div class="commentsComposer__footer">
        <span class="commentsComposer__hint">{{ commentDraft.length }}/2000</span>
        <button class="btn btn--save commentsComposer__submit" type="button" :disabled="!commentDraft.trim() || submittingTargetId === 'root'" @click="submitComment()">
          댓글 등록
        </button>
      </div>
    </div>
    <div v-else class="commentsLoginCta">
      <div class="commentsLoginCta__text">로그인하면 댓글과 답글을 남길 수 있어요.</div>
      <RouterLink class="btn btn--ghost commentsComposer__submit" :to="loginTarget">로그인</RouterLink>
    </div>

    <div v-if="isLoading" class="commentsCard__empty">댓글을 불러오는 중이에요.</div>
    <div v-else-if="comments.length === 0" class="commentsCard__empty">아직 댓글이 없어요. 첫 댓글을 남겨보세요.</div>

    <div v-else class="commentsThread">
      <article
        v-for="comment in visibleComments"
        :key="comment.id"
        class="commentItem"
        :class="{ 'commentItem--highlighted': isHighlighted(comment.id) }"
        :data-comment-id="comment.id"
      >
        <div class="commentItem__head">
          <div class="commentItem__author">
            <img v-if="avatarUrlOf(comment)" class="commentItem__avatar" :src="avatarUrlOf(comment)" :alt="displayNameOf(comment)" draggable="false" />
            <div v-else class="commentItem__avatar commentItem__avatar--fallback">{{ avatarFallbackOf(comment) }}</div>
            <div class="commentItem__meta">
              <div class="commentItem__name">{{ displayNameOf(comment) }}</div>
              <div class="commentItem__date">{{ formatDate(comment.createdAt) }}</div>
            </div>
          </div>
          <div class="commentItem__actions">
            <button v-if="props.canWrite" class="commentItem__action" type="button" @click="toggleReplyComposer(comment.id)">답글</button>
            <button
              v-if="isOwnComment(comment)"
              class="commentItem__action commentItem__action--danger"
              type="button"
              :disabled="deletingCommentId === comment.id"
              @click="deleteComment(comment.id)"
            >
              삭제
            </button>
          </div>
        </div>
        <div class="commentItem__body">{{ comment.content }}</div>

        <div v-if="openedReplyComposerId === comment.id && props.canWrite" class="replyComposer">
          <textarea
            v-model="replyDrafts[comment.id]"
            :ref="(element) => registerReplyInput(comment.id, element)"
            class="commentsComposer__input commentsComposer__input--reply"
            maxlength="2000"
            rows="2"
            placeholder="답글을 입력하세요."
          />
          <div class="commentsComposer__footer">
            <span class="commentsComposer__hint">{{ (replyDrafts[comment.id] || '').length }}/2000</span>
            <button class="btn btn--save commentsComposer__submit" type="button" :disabled="!(replyDrafts[comment.id] || '').trim() || submittingTargetId === comment.id" @click="submitComment(comment.id)">
              답글 등록
            </button>
          </div>
        </div>

        <div v-if="comment.replies?.length" class="replyList">
          <article
            v-for="reply in visibleRepliesOf(comment)"
            :key="reply.id"
            class="commentItem commentItem--reply"
            :class="{ 'commentItem--highlighted': isHighlighted(reply.id) }"
            :data-comment-id="reply.id"
          >
            <div class="commentItem__head">
              <div class="commentItem__author">
                <img v-if="avatarUrlOf(reply)" class="commentItem__avatar" :src="avatarUrlOf(reply)" :alt="displayNameOf(reply)" draggable="false" />
                <div v-else class="commentItem__avatar commentItem__avatar--fallback">{{ avatarFallbackOf(reply) }}</div>
                <div class="commentItem__meta">
                  <div class="commentItem__name">{{ displayNameOf(reply) }}</div>
                  <div class="commentItem__date">{{ formatDate(reply.createdAt) }}</div>
                </div>
              </div>
              <div class="commentItem__actions">
                <button
                  v-if="isOwnComment(reply)"
                  class="commentItem__action commentItem__action--danger"
                  type="button"
                  :disabled="deletingCommentId === reply.id"
                  @click="deleteComment(reply.id)"
                >
                  삭제
                </button>
              </div>
            </div>
            <div class="commentItem__body">{{ reply.content }}</div>
          </article>
          <button v-if="hasMoreReplies(comment)" class="commentMoreButton" type="button" @click="showMoreReplies(comment.id)">
            답글 {{ remainingReplyCount(comment) }}개 더 보기
          </button>
        </div>
      </article>
      <button v-if="hasMoreRootComments" class="commentMoreButton commentMoreButton--root" type="button" @click="showMoreRootComments">
        댓글 {{ remainingRootCount }}개 더 보기
      </button>
    </div>
  </section>
</template>

<style scoped>
.commentsCard {
  margin-top: 24px;
  padding: 18px;
  border-radius: 22px;
  border: 1px solid var(--theme-border);
  background: var(--theme-pill-bg);
}

.commentsCard__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}

.commentsCard__headline {
  min-width: 0;
}

.commentsCard__eyebrow {
  font-size: 11px;
  font-weight: 900;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--theme-text-faint);
}

.commentsCard__title {
  margin: 6px 0 8px;
  font-size: 22px;
  font-weight: 900;
  letter-spacing: -0.04em;
}

.commentsCard__desc {
  margin: 0;
  color: var(--theme-text-muted);
  font-size: 12px;
  line-height: 1.55;
}

.commentsCard__count {
  flex: 0 0 auto;
  padding: 8px 12px;
  border-radius: 999px;
  border: 1px solid var(--theme-border);
  background: var(--theme-surface-soft);
  color: var(--theme-text-muted);
  font-size: 13px;
  font-weight: 800;
}

.commentsCard__error {
  margin-bottom: 16px;
  padding: 12px 14px;
  border-radius: 16px;
  border: 1px solid var(--theme-danger-border);
  background: var(--theme-danger-bg);
  color: var(--theme-text);
}

.commentsComposer,
.commentsLoginCta {
  margin-bottom: 18px;
  padding: 14px;
  border-radius: 18px;
  background: var(--theme-surface-soft);
}

.commentsComposer__input {
  width: 100%;
  min-height: 92px;
  resize: vertical;
  padding: 14px 16px;
  border-radius: 16px;
  border: 1px solid var(--theme-border);
  background: var(--theme-input-bg);
  color: var(--theme-text);
  box-sizing: border-box;
}

.commentsComposer__input--reply {
  min-height: 72px;
  background: var(--theme-input-bg);
}

.commentsComposer__input:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--theme-accent) 60%, var(--theme-field-border));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--theme-accent) 16%, transparent);
}

.commentsComposer__footer,
.commentsLoginCta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.commentsComposer__footer {
  margin-top: 12px;
}

.commentsComposer__hint,
.commentsLoginCta__text {
  color: var(--theme-text-muted);
  font-size: 13px;
}

.commentsCard__empty {
  padding: 20px 0;
  color: var(--theme-text-muted);
}

.commentsThread {
  display: grid;
  gap: 10px;
}

.commentMoreButton {
  justify-self: flex-start;
  margin-top: 10px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--theme-text-muted);
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
}

.commentMoreButton--root {
  margin-top: 4px;
}

.commentItem {
  position: relative;
  padding: 12px 0 0;
}

.commentItem--reply {
  margin-top: 8px;
  margin-left: 20px;
  padding-top: 8px;
}

.commentItem--reply::before {
  content: '';
  position: absolute;
  top: 0;
  left: -12px;
  width: 1px;
  bottom: 0;
  background: color-mix(in srgb, var(--theme-border) 82%, transparent);
}

.commentItem--highlighted {
  border-radius: 18px;
  background: color-mix(in srgb, var(--theme-accent) 10%, transparent);
}

.commentItem__head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}

.commentItem__author {
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}

.commentItem__avatar {
  width: 38px;
  height: 38px;
  border-radius: 999px;
  object-fit: cover;
  border: 1px solid var(--theme-avatar-border);
  background: var(--theme-border);
  flex: 0 0 auto;
}

.commentItem__avatar--fallback {
  display: grid;
  place-items: center;
  font-size: 14px;
  font-weight: 900;
}

.commentItem__meta {
  min-width: 0;
}

.commentItem__name {
  font-size: 14px;
  font-weight: 800;
}

.commentItem__date {
  margin-top: 4px;
  font-size: 12px;
  color: var(--theme-text-faint);
}

.commentItem__actions {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.commentItem__action {
  border: 0;
  background: transparent;
  color: var(--theme-text-muted);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}

.commentItem__action--danger {
  color: var(--theme-danger-text, #ff8f8f);
}

.commentItem__body {
  padding: 12px 14px;
  border-radius: 16px;
  border: 1px solid var(--theme-border);
  background: var(--theme-surface);
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.6;
}

.replyComposer {
  margin-top: 12px;
  padding: 12px;
  border-radius: 18px;
  background: var(--theme-surface-soft);
}

.commentsComposer__submit {
  min-width: 112px;
  min-height: 44px;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 12px 18px;
  border-radius: 14px;
  border: 1px solid var(--theme-border);
  background: var(--theme-surface-soft);
  color: var(--theme-text);
  cursor: pointer;
  font-size: 15px;
  font-weight: 700;
  transition: background 160ms ease;
}

.btn:hover {
  background: var(--theme-surface-soft-3);
}

.btn:disabled {
  opacity: 0.58;
  cursor: default;
}

.btn--save {
  min-width: 112px;
  font-weight: 900;
  background: rgba(96, 165, 250, 0.22);
  border-color: rgba(96, 165, 250, 0.36);
}

.btn--save:hover {
  background: rgba(96, 165, 250, 0.3);
}

.btn--ghost {
  background: color-mix(in srgb, var(--theme-surface-soft) 86%, transparent);
}

@media (max-width: 860px) {
  .commentsCard {
    padding: 18px 16px;
  }

  .commentsCard__head,
  .commentsComposer__footer,
  .commentsLoginCta,
  .commentItem__head {
    flex-direction: column;
    align-items: stretch;
  }

  .commentItem--reply {
    margin-left: 14px;
  }

  .commentItem--reply::before {
    left: -8px;
  }
}
</style>
