<template>
  <div class="mt-4 lg:mt-6">
    <UCard variant="subtle" :ui="{ body: 'p-4 sm:p-5', header: 'p-4 sm:p-5', footer: 'p-4 sm:p-5' }">
      <template #header>
        <div class="flex items-center justify-between gap-3">
          <div class="min-w-0">
            <p class="text-sm font-medium text-highlighted">
              {{ $t('comments.title') }}
            </p>
            <p class="text-xs text-muted">
              {{ $t('comments.count', { count: commentCount }) }}
            </p>
          </div>
          <UButton color="neutral" variant="ghost" size="sm" icon="i-lucide-x" @click="$emit('close')" />
        </div>
      </template>

      <div v-if="comments && comments.length > 0" class="relative">
        <UProgress
          v-if="isLoadingMore"
          indeterminate
          size="xs"
          class="absolute top-0 inset-x-0 z-10"
          :ui="{ base: 'bg-default' }"
        />

        <UScrollArea ref="scrollAreaRef" class="max-h-96" :ui="{ viewport: 'space-y-4 pe-2' }">
          <div v-for="comment in comments" :key="comment.id" class="space-y-2">
            <div class="flex items-start justify-between gap-2">
              <div class="flex items-center gap-2 min-w-0">
                <UAvatar icon="i-lucide-user" size="2xs" />
                <div class="min-w-0">
                  <p class="text-sm text-highlighted truncate">
                    {{ comment.createdBy.name }}
                  </p>
                  <p class="text-xs text-muted">
                    {{ comment.createdAt ? formatDate(comment.createdAt) : '-' }}
                  </p>
                </div>
              </div>

              <div class="flex items-center gap-1 shrink-0">
                <UDropdownMenu
                  v-if="isCommentByUser(comment)"
                  :items="[createCommentActions(comment)]"
                  :content="{ align: 'end' }"
                >
                  <UButton icon="i-lucide-ellipsis" color="neutral" variant="ghost" size="xs" square />
                </UDropdownMenu>
              </div>
            </div>

            <div class="rounded-md ring-1 ring-inset ring-default bg-default">
              <!-- Edit comment form -->
              <template v-if="editingCommentId === comment.id">
                <UEditor
                  v-slot="{ editor }"
                  v-model="editingCommentEditorContent"
                  content-type="json"
                  :editable="true"
                  :ui="{
                    root: 'bg-transparent',
                    content: 'bg-transparent',
                    base: 'min-h-[120px] p-3 bg-transparent'
                  }"
                >
                  <UEditorToolbar
                    :editor="editor"
                    :items="toolbarItems"
                    class="border-b border-default px-3 py-2 bg-default/50 overflow-x-auto"
                  />
                </UEditor>

                <div class="flex flex-col sm:flex-row gap-2 sm:justify-end p-3 border-t border-default">
                  <UButton color="neutral" variant="outline" @click="cancelEditComment">
                    {{ $t('common.cancel') }}
                  </UButton>
                  <UButton @click="saveEditedComment(comment)">
                    {{ $t('comments.saveChanges') }}
                  </UButton>
                </div>
              </template>

              <!-- Display comment content when not editing -->
              <UEditor
                v-else
                :key="`${comment.id}:${comment.modifiedAt?.toISOString?.() ?? ''}`"
                :model-value="getCommentEditorModelValue(comment)"
                content-type="json"
                :editable="false"
                :ui="{
                  root: 'bg-transparent',
                  content: 'bg-transparent',
                  base: 'p-3 bg-transparent'
                }"
              />
            </div>
          </div>
        </UScrollArea>
      </div>

      <UEmpty v-else :title="$t('comments.empty')" icon="i-lucide-message-square" class="py-6" />

      <template #footer>
        <div class="space-y-3">
          <div class="rounded-md ring-1 ring-inset ring-default bg-default">
            <UEditor
              v-slot="{ editor }"
              v-model="newCommentEditorContent"
              content-type="json"
              :editable="true"
              :placeholder="$t('comments.placeholder')"
              :ui="{
                root: 'bg-transparent',
                content: 'bg-transparent',
                base: 'min-h-[120px] p-3 bg-transparent'
              }"
            >
              <UEditorToolbar
                :editor="editor"
                :items="toolbarItems"
                class="border-b border-default px-3 py-2 bg-transparent overflow-x-auto"
              />
            </UEditor>
          </div>

          <div class="flex flex-col sm:flex-row gap-2 sm:justify-end">
            <UButton @click="submitComment(formElementId)">
              {{ $t('comments.submit') }}
            </UButton>
          </div>
        </div>
      </template>
    </UCard>
  </div>
</template>

<script setup lang="ts">
import type { CommentDto } from '~~/.api-client'
import type { JSONContent } from '@tiptap/vue-3'
import type { DropdownMenuItem } from '@nuxt/ui'
import { useInfiniteScroll } from '@vueuse/core'
import { useCommentStore } from '~~/stores/useCommentStore'
import { useUserStore } from '~~/stores/useUserStore'

const props = defineProps<{
  formElementId: string
  applicationFormId: string
  comments?: CommentDto[]
  totalCount?: number
}>()

defineEmits<{
  (e: 'close'): void
}>()

const commentStore = useCommentStore()
const { loadMore } = commentStore
const userStore = useUserStore()
const { user } = storeToRefs(userStore)
const toast = useToast()
const { t: $t } = useI18n()

const scrollAreaRef = useTemplateRef('scrollAreaRef')
const scrollContainerEl = ref<HTMLElement | null>(null)

const commentCount = computed(() => props.totalCount ?? props.comments?.length ?? 0)

