<script setup>
const props = defineProps({
  slug: {
    type: String,
    required: true
  }
})

const comments = ref([])
const member = ref(null)
const loadingComments = ref(false)
const submitting = ref(false)
const submittingReplyId = ref('')
const likingCommentIds = ref([])
const errorMessage = ref('')
const replyErrorMessage = ref('')
const newCommentBody = ref('')
const replyBody = ref('')
const activeReplyTargetId = ref('')
const sortOption = ref('best')
const brokenAvatarCommentIds = ref([])

/**
 * 댓글 시간을 상대 시간 형식으로 변환한다.
 * @param {string} value - ISO 날짜 문자열
 * @returns {string} 표시용 문자열
 */
const formatCommentDate = (value) => {
  if (!value) {
    return ''
  }

  const date = new Date(value)
  if (Number.isNaN(date.getTime())) {
    return ''
  }

  const now = Date.now()
  const diffMs = now - date.getTime()

  if (diffMs < 60 * 1000) {
    return '방금 전'
  }

  if (diffMs < 60 * 60 * 1000) {
    return `${Math.max(1, Math.floor(diffMs / (60 * 1000)))}분 전`
  }

  if (diffMs < 24 * 60 * 60 * 1000) {
    return `${Math.max(1, Math.floor(diffMs / (60 * 60 * 1000)))}시간 전`
  }

  const sameYear = date.getFullYear() === new Date(now).getFullYear()
  return date.toLocaleDateString('ko-KR', sameYear
    ? { month: 'short', day: 'numeric' }
    : { year: 'numeric', month: 'short', day: 'numeric' })
}

/**
 * 댓글 작성자 아바타 이니셜을 생성한다.
 * @param {{ username?: string, email?: string }} user - 작성자 정보
 * @returns {string} 아바타 이니셜
 */
const getAvatarInitials = (user) => {
  const baseText = String(user?.username || user?.email || '').trim()
  if (!baseText) {
    return '@'
  }
  const tokens = baseText.split(/\s+/g).filter(Boolean)
  if (tokens.length >= 2) {
    return `${tokens[0].slice(0, 1)}${tokens[1].slice(0, 1)}`.toUpperCase()
  }
  return baseText.slice(0, 2).toUpperCase()
}

/**
 * 댓글 작성 시각 숫자값 반환
 * @param {string} value - ISO 날짜 문자열
 * @returns {number} 시간 숫자값
 */
const toTimeValue = (value) => {
  const date = new Date(value)
  if (Number.isNaN(date.getTime())) {
    return 0
  }
  return date.getTime()
}

/**
 * 아바타 이미지 깨짐 여부 확인
 * @param {string} commentId - 댓글 ID
 * @returns {boolean} 깨짐 여부
 */
const isAvatarBroken = (commentId) => brokenAvatarCommentIds.value.includes(commentId)

/**
 * 아바타 이미지 로드 실패를 기록한다.
 * @param {string} commentId - 댓글 ID
 * @returns {void}
 */
const markAvatarBroken = (commentId) => {
  if (isAvatarBroken(commentId)) {
    return
  }
  brokenAvatarCommentIds.value = [...brokenAvatarCommentIds.value, commentId]
}

/**
 * 회원 세션을 조회한다.
 * @returns {Promise<void>}
 */
const fetchMember = async () => {
  try {
    member.value = await $fetch('/api/auth/me')
  } catch {
    member.value = null
  }
}

/**
 * 댓글 목록을 조회한다.
 * @returns {Promise<void>}
 */
const fetchComments = async () => {
  loadingComments.value = true
  errorMessage.value = ''

  try {
    const response = await $fetch(`/api/posts/${props.slug}/comments`)
    comments.value = response.comments || []
  } catch (error) {
    comments.value = []
    errorMessage.value = error?.data?.message || '댓글을 불러오지 못했습니다.'
  } finally {
    loadingComments.value = false
  }
}

/**
 * 루트 댓글을 작성한다.
 * @returns {Promise<void>}
 */
const submitComment = async () => {
  const body = newCommentBody.value.trim()
  if (!body || submitting.value) {
    return
  }

  submitting.value = true
  errorMessage.value = ''

  try {
    await $fetch(`/api/posts/${props.slug}/comments`, {
      method: 'POST',
      body: {
        body
      }
    })
    newCommentBody.value = ''
    await fetchComments()
  } catch (error) {
    errorMessage.value = error?.data?.message || '댓글 작성에 실패했습니다.'
  } finally {
    submitting.value = false
  }
}

