<script lang="ts" setup>
import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import dayjs from 'dayjs'
import * as select from '@zag-js/select'
import { CircleAlert, CircleCheck, Play } from '@lucide/vue'
import {
  AlertRoot,
  AlertTitle,
  AlertDescription,
  AlertCloseTrigger,
} from '@/components/ui/alert'
import { Badge } from '@/components/ui/badge'
import { Box } from '@/components/ui/box'
import { Button } from '@/components/ui/button'
import { Field, FieldLabel } from '@/components/ui/field'
import { Textarea } from '@/components/ui/textarea'
import {
  SelectRoot,
  SelectControl,
  SelectTrigger,
  SelectValueText,
  SelectContent,
  SelectItemGroup,
  SelectItem,
  SelectItemText,
} from '@/components/ui/select'
import { usePermissions } from '@/composables/usePermissions'
import { getApiErrorMessage } from '@/core/utils/getApiErrorMessage'
import {
  deleteFeedback,
  fetchFeedbackDocument,
  getFeedback,
  updateFeedback,
} from '../services/feedback.service'
import {
  FEEDBACK_STATUS_OPTIONS,
  feedbackPriorityLabel,
  feedbackStatusLabel,
  feedbackTypeLabel,
  type Feedback,
  type FeedbackDocument,
  type FeedbackStatus,
} from '../types/feedback.types'

type SelectOption = { label: string; value: string }

type MediaPreviewKind = 'image' | 'video'

function createSelectCollection(options: SelectOption[]) {
  return select.collection({
    items: options,
    itemToValue: (item) => item.label,
  })
}

function labelToValue(options: SelectOption[], label: string | undefined): string {
  if (!label) return options[0]?.value ?? ''
  return options.find((option) => option.label === label)?.value ?? ''
}

function valueToLabel(options: SelectOption[], value: string): string[] {
  const option = options.find((item) => item.value === value)
  return option ? [option.label] : options[0] ? [options[0].label] : []
}

const route = useRoute()
const router = useRouter()
const { hasPermission } = usePermissions()

const feedback = ref<Feedback | null>(null)
const loading = ref(true)
const saving = ref(false)
const deleting = ref(false)
const error = ref<string | null>(null)
const successMessage = ref<string | null>(null)

const mediaObjectUrls = ref<Record<string, string>>({})
const mediaLoadErrors = ref<Record<string, boolean>>({})

const previewOpen = ref(false)
const previewLoading = ref(false)
const previewDocument = ref<FeedbackDocument | null>(null)
const previewKind = ref<MediaPreviewKind>('image')
const previewUrl = ref<string | null>(null)

const adminForm = reactive({
  status: 'open' as FeedbackStatus,
  admin_notes: '',
})

const canUpdate = computed(() => hasPermission('kemaskini maklum balas'))
const canDelete = computed(() => hasPermission('padam maklum balas'))

const statusCollection = createSelectCollection(FEEDBACK_STATUS_OPTIONS)
const statusInitial = computed(() => valueToLabel(FEEDBACK_STATUS_OPTIONS, adminForm.status))

function setStatusValue(details: { value: string[] }) {
  adminForm.status = labelToValue(FEEDBACK_STATUS_OPTIONS, details.value[0]) as FeedbackStatus
}

function formatDate(value: string | null | undefined): string {
  if (!value) return '-'
  return dayjs(value).format('DD MMM YYYY, HH:mm')
}

function formatFileSize(bytes: number | null | undefined): string {
  if (!bytes) return '-'
  if (bytes < 1024) return `${bytes} B`
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}

function revokeUrl(url: string | null | undefined) {
  if (url) {
    window.URL.revokeObjectURL(url)
  }
}

function clearMediaObjectUrls() {
  Object.values(mediaObjectUrls.value).forEach((url) => revokeUrl(url))
  mediaObjectUrls.value = {}
  mediaLoadErrors.value = {}
}

function revokePreviewUrl() {
  if (previewUrl.value && !Object.values(mediaObjectUrls.value).includes(previewUrl.value)) {
    revokeUrl(previewUrl.value)
  }
  previewUrl.value = null
}