const commentCursorSate = computed(
  () => commentStore.nextCursorByApplicationFormId[props.applicationFormId]?.[props.formElementId]
)
const canLoadMore = computed(() => commentCursorSate.value?.hasMore === true)
const isLoadingMore = computed(() => commentCursorSate.value?.isLoading === true)
const newCommentValue = ref<string>('')

const newCommentEditorContent = computed<JSONContent>({
  get: () => toEditorJson(newCommentValue.value),
  set: (newValue) => {
    newCommentValue.value = JSON.stringify(newValue)
  }
})
const editingCommentId = ref<string | null>(null)

const editingCommentValue = ref<string>('')
const editingCommentEditorContent = computed<JSONContent>({
  get: () => toEditorJson(editingCommentValue.value),
  set: (newValue) => {
    editingCommentValue.value = JSON.stringify(newValue)
  }
})

watch(
  () => scrollAreaRef.value,
  async (scrollAreaComponent) => {
    if (!scrollAreaComponent) {
      scrollContainerEl.value = null
      return
    }

    await nextTick()

    const rootEl = scrollAreaComponent.$el as HTMLElement | undefined
    if (!rootEl) return

    scrollContainerEl.value = rootEl

    // Wait another tick for content to be measured, then scroll to bottom
    await nextTick()
    // Use requestAnimationFrame to ensure layout is complete
    requestAnimationFrame(() => {
      rootEl.scrollTop = rootEl.scrollHeight
    })
  },
  { immediate: true }
)

useInfiniteScroll(
  scrollContainerEl,
  async () => {
    const scrollEl = scrollContainerEl.value
    if (!scrollEl) return

    const previousScrollHeight = scrollEl.scrollHeight
    await loadMore(props.applicationFormId, props.formElementId)
    // Maintain scroll position after prepending older comments
    await nextTick()
    const newScrollHeight = scrollEl.scrollHeight
    scrollEl.scrollTop = newScrollHeight - previousScrollHeight + scrollEl.scrollTop
  },
  {
    direction: 'top',
    distance: 100,
    canLoadMore: () => canLoadMore.value && !isLoadingMore.value
  }
)

async function submitComment(formElementId: string) {
  if (!newCommentValue.value.trim()) {
    return
  }

  try {
    await commentStore.createComment(props.applicationFormId, formElementId, { message: newCommentValue.value })
    newCommentValue.value = ''
    toast.add({ title: $t('comments.created'), color: 'success' })
  } catch {
    toast.add({ title: $t('comments.createError'), color: 'error' })
  }
}

function startEditComment(comment: CommentDto) {
  editingCommentId.value = comment.id
  editingCommentValue.value = comment.message || ''
}

function cancelEditComment() {
  editingCommentId.value = null
  editingCommentValue.value = ''
}

async function saveEditedComment(comment: CommentDto) {
  try {
    const updatedComment: CommentDto = {
      ...comment,
      message: editingCommentValue.value,
      modifiedAt: new Date()
    }

    await commentStore.updateComment(comment.id, updatedComment)
    cancelEditComment()
    toast.add({ title: $t('comments.updated'), color: 'success' })
  } catch {
    toast.add({ title: $t('comments.updateError'), color: 'error' })
  }
}

function isCommentByUser(comment: CommentDto) {
  return comment.createdBy.keycloakId === user.value?.keycloakId
}

function createCommentActions(comment: CommentDto): DropdownMenuItem[] {
  return [
    {
      label: $t('comments.editAction'),
      icon: 'i-lucide-pencil',
      onClick: () => startEditComment(comment)
    }
  ]
}

function getCommentEditorModelValue(comment: CommentDto): JSONContent {
  return toEditorJson(comment.message)
}

function toEditorJson(rawValue: string | undefined): JSONContent {
  const raw = (rawValue ?? '').trim()
  if (raw) {
    try {
      if (raw.startsWith('{')) {
        return JSON.parse(raw) as JSONContent
      }
    } catch {
      // fall through to plain text
    }
    return wrapPlainTextAsDoc(raw)
  }

  return wrapPlainTextAsDoc('')
}

function wrapPlainTextAsDoc(text: string): JSONContent {
  return {
    type: 'doc',
    content: [
      {
        type: 'paragraph',
        content: text
          ? [
              {
                type: 'text',
                text
              }
            ]
          : []
      }
    ]
  }
}

const toolbarItems = [
  [
    { kind: 'undo', icon: 'i-lucide-undo' },
    { kind: 'redo', icon: 'i-lucide-redo' }
  ],
  [
    { kind: 'heading', level: 1, icon: 'i-lucide-heading-1', label: 'H1' },
    { kind: 'heading', level: 2, icon: 'i-lucide-heading-2', label: 'H2' },
    { kind: 'heading', level: 3, icon: 'i-lucide-heading-3', label: 'H3' }
  ],
  [
    { kind: 'mark', mark: 'bold', icon: 'i-lucide-bold' },
    { kind: 'mark', mark: 'italic', icon: 'i-lucide-italic' },
    { kind: 'mark', mark: 'underline', icon: 'i-lucide-underline' },
    { kind: 'mark', mark: 'strike', icon: 'i-lucide-strikethrough' }
  ],
  [
    { kind: 'bulletList', icon: 'i-lucide-list' },
    { kind: 'orderedList', icon: 'i-lucide-list-ordered' }
  ],
  [
    { kind: 'blockquote', icon: 'i-lucide-quote' },
    { kind: 'codeBlock', icon: 'i-lucide-code' }
  ],
  [{ kind: 'link', icon: 'i-lucide-link' }]
]
</script>