/**
 * 답글 작성 UI를 연다.
 * @param {string} commentId - 대상 댓글 ID
 * @returns {void}
 */
const openReplyForm = (commentId) => {
  activeReplyTargetId.value = commentId
  replyBody.value = ''
  replyErrorMessage.value = ''
}

/**
 * 답글 작성 UI를 닫는다.
 * @returns {void}
 */
const closeReplyForm = () => {
  activeReplyTargetId.value = ''
  replyBody.value = ''
  replyErrorMessage.value = ''
}

/**
 * 대댓글을 작성한다.
 * @param {string} parentId - 부모 댓글 ID
 * @returns {Promise<void>}
 */
const submitReply = async (parentId) => {
  const body = replyBody.value.trim()
  if (!body || submittingReplyId.value) {
    return
  }

  submittingReplyId.value = parentId
  replyErrorMessage.value = ''

  try {
    await $fetch(`/api/posts/${props.slug}/comments`, {
      method: 'POST',
      body: {
        body,
        parentId
      }
    })
    closeReplyForm()
    await fetchComments()
  } catch (error) {
    replyErrorMessage.value = error?.data?.message || '답글 작성에 실패했습니다.'
  } finally {
    submittingReplyId.value = ''
  }
}

/**
 * 특정 댓글의 좋아요 요청 진행 여부
 * @param {string} commentId - 댓글 ID
 * @returns {boolean} 진행 여부
 */
const isLikingComment = (commentId) => likingCommentIds.value.includes(commentId)

/**
 * 댓글 좋아요를 토글한다.
 * @param {string} commentId - 댓글 ID
 * @returns {Promise<void>}
 */
const toggleLike = async (commentId) => {
  if (!member.value || isLikingComment(commentId)) {
    return
  }

  likingCommentIds.value = [...likingCommentIds.value, commentId]
  errorMessage.value = ''

  try {
    const result = await $fetch(`/api/posts/${props.slug}/comments/${commentId}/like`, {
      method: 'POST'
    })

    comments.value = comments.value.map((item) => {
      if (item.id !== commentId) {
        return item
      }
      return {
        ...item,
        likeCount: Number(result.likeCount || 0),
        likedByMe: Boolean(result.liked)
      }
    })
  } catch (error) {
    errorMessage.value = error?.data?.message || '좋아요 처리에 실패했습니다.'
  } finally {
    likingCommentIds.value = likingCommentIds.value.filter((id) => id !== commentId)
  }
}

const rootComments = computed(() => comments.value.filter((item) => !item.parentId))
const sortedRootComments = computed(() => {
  const copied = [...rootComments.value]

  if (sortOption.value === 'latest') {
    return copied.sort((left, right) => toTimeValue(right.createdAt) - toTimeValue(left.createdAt))
  }

  if (sortOption.value === 'oldest') {
    return copied.sort((left, right) => toTimeValue(left.createdAt) - toTimeValue(right.createdAt))
  }

  return copied.sort((left, right) => {
    const likeDiff = Number(right.likeCount || 0) - Number(left.likeCount || 0)
    if (likeDiff !== 0) {
      return likeDiff
    }
    return toTimeValue(left.createdAt) - toTimeValue(right.createdAt)
  })
})
const repliesByParent = computed(() => {
  /** @type {Record<string, Array<any>>} */
  const grouped = {}

  for (const item of comments.value) {
    if (!item.parentId) {
      continue
    }
    if (!grouped[item.parentId]) {
      grouped[item.parentId] = []
    }
    grouped[item.parentId].push(item)
  }

  for (const parentId of Object.keys(grouped)) {
    grouped[parentId] = grouped[parentId].sort((left, right) => toTimeValue(left.createdAt) - toTimeValue(right.createdAt))
  }

  return grouped
})

onMounted(async () => {
  await Promise.all([fetchMember(), fetchComments()])
})
</script>