async function loadMediaDocument(document: FeedbackDocument): Promise<string | null> {
  if (mediaObjectUrls.value[document.id]) {
    return mediaObjectUrls.value[document.id] ?? null
  }

  if (!feedback.value) return null

  try {
    const blob = await fetchFeedbackDocument(
      feedback.value.id,
      document.id,
      document.mime_type,
    )
    const objectUrl = window.URL.createObjectURL(blob)
    mediaObjectUrls.value = {
      ...mediaObjectUrls.value,
      [document.id]: objectUrl,
    }
    return objectUrl
  } catch {
    mediaLoadErrors.value = {
      ...mediaLoadErrors.value,
      [document.id]: true,
    }
    return null
  }
}

async function preloadAttachments(item: Feedback) {
  clearMediaObjectUrls()

  const documents = [...(item.images ?? []), ...(item.videos ?? [])]
  await Promise.all(documents.map((document) => loadMediaDocument(document)))
}

async function openMediaPreview(document: FeedbackDocument, kind: MediaPreviewKind) {
  previewDocument.value = document
  previewKind.value = kind
  previewOpen.value = true
  previewLoading.value = true
  revokePreviewUrl()

  const objectUrl = await loadMediaDocument(document)
  previewUrl.value = objectUrl
  previewLoading.value = false

  if (!objectUrl) {
    error.value = 'Gagal memuatkan fail lampiran.'
    closeMediaPreview()
  }
}

function closeMediaPreview() {
  previewOpen.value = false
  previewDocument.value = null
  previewLoading.value = false
  revokePreviewUrl()
}

async function fetchDetail() {
  loading.value = true
  error.value = null
  closeMediaPreview()

  try {
    const id = String(route.params.id)
    feedback.value = await getFeedback(id)
    adminForm.status = feedback.value.status
    adminForm.admin_notes = feedback.value.admin_notes ?? ''
    await preloadAttachments(feedback.value)
  } catch (err) {
    error.value = getApiErrorMessage(err, 'Gagal memuatkan maklum balas.')
    feedback.value = null
    clearMediaObjectUrls()
  } finally {
    loading.value = false
  }
}

async function handleSave() {
  if (!feedback.value || saving.value || !canUpdate.value) return

  saving.value = true
  error.value = null
  successMessage.value = null

  try {
    const response = await updateFeedback(feedback.value.id, {
      status: adminForm.status,
      admin_notes: adminForm.admin_notes.trim() || null,
    })
    feedback.value = response.data
    adminForm.status = response.data.status
    adminForm.admin_notes = response.data.admin_notes ?? ''
    successMessage.value = response.message ?? 'Maklum balas berjaya dikemas kini.'
    await preloadAttachments(response.data)
  } catch (err) {
    error.value = getApiErrorMessage(err, 'Gagal mengemas kini maklum balas.')
  } finally {
    saving.value = false
  }
}

async function handleDelete() {
  if (!feedback.value || deleting.value || !canDelete.value) return
  if (!window.confirm('Padam maklum balas ini?')) return

  deleting.value = true
  error.value = null

  try {
    await deleteFeedback(feedback.value.id)
    router.push({ name: 'list-feedback' })
  } catch (err) {
    error.value = getApiErrorMessage(err, 'Gagal memadam maklum balas.')
  } finally {
    deleting.value = false
  }
}

watch(
  () => route.params.id,
  () => {
    fetchDetail()
  },
)

onMounted(fetchDetail)

onUnmounted(() => {
  closeMediaPreview()
  clearMediaObjectUrls()
})
</script>

<template>
  <div>
    <div class="flex flex-col gap-3 sm:flex-row sm:items-start">
      <div class="mr-auto">
        <Button look="outline" size="sm" @click="router.push({ name: 'list-feedback' })">
          Kembali
        </Button>
        <h2 class="mt-3 text-lg font-medium">Butiran Maklum Balas</h2>
        <p class="mt-1 text-sm opacity-70">Semak dan kemas kini status laporan.</p>
      </div>
      <div v-if="canDelete" class="flex gap-2">
        <Button look="outline" variant="danger" :disabled="deleting" @click="handleDelete">
          {{ deleting ? 'Memadam...' : 'Padam' }}
        </Button>
      </div>
    </div>

    <AlertRoot v-if="error" class="mt-6" variant="danger">
      <CircleAlert class="size-4" />
      <AlertTitle>Ralat</AlertTitle>
      <AlertDescription>{{ error }}</AlertDescription>
      <AlertCloseTrigger @click="error = null" />
    </AlertRoot>

    <AlertRoot v-if="successMessage" class="mt-6" variant="success">
      <CircleCheck class="size-4" />
      <AlertTitle>Berjaya</AlertTitle>
      <AlertDescription>{{ successMessage }}</AlertDescription>
      <AlertCloseTrigger @click="successMessage = null" />
    </AlertRoot>

    <div v-if="loading" class="mt-8 opacity-70">Memuatkan...</div>

    <template v-else-if="feedback">
      <div class="mt-6 grid gap-6 lg:grid-cols-3">
        <Box class="space-y-4 p-6 lg:col-span-2">
          <div class="flex flex-wrap items-center gap-2">
            <Badge look="outline">{{ feedbackTypeLabel(feedback.type) }}</Badge>
            <Badge look="outline">{{ feedbackPriorityLabel(feedback.priority) }}</Badge>
            <Badge look="outline">{{ feedbackStatusLabel(feedback.status) }}</Badge>
          </div>

          <div>
            <h3 class="text-xl font-semibold">{{ feedback.title }}</h3>
            <p class="mt-2 whitespace-pre-wrap text-sm opacity-80">{{ feedback.description }}</p>
          </div>

          <div v-if="feedback.page_url" class="text-sm">
            <span class="opacity-60">URL:</span>
            <a :href="feedback.page_url" target="_blank" rel="noopener" class="ml-2 text-primary underline">
              {{ feedback.page_url }}
            </a>
          </div>

          <div v-if="feedback.steps_to_reproduce" class="text-sm">
            <div class="font-medium">Langkah menghasilkan semula</div>
            <p class="mt-1 whitespace-pre-wrap opacity-80">{{ feedback.steps_to_reproduce }}</p>
          </div>

          <div class="grid gap-4 sm:grid-cols-2">
            <div v-if="feedback.expected_behavior" class="text-sm">
              <div class="font-medium">Kelakuan dijangka</div>
              <p class="mt-1 whitespace-pre-wrap opacity-80">{{ feedback.expected_behavior }}</p>
            </div>
            <div v-if="feedback.actual_behavior" class="text-sm">
              <div class="font-medium">Kelakuan sebenar</div>
              <p class="mt-1 whitespace-pre-wrap opacity-80">{{ feedback.actual_behavior }}</p>
            </div>
          </div>

          <div v-if="feedback.additional_notes" class="text-sm">
            <div class="font-medium">Nota tambahan</div>
            <p class="mt-1 whitespace-pre-wrap opacity-80">{{ feedback.additional_notes }}</p>
          </div>

          <div v-if="feedback.images?.length" class="text-sm">
            <div class="font-medium">Imej</div>
            <div class="mt-3 grid grid-cols-2 gap-3 sm:grid-cols-3">
              <button v-for="image in feedback.images" :key="image.id" type="button"
                class="group relative overflow-hidden rounded-lg border border-foreground/10 bg-foreground/5 text-left"
                @click="openMediaPreview(image, 'image')">
                <img v-if="mediaObjectUrls[image.id]" :src="mediaObjectUrls[image.id]" :alt="image.name"
                  class="aspect-video w-full object-cover transition group-hover:scale-[1.02]" />
                <div v-else class="flex aspect-video items-center justify-center px-2 text-center text-xs opacity-60">
                  {{ mediaLoadErrors[image.id] ? 'Gagal dimuatkan' : 'Memuatkan...' }}
                </div>
                <div class="truncate border-t border-foreground/10 px-2 py-1.5 text-xs opacity-70">
                  {{ image.name }}
                </div>
              </button>
            </div>
          </div>

          <div v-if="feedback.videos?.length" class="text-sm">
            <div class="font-medium">Video</div>
            <div class="mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2">
              <button v-for="video in feedback.videos" :key="video.id" type="button"
                class="group relative overflow-hidden rounded-lg border border-foreground/10 bg-foreground/5 text-left"
                @click="openMediaPreview(video, 'video')">
                <div class="relative aspect-video bg-black/80">
                  <video v-if="mediaObjectUrls[video.id]" :src="mediaObjectUrls[video.id]"
                    class="size-full object-cover opacity-80" muted preload="metadata" />
                  <div v-else class="flex size-full items-center justify-center px-2 text-center text-xs text-white/70">
                    {{ mediaLoadErrors[video.id] ? 'Gagal dimuatkan' : 'Memuatkan...' }}
                  </div>
                  <div
                    class="absolute inset-0 flex items-center justify-center bg-black/20 transition group-hover:bg-black/35">
                    <span class="flex size-12 items-center justify-center rounded-full bg-white/90 text-foreground">
                      <Play class="size-5 fill-current" />
                    </span>
                  </div>
                </div>
                <div class="truncate border-t border-foreground/10 px-2 py-1.5 text-xs opacity-70">
                  {{ video.name }}
                </div>
              </button>
            </div>
          </div>
        </Box>

        <div class="space-y-6">
          <Box class="space-y-3 p-6 text-sm">
            <div>
              <div class="opacity-60">Penghantar</div>
              <div class="mt-0.5 font-medium">{{ feedback.user?.name ?? 'Tetamu' }}</div>
              <div v-if="feedback.user?.email" class="opacity-70">{{ feedback.user.email }}</div>
            </div>
            <div>
              <div class="opacity-60">Dihantar</div>
              <div class="mt-0.5">{{ formatDate(feedback.created_at) }}</div>
            </div>
            <div v-if="feedback.resolved_at">
              <div class="opacity-60">Diselesaikan</div>
              <div class="mt-0.5">{{ formatDate(feedback.resolved_at) }}</div>
            </div>
            <div v-if="feedback.assigned_user">
              <div class="opacity-60">Ditugaskan kepada</div>
              <div class="mt-0.5 font-medium">{{ feedback.assigned_user.name }}</div>
            </div>
          </Box>

          <Box v-if="canUpdate" class="space-y-4 p-6">
            <h3 class="font-medium">Tindakan Admin</h3>

            <Field>
              <FieldLabel>Status</FieldLabel>
              <SelectRoot :key="statusInitial[0]" :collection="statusCollection" :default-value="statusInitial"
                @value-change="setStatusValue">
                <SelectControl>
                  <SelectTrigger>
                    <SelectValueText placeholder="Status" />
                  </SelectTrigger>
                </SelectControl>
                <SelectContent>
                  <SelectItemGroup>
                    <SelectItem v-for="item in statusCollection.items" :key="item.label" :item="item">
                      <SelectItemText>{{ item.label }}</SelectItemText>
                    </SelectItem>
                  </SelectItemGroup>
                </SelectContent>
              </SelectRoot>
            </Field>

            <Field>
              <FieldLabel>Nota admin</FieldLabel>
              <Textarea v-model="adminForm.admin_notes" rows="4" />
            </Field>

            <Button variant="primary" :disabled="saving" @click="handleSave">
              {{ saving ? 'Menyimpan...' : 'Simpan' }}
            </Button>
          </Box>
        </div>
      </div>
    </template>
  </div>

  <Teleport to="body">
    <div v-if="previewOpen" class="fixed inset-0 z-70 flex items-center justify-center p-4 sm:p-6" role="dialog"
      aria-modal="true" :aria-label="previewDocument?.name ?? 'Pratonton lampiran'">
      <button type="button" class="absolute inset-0 bg-black/80" aria-label="Tutup pratonton"
        @click="closeMediaPreview" />

      <div
        class="relative z-10 flex w-full max-w-5xl flex-col overflow-hidden rounded-2xl border border-foreground/10 bg-background shadow-2xl">
        <div class="border-b border-foreground/10 px-5 py-4">
          <div class="text-lg font-medium">
            {{ previewKind === 'video' ? 'Main Video' : 'Pratonton Imej' }}
          </div>
          <div v-if="previewDocument" class="mt-1 text-sm opacity-70">
            {{ previewDocument.name }} · {{ formatFileSize(previewDocument.file_size) }}
          </div>
        </div>

        <div class="overflow-auto p-5">
          <div v-if="previewLoading" class="py-12 text-center opacity-70">
            Memuatkan...
          </div>

          <div v-else-if="previewUrl && previewKind === 'image'" class="flex justify-center">
            <img :src="previewUrl" :alt="previewDocument?.name ?? 'Pratonton imej'"
              class="block h-auto max-h-[calc(90vh-12rem)] w-auto max-w-full object-contain" />
          </div>

          <div v-else-if="previewUrl && previewKind === 'video'" class="flex justify-center">
            <video :src="previewUrl" class="block max-h-[calc(90vh-12rem)] w-full max-w-full rounded-lg bg-black"
              controls autoplay />
          </div>
        </div>

        <div class="flex justify-end gap-2 border-t border-foreground/10 px-5 py-4">
          <Button type="button" look="outline" @click="closeMediaPreview">
            Tutup
          </Button>
        </div>
      </div>
    </div>
  </Teleport>
</template>