<template>
  <div class="post-comments text-sm">
    <div class="flex items-center justify-between gap-2">
      <p class="font-medium"><span class="site-muted">{{ comments.length }}</span> Comments</p>
      
    </div>

    <div class="mt-3 flex items-center gap-2 text-xs site-muted">
      <label for="comment-sort">Sort by:</label>
      <select
        id="comment-sort"
        v-model="sortOption"
        class="rounded-md border border-[var(--site-line)] bg-transparent px-2 py-1 text-xs font-semibold text-[var(--site-ink)] outline-none"
      >
        <option value="best">Best</option>
        <option value="latest">Latest</option>
        <option value="oldest">Oldest</option>
      </select>
    </div>

    <div class="mt-4">
      <div v-if="member" class="rounded-[10px] p-3">
        <p class="mb-2 text-xs site-muted">
          {{ member.username || member.email }} 님으로 댓글 작성
        </p>
        <textarea
          v-model="newCommentBody"
          rows="4"
          class="w-full rounded-[10px] border border-[var(--site-line)] bg-transparent px-3 py-2 outline-none focus-visible:border-[var(--site-accent)]"
          placeholder="댓글을 입력해 주세요."
        />
        <div class="mt-2 flex justify-end">
          <button
            type="button"
            class="site-accent-button rounded-[10px] px-3 py-1.5 text-xs font-semibold disabled:opacity-60"
            :disabled="submitting"
            @click="submitComment"
          >
            {{ submitting ? '등록 중...' : '댓글 등록' }}
          </button>
        </div>
      </div>

      <div v-else class="rounded-[10px] border border-[var(--site-line)] bg-[var(--site-bg)] p-3">
        <p class="site-muted">
          댓글은 로그인한 회원만 작성할 수 있습니다.
        </p>
        <NuxtLink to="/signin" class="mt-2 inline-flex rounded-[10px] border border-[var(--site-line)] px-3 py-1.5 text-xs font-semibold hover:opacity-80">
          로그인하러 가기
        </NuxtLink>
      </div>
    </div>

    <p v-if="errorMessage" class="mt-3 text-xs text-red-500">
      {{ errorMessage }}
    </p>

    <div class="mt-5">
      <p v-if="loadingComments" class="text-xs site-muted">
        댓글을 불러오는 중입니다.
      </p>

      <ul v-else-if="sortedRootComments.length > 0" class="flex flex-col divide-y divide-[var(--site-line)]">
        <li
          v-for="comment in sortedRootComments"
          :key="comment.id"
          class="py-4"
        >
          <div class="flex gap-3">
            <div class="flex w-8 flex-none flex-col items-center">
              <div class="h-8 w-8 min-h-8 min-w-8 overflow-hidden rounded-full border border-[var(--site-line)] bg-[var(--site-panel)]">
                <img
                  v-if="comment.user.avatarUrl && !isAvatarBroken(comment.id)"
                  :src="comment.user.avatarUrl"
                  :alt="`${comment.user.username} 아바타`"
                  class="block h-full w-full object-cover"
                  @error="markAvatarBroken(comment.id)"
                >
                <span
                  v-else
                  class="grid h-full w-full place-items-center text-[11px] font-semibold site-muted"
                >
                  {{ getAvatarInitials(comment.user) }}
                </span>
              </div>
            </div>
            <div class="min-w-0 flex-1">
              <div class="flex flex-wrap items-center gap-2">
                <strong class="text-sm">{{ comment.user.username }}</strong>
                <span class="text-xs site-muted">{{ formatCommentDate(comment.createdAt) }}</span>
              </div>
              <p class="mt-2 whitespace-pre-line leading-relaxed">
                {{ comment.body }}
              </p>

              <div class="mt-2 flex items-center gap-3 text-xs">
                <button
                  type="button"
                  class="group inline-flex items-center gap-1 site-muted hover:opacity-75 disabled:opacity-50"
                  :disabled="!member || isLikingComment(comment.id)"
                  @click="toggleLike(comment.id)"
                >
                  <svg
                    viewBox="0 0 16 16"
                    class="h-3.5 w-3.5"
                    fill="none"
                    xmlns="http://www.w3.org/2000/svg"
                  >
                    <path
                      d="M8 13.2 2.9 8.1a3.4 3.4 0 0 1 0-4.8 3.4 3.4 0 0 1 4.8 0L8 3.6l.3-.3a3.4 3.4 0 0 1 4.8 4.8L8 13.2Z"
                      :fill="comment.likedByMe ? 'currentColor' : 'none'"
                      stroke="currentColor"
                      stroke-width="1.2"
                    />
                  </svg>
                  <span>{{ comment.likeCount || 0 }}</span>
                </button>
                <button
                  v-if="member"
                  type="button"
                  class="group inline-flex items-center gap-1 site-muted hover:opacity-75"
                  @click="openReplyForm(comment.id)"
                >
                  <svg
                    viewBox="0 0 16 16"
                    class="h-3.5 w-3.5"
                    fill="none"
                    xmlns="http://www.w3.org/2000/svg"
                  >
                    <path
                      d="m1.5 7 4.7-4.2v2.6c4.7 0 8 2 8.3 6.5-1.5-2.3-3.3-3.1-8.3-3.1v2.4L1.5 7Z"
                      stroke="currentColor"
                      stroke-width="1.2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                  </svg>
                  답글
                </button>
              </div>
            </div>
          </div>

          <div v-if="activeReplyTargetId === comment.id" class="mt-2 rounded-[10px] border border-[var(--site-line)] p-2">
            <textarea
              v-model="replyBody"
              rows="3"
              class="w-full rounded-[10px] border border-[var(--site-line)] bg-transparent px-3 py-2 outline-none focus-visible:border-[var(--site-accent)]"
              placeholder="답글을 입력해 주세요."
            />
            <p v-if="replyErrorMessage" class="mt-2 text-xs text-red-500">
              {{ replyErrorMessage }}
            </p>
            <div class="mt-2 flex justify-end gap-2">
              <button type="button" class="rounded-[10px] border border-[var(--site-line)] px-3 py-1.5 text-xs" @click="closeReplyForm">
                취소
              </button>
              <button
                type="button"
                class="site-accent-button rounded-[10px] px-3 py-1.5 text-xs font-semibold disabled:opacity-60"
                :disabled="submittingReplyId === comment.id"
                @click="submitReply(comment.id)"
              >
                {{ submittingReplyId === comment.id ? '등록 중...' : '답글 등록' }}
              </button>
            </div>
          </div>

          <ul
            v-if="repliesByParent[comment.id]?.length"
            class="mt-3 ml-4 flex flex-col gap-3 border-l border-[var(--site-line)]/90 pl-4"
          >
            <li
              v-for="reply in repliesByParent[comment.id]"
              :key="reply.id"
              class="relative rounded-[10px] bg-[var(--site-panel)] p-2.5"
            >
              <span class="absolute -left-4 top-5 h-px w-3 bg-[var(--site-line)]/90" />
              <div class="flex gap-2.5">
                <div class="h-8 w-8 min-h-8 min-w-8 flex-none overflow-hidden rounded-full border border-[var(--site-line)] bg-[var(--site-bg)]">
                  <img
                    v-if="reply.user.avatarUrl && !isAvatarBroken(reply.id)"
                    :src="reply.user.avatarUrl"
                    :alt="`${reply.user.username} 아바타`"
                    class="block h-full w-full object-cover"
                    @error="markAvatarBroken(reply.id)"
                  >
                  <span
                    v-else
                    class="grid h-full w-full place-items-center text-[11px] font-semibold site-muted"
                  >
                    {{ getAvatarInitials(reply.user) }}
                  </span>
                </div>
                <div class="min-w-0 flex-1">
                  <div class="flex flex-wrap items-center gap-2">
                    <strong class="text-sm">{{ reply.user.username }}</strong>
                    <span class="text-xs site-muted">{{ formatCommentDate(reply.createdAt) }}</span>
                  </div>
                  <p class="mt-1 whitespace-pre-line leading-relaxed">
                    {{ reply.body }}
                  </p>
                  <div class="mt-1.5 text-xs">
                    <button
                      type="button"
                      class="inline-flex items-center gap-1 site-muted hover:opacity-75 disabled:opacity-50"
                      :disabled="!member || isLikingComment(reply.id)"
                      @click="toggleLike(reply.id)"
                    >
                      <svg
                        viewBox="0 0 16 16"
                        class="h-3.5 w-3.5"
                        fill="none"
                        xmlns="http://www.w3.org/2000/svg"
                      >
                        <path
                          d="M8 13.2 2.9 8.1a3.4 3.4 0 0 1 0-4.8 3.4 3.4 0 0 1 4.8 0L8 3.6l.3-.3a3.4 3.4 0 0 1 4.8 4.8L8 13.2Z"
                          :fill="reply.likedByMe ? 'currentColor' : 'none'"
                          stroke="currentColor"
                          stroke-width="1.2"
                        />
                      </svg>
                      <span>{{ reply.likeCount || 0 }}</span>
                    </button>
                  </div>
                </div>
              </div>
            </li>
          </ul>
        </li>
      </ul>

      <p v-else class="text-xs site-muted">
        첫 댓글을 남겨보세요.
      </p>
    </div>
  </div>
</template>

