<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import dayjs from 'dayjs'
import { CircleAlert, CircleCheck, Download, Eye, Pencil, Trash2 } 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 { DialogRoot, DialogContent, DialogCloseTrigger } from '@/components/ui/dialog'
import { Field, FieldError, FieldLabel } from '@/components/ui/field'
import { Input } from '@/components/ui/input'
import { TabsRoot, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'
import { Textarea } from '@/components/ui/textarea'
import { usePermissions } from '@/composables/usePermissions'
import { getApiErrorMessage, getApiValidationErrors } from '@/core/utils/getApiErrorMessage'
import {
  completeMembershipApplication,
  deleteMembershipApplicationDocument,
  downloadMembershipApplicationDocument,
  fetchMembershipApplicationDocument,
  getMembershipApplication,
  submitBoardReview,
  submitManagementReview,
} from '../services/membership-application.service'
import {
  reviewDecisionBadgeLook,
  reviewDecisionBadgeVariant,
  statusBadgeLook,
  statusBadgeVariant,
} from '../utils/membership-application-badge.utils'
import type {
  BoardReviewDecision,
  ManagementReviewDecision,
  MembershipApplicationDetail,
  MembershipApplicationDocumentDetail,
  MembershipApplicationReferenceDetail,
  MembershipApplicationReviewDetail,
  MembershipApplicationStatus,
} from '../types/membership-application.types'
import {
  ADMIN_ATTACHMENT_DOCUMENT_TYPE,
  RESULT_LETTER_DOCUMENT_TYPE,
} from '../types/membership-application.types'

const WORKFLOW_STEPS = [
  { id: 1, label: 'Dihantar' },
  { id: 2, label: 'Semakan Pentadbiran' },
  { id: 3, label: 'Keputusan Ahli Lembaga Koperasi (ALK)' },
  { id: 4, label: 'Makluman Keputusan' },
  { id: 5, label: 'Selesai' },
] as const

const DOCUMENT_TYPE_LABELS: Record<string, string> = {
  ic_copy: 'Salinan Kad Pengenalan',
  photo: 'Gambar Passport',
  salary_slip: 'Slip Gaji',
  employer_letter: 'Surat Pengesahan Majikan',
  admin_attachment: 'Lampiran Pentadbir',
  [RESULT_LETTER_DOCUMENT_TYPE]: 'Surat Keputusan',
}

const BOARD_MEETING_REFERENCE_PREFIX = 'Mesyuarat Lembaga Bil.'

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

const applicationId = computed(() => String(route.params.id ?? ''))
const loading = ref(false)
const error = ref<string | null>(null)
const successMessage = ref<string | null>(null)
const application = ref<MembershipApplicationDetail | null>(null)
const managementRemarks = ref('')
const boardRemarks = ref('')
const reviewSubmitting = ref(false)
const completeSubmitting = ref(false)
const confirmDialogOpen = ref(false)
const pendingAction = ref<
  | { type: 'management'; decision: ManagementReviewDecision }
  | { type: 'board'; decision: BoardReviewDecision }
  | { type: 'complete' }
  | null
>(null)
const downloadingDocumentId = ref<string | null>(null)
const deletingDocumentId = ref<string | null>(null)
const previewOpen = ref(false)
const previewLoading = ref(false)
const previewUrl = ref<string | null>(null)
const previewDocument = ref<MembershipApplicationDocumentDetail | null>(null)
const boardMeetingReference = ref('')
const boardMeetingReferenceError = ref<string | null>(null)
const boardMeetingDate = ref(dayjs().format('YYYY-MM-DD'))
const boardMeetingDateError = ref<string | null>(null)
const deleteConfirmDialogOpen = ref(false)
const pendingDeleteDocument = ref<MembershipApplicationDocumentDetail | null>(null)

function statusLabel(status: MembershipApplicationStatus): string {
  const labels: Record<MembershipApplicationStatus, string> = {
    SUBMITTED: 'Dihantar',
    PENDING_BOARD: 'Menunggu Keputusan Mesyuarat ALK',
    MANAGEMENT_REJECTED: 'Ditolak Pentadbiran',
    PENDING_NOTIFICATION: 'Menunggu Makluman',
    COMPLETED: 'Selesai',
  }

  return labels[status] ?? status
}

function getWorkflowProgress(status: MembershipApplicationStatus) {
  switch (status) {
    case 'SUBMITTED':
      return { currentStep: 2, failed: false, failedStep: null as number | null }
    case 'MANAGEMENT_REJECTED':
      return { currentStep: 2, failed: true, failedStep: 2 }
    case 'PENDING_BOARD':
      return { currentStep: 3, failed: false, failedStep: null }
    case 'PENDING_NOTIFICATION':
      return { currentStep: 4, failed: false, failedStep: null }
    case 'COMPLETED':
      return { currentStep: 6, failed: false, failedStep: null }
    default:
      return { currentStep: 1, failed: false, failedStep: null }
  }
}

const workflowProgress = computed(() =>
  application.value ? getWorkflowProgress(application.value.status) : null,
)

const showManagementReview = computed(
  () =>
    hasPermission('semak permohonan keahlian pentadbiran') &&
    application.value?.status === 'SUBMITTED',
)

const showBoardReview = computed(
  () =>
    hasPermission('semak permohonan keahlian lembaga') &&
    application.value?.status === 'PENDING_BOARD',
)

const showCompleteAction = computed(
  () =>
    hasPermission('selesaikan permohonan keahlian') &&
    application.value?.status === 'PENDING_NOTIFICATION',
)

const canEditAdminAttachments = computed(
  () =>
    hasPermission('kemaskini permohonan keahlian') &&
    application.value?.status !== 'COMPLETED',
)

const resultLetterDocument = computed(() =>
  application.value?.documents.find((document) => document.type === RESULT_LETTER_DOCUMENT_TYPE) ?? null,
)

const applicant = computed(() => application.value?.applicant ?? null)

const confirmDialogTitle = computed(() => {
  if (!pendingAction.value) return 'Sahkan Tindakan'

  if (pendingAction.value.type === 'management') {
    return pendingAction.value.decision === 'APPROVED'
      ? 'Semak Permohonan?'
      : 'Tolak Permohonan?'
  }

  if (pendingAction.value.type === 'board') {
    return pendingAction.value.decision === 'PASS'
      ? 'Keputusan ALK?'
      : 'Keputusan ALK?'
  }

  return 'Selesaikan Permohonan?'
})

const confirmDialogDescription = computed(() => {
  if (!pendingAction.value) return ''

  if (pendingAction.value.type === 'management') {
    return pendingAction.value.decision === 'APPROVED'
      ? 'Permohonan akan dihantar ke peringkat keputusan ALK.'
      : 'Permohonan akan ditolak pada peringkat pentadbiran.'
  }

  if (pendingAction.value.type === 'board') {
    return pendingAction.value.decision === 'PASS'
      ? 'Permohonan akan dihantar ke peringkat makluman keputusan.'
      : 'Permohonan akan ditandakan gagal mesyuarat ALK.'
  }

  return 'Surat keputusan akan dijana, dilampirkan dalam e-mel keputusan, dan dihantar kepada pemohon. Akaun ahli akan dicipta jika permohonan lulus.'
})

function workflowStepButtonClass(stepId: number) {
  const progress = workflowProgress.value
  if (!progress) return 'mx-2 size-12 rounded-full shadow-none bg-background border border-foreground/15'

  if (progress.failed && stepId === progress.failedStep) {
    return 'mx-2 size-12 rounded-full shadow-none bg-danger text-danger-foreground'
  }

  if (stepId < progress.currentStep) {
    return 'mx-2 size-12 rounded-full shadow-none bg-primary text-primary-foreground'
  }

  if (stepId === progress.currentStep && application.value?.status !== 'COMPLETED') {
    return 'mx-2 size-12 rounded-full shadow-none'
  }

  if (application.value?.status === 'COMPLETED' || stepId < progress.currentStep) {
    return 'mx-2 size-12 rounded-full shadow-none bg-primary text-primary-foreground'
  }

  return 'bg-background border border-foreground/15 shadow-none mx-2 size-12 rounded-full'
}

function workflowStepLabelClass(stepId: number) {
  const progress = workflowProgress.value
  const isCurrent =
    progress &&
    stepId === progress.currentStep &&
    application.value?.status !== 'COMPLETED' &&
    !progress.failed

  return isCurrent
    ? 'text-primary ml-3 font-medium opacity-100 lg:mx-auto lg:mt-3 lg:w-32'
    : 'ml-3 opacity-70 lg:mx-auto lg:mt-3 lg:w-32'
}

const sortedReviews = computed(() => {
  if (!application.value?.reviews.length) return []

  return [...application.value.reviews].sort((left, right) => {
    const leftTime = left.reviewed_at ? new Date(left.reviewed_at).getTime() : 0
    const rightTime = right.reviewed_at ? new Date(right.reviewed_at).getTime() : 0
    return leftTime - rightTime
  })
})

const uploadedDocuments = computed(() =>
  application.value?.documents.filter((document) => document.type !== RESULT_LETTER_DOCUMENT_TYPE) ?? [],
)

const applicantDocuments = computed(() =>
  uploadedDocuments.value.filter((document) => document.type !== ADMIN_ATTACHMENT_DOCUMENT_TYPE),
)

const adminAttachments = computed(() =>
  uploadedDocuments.value.filter((document) => document.type === ADMIN_ATTACHMENT_DOCUMENT_TYPE),
)

function displayValue(value: string | number | null | undefined): string {
  if (value === null || value === undefined || value === '') return '-'
  return String(value)
}

function formatDate(value: string | null | undefined): string {
  if (!value) return '-'
  return dayjs(value).format('DD/MM/YYYY')
}

function formatDateTime(value: string | null | undefined): string {
  if (!value) return '-'
  return dayjs(value).format('DD/MM/YYYY HH:mm')
}

function formatCurrency(value: string | number | null | undefined): string {
  if (value === null || value === undefined || value === '') return '-'
  const amount = Number(value)
  if (Number.isNaN(amount)) return String(value)
  return `RM ${amount.toFixed(2)}`
}

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 documentLabel(type: string, name: string): string {
  return DOCUMENT_TYPE_LABELS[type] ?? name
}

function isImageMimeType(mimeType: string | null | undefined): boolean {
  return !!mimeType?.startsWith('image/')
}

function isPdfDocument(document: MembershipApplicationDocumentDetail): boolean {
  return document.mime_type === 'application/pdf' || document.name.toLowerCase().endsWith('.pdf')
}

const isPreviewImage = computed(() => isImageMimeType(previewDocument.value?.mime_type))
const isPreviewPdf = computed(() => (previewDocument.value ? isPdfDocument(previewDocument.value) : false))

function revokePreviewUrl() {
  if (previewUrl.value) {
    window.URL.revokeObjectURL(previewUrl.value)
    previewUrl.value = null
  }
}

function handlePreviewOpenChange(open: boolean) {
  previewOpen.value = open

  if (!open) {
    revokePreviewUrl()
    previewDocument.value = null
    previewLoading.value = false
  }
}

function getReference(type: 'PROPOSER' | 'SUPPORTER'): MembershipApplicationReferenceDetail | null {
  return application.value?.references.find((reference) => reference.reference_type === type) ?? null
}

function reviewStageLabel(stage: string): string {
  if (stage === 'MANAGEMENT') return 'Semakan Pentadbiran'
  if (stage === 'BOARD') return 'Keputusan Ahli Lembaga Koperasi (ALK)'
  if (stage === 'COMPLETION') return 'Penyelesaian & Makluman Keputusan'
  return stage
}

function reviewDecisionLabel(decision: string | null, stage: string): string {
  if (!decision) return '-'

  if (stage === 'MANAGEMENT') {
    if (decision === 'APPROVED') return 'Disemak'
    if (decision === 'REJECTED') return 'Ditolak'
  }

  if (stage === 'BOARD' || stage === 'COMPLETION') {
    if (decision === 'PASS') return 'Lulus'
    if (decision === 'FAIL') return 'Gagal'
  }

  return decision
}

function reviewTimelineDotClass(review: MembershipApplicationReviewDetail): string {
  const variant = reviewDecisionBadgeVariant(review.decision, review.stage)

  if (variant === 'success') return 'border-success bg-success'
  if (variant === 'danger') return 'border-danger bg-danger'
  return 'border-primary bg-primary'
}

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

  try {
    const response = await getMembershipApplication(applicationId.value)
    application.value = response.data
  } catch (err) {
    error.value = getApiErrorMessage(err, 'Gagal memuatkan butiran permohonan.')
    application.value = null
  } finally {
    loading.value = false
  }
}

function openConfirmAction(
  action:
    | { type: 'management'; decision: ManagementReviewDecision }
    | { type: 'board'; decision: BoardReviewDecision }
    | { type: 'complete' },
) {
  if (action.type === 'complete') {
    boardMeetingReference.value = ''
    boardMeetingReferenceError.value = null
    boardMeetingDate.value = dayjs().format('YYYY-MM-DD')
    boardMeetingDateError.value = null
  }

  pendingAction.value = action
  confirmDialogOpen.value = true
}

function closeConfirmDialog() {
  confirmDialogOpen.value = false
  pendingAction.value = null
  boardMeetingReference.value = ''
  boardMeetingReferenceError.value = null
  boardMeetingDate.value = dayjs().format('YYYY-MM-DD')
  boardMeetingDateError.value = null
}

async function confirmPendingAction() {
  if (!pendingAction.value || !application.value) return

  error.value = null
  successMessage.value = null
  boardMeetingReferenceError.value = null
  boardMeetingDateError.value = null

  if (pendingAction.value.type === 'complete') {
    const reference = boardMeetingReference.value.trim()
    if (!reference) {
      boardMeetingReferenceError.value = 'Rujukan mesyuarat lembaga diperlukan.'
      return
    }
    if (!boardMeetingDate.value) {
      boardMeetingDateError.value = 'Tarikh mesyuarat lembaga diperlukan.'
      return
    }
    completeSubmitting.value = true
  } else {
    reviewSubmitting.value = true
  }

  try {
    let response

    if (pendingAction.value.type === 'management') {
      response = await submitManagementReview(applicationId.value, {
        decision: pendingAction.value.decision,
        remarks: managementRemarks.value.trim() || null,
      })
      managementRemarks.value = ''
    } else if (pendingAction.value.type === 'board') {
      response = await submitBoardReview(applicationId.value, {
        decision: pendingAction.value.decision,
        remarks: boardRemarks.value.trim() || null,
      })
      boardRemarks.value = ''
    } else {
      const boardMeetingReferenceValue = `${BOARD_MEETING_REFERENCE_PREFIX} ${boardMeetingReference.value.trim()}`

      response = await completeMembershipApplication(applicationId.value, {
        board_meeting_reference: boardMeetingReferenceValue,
        board_meeting_date: boardMeetingDate.value,
      })
    }

    application.value = response.data
    successMessage.value = response.message
    closeConfirmDialog()
  } catch (err) {
    error.value = getApiErrorMessage(err, 'Gagal memproses tindakan.')
    const validationErrors = getApiValidationErrors(err)
    if (validationErrors?.remarks?.[0]) {
      error.value = validationErrors.remarks[0]
    }
    if (validationErrors?.board_meeting_reference?.[0]) {
      boardMeetingReferenceError.value = validationErrors.board_meeting_reference[0]
    }
    if (validationErrors?.board_meeting_date?.[0]) {
      boardMeetingDateError.value = validationErrors.board_meeting_date[0]
    }
  } finally {
    reviewSubmitting.value = false
    completeSubmitting.value = false
  }
}

async function handleDownloadDocument(document: MembershipApplicationDocumentDetail) {
  if (!application.value || downloadingDocumentId.value) return

  downloadingDocumentId.value = document.id

  try {
    await downloadMembershipApplicationDocument(
      application.value.id,
      document.id,
      document.name,
      document.mime_type,
    )
  } catch (err) {
    error.value = getApiErrorMessage(err, 'Gagal memuat turun dokumen.')
  } finally {
    downloadingDocumentId.value = null
  }
}

async function handleDeleteAdminAttachment(document: MembershipApplicationDocumentDetail) {
  if (!application.value || !canEditAdminAttachments.value || deletingDocumentId.value) return

  pendingDeleteDocument.value = document
  deleteConfirmDialogOpen.value = true
}

function closeDeleteConfirmDialog() {
  deleteConfirmDialogOpen.value = false
  pendingDeleteDocument.value = null
}

async function confirmDeleteAdminAttachment() {
  const document = pendingDeleteDocument.value
  if (!application.value || !document || deletingDocumentId.value) return

  deletingDocumentId.value = document.id
  error.value = null
  successMessage.value = null

  try {
    const response = await deleteMembershipApplicationDocument(application.value.id, document.id)
    application.value = response.data

    if (previewDocument.value?.id === document.id) {
      handlePreviewOpenChange(false)
    }

    successMessage.value = response.message || 'Lampiran pentadbir berjaya dipadam.'
    closeDeleteConfirmDialog()
  } catch (err) {
    error.value = getApiErrorMessage(err, 'Gagal memadam lampiran pentadbir.')
  } finally {
    deletingDocumentId.value = null
  }
}

async function handleViewDocument(document: MembershipApplicationDocumentDetail) {
  if (!application.value) return

  previewDocument.value = document
  previewOpen.value = true
  previewLoading.value = true
  revokePreviewUrl()

  try {
    const blob = await fetchMembershipApplicationDocument(
      application.value.id,
      document.id,
      document.mime_type,
    )
    previewUrl.value = window.URL.createObjectURL(blob)
  } catch (err) {
    handlePreviewOpenChange(false)
    error.value = getApiErrorMessage(err, 'Gagal memuatkan dokumen.')
  } finally {
    previewLoading.value = false
  }
}

onMounted(() => {
  fetchApplication()
})

onUnmounted(() => {
  revokePreviewUrl()
})
</script>

<template>
  <div class="w-full space-y-6">
    <div class="flex flex-wrap items-center gap-3">
      <div class="mr-auto">
        <h2 class="text-lg font-medium">Butiran Permohonan Keahlian</h2>
        <p v-if="application" class="mt-1 text-sm opacity-70">
          {{ application.application_number }} · {{ application.applicant?.name ?? '-' }}
        </p>
      </div>
      <Button look="outline" variant="secondary" type="button"
        @click="router.push({ name: 'list-membership-applications' })">
        Kembali
      </Button>
      <Button v-if="hasPermission('kemaskini permohonan keahlian') && application?.status !== 'COMPLETED'" type="button"
        @click="router.push({ name: 'edit-membership-application', params: { id: applicationId } })">
        <Pencil class="mr-2 size-4" />
        Kemaskini
      </Button>
    </div>

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

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

    <div v-if="loading" class="opacity-70">Memuatkan butiran permohonan...</div>

    <template v-else-if="application">
      <Box class="p-5 sm:p-6">
        <div class="flex flex-wrap items-center justify-between gap-3">
          <div>
            <div class="text-sm opacity-70">No. Permohonan</div>
            <div class="text-xl font-semibold">{{ application.application_number }}</div>
          </div>
          <div class="flex flex-wrap items-center gap-2">
            <Badge :variant="statusBadgeVariant(application.status)" :look="statusBadgeLook(application.status)"
              class="whitespace-nowrap">
              {{ statusLabel(application.status) }}
            </Badge>
            <Badge v-if="application.board_result"
              :variant="application.board_result === 'PASS' ? 'success' : 'danger'">
              Lembaga: {{ application.board_result === 'PASS' ? 'Lulus' : 'Gagal' }}
            </Badge>
          </div>
        </div>

        <div class="mt-4 grid gap-3 text-sm sm:grid-cols-2 lg:grid-cols-4">
          <div>
            <span class="opacity-70">Tarikh Hantar:</span>
            {{ formatDateTime(application.submitted_at) }}
          </div>
          <div>
            <span class="opacity-70">Tarikh Selesai:</span>
            {{ formatDateTime(application.completed_at) }}
          </div>
          <div>
            <span class="opacity-70">Emel Pemohon:</span>
            <span class="lowercase">{{ displayValue(application.applicant?.email) }}</span>
          </div>
          <div>
            <span class="opacity-70">No. IC:</span>
            {{ displayValue(application.applicant?.ic_number) }}
          </div>
        </div>
      </Box>

      <Box class="py-8 sm:py-10">
        <div class="px-5 sm:px-8">
          <div class="text-sm font-medium opacity-70">Status Permohonan</div>
        </div>
        <div
          class="before:bg-foreground/10 relative mt-4 flex flex-col justify-center px-5 before:absolute before:bottom-0 before:top-0 before:mt-6 before:hidden before:h-0.5 before:w-[69%] sm:px-8 lg:flex-row before:lg:block">
          <div v-for="step in WORKFLOW_STEPS" :key="step.id"
            class="z-10 flex flex-1 items-center lg:block lg:text-center">
            <Button type="button" :class="workflowStepButtonClass(step.id)"
              :variant="step.id === workflowProgress?.currentStep && application.status !== 'COMPLETED' && !workflowProgress?.failed ? 'primary' : 'ghost'"
              disabled>
              {{ step.id }}
            </Button>
            <div :class="workflowStepLabelClass(step.id)">
              {{ step.label }}
            </div>
          </div>
        </div>
      </Box>

      <Box v-if="showManagementReview" class="p-5 sm:p-6">
        <div class="font-medium">Semakan Pentadbiran</div>
        <p class="mt-1 text-sm opacity-70">
          Semak atau tolak permohonan pada peringkat pentadbiran.
        </p>
        <Field class="mt-4">
          <FieldLabel for="management-remarks">Catatan</FieldLabel>
          <Textarea id="management-remarks" v-model="managementRemarks" rows="3" placeholder="Catatan semakan (pilihan)"
            :disabled="reviewSubmitting" />
        </Field>
        <div class="mt-4 flex flex-wrap gap-2">
          <Button type="button" variant="success" :disabled="reviewSubmitting || completeSubmitting"
            @click="openConfirmAction({ type: 'management', decision: 'APPROVED' })">
            Semak
          </Button>
          <Button type="button" variant="danger" look="outline" :disabled="reviewSubmitting || completeSubmitting"
            @click="openConfirmAction({ type: 'management', decision: 'REJECTED' })">
            Tolak
          </Button>
        </div>
      </Box>

      <Box v-if="showBoardReview" class="p-5 sm:p-6">
        <div class="font-medium">Keputusan Ahli Lembaga Koperasi (ALK)</div>
        <p class="mt-1 text-sm opacity-70">
          Luluskan atau gagalkan permohonan pada peringkat ALK.
        </p>
        <Field class="mt-4">
          <FieldLabel for="board-remarks">Catatan</FieldLabel>
          <Textarea id="board-remarks" v-model="boardRemarks" rows="3" placeholder="Catatan semakan (pilihan)"
            :disabled="reviewSubmitting" />
        </Field>
        <div class="mt-4 flex flex-wrap gap-2">
          <Button type="button" variant="success" :disabled="reviewSubmitting || completeSubmitting"
            @click="openConfirmAction({ type: 'board', decision: 'PASS' })">
            Lulus
          </Button>
          <Button type="button" variant="danger" look="outline" :disabled="reviewSubmitting || completeSubmitting"
            @click="openConfirmAction({ type: 'board', decision: 'FAIL' })">
            Gagal
          </Button>
        </div>
      </Box>

      <Box v-if="showCompleteAction" class="p-5 sm:p-6">
        <div class="font-medium">Makluman Keputusan</div>
        <p class="mt-1 text-sm opacity-70">
          Jana surat keputusan, hantar e-mel dengan lampiran surat kepada pemohon
          <span v-if="application.board_result === 'PASS'">, dan cipta akaun ahli</span>.
        </p>
        <div class="mt-4 flex flex-wrap gap-2">
          <Button type="button" variant="primary" :disabled="reviewSubmitting || completeSubmitting"
            @click="openConfirmAction({ type: 'complete' })">
            Selesaikan, Jana Surat & Hantar E-mel
          </Button>
        </div>
      </Box>

      <Box v-if="resultLetterDocument" class="p-5 sm:p-6">
        <div class="font-medium">Surat Keputusan</div>
        <p class="mt-1 text-sm opacity-70">
          {{ resultLetterDocument.name }} · {{ formatFileSize(resultLetterDocument.file_size) }}
        </p>
        <div class="mt-4 flex flex-wrap gap-2">
          <Button type="button" look="outline"
            :disabled="previewLoading && previewDocument?.id === resultLetterDocument.id"
            @click="handleViewDocument(resultLetterDocument)">
            <Eye class="mr-2 size-4" />
            Lihat
          </Button>
          <Button type="button" look="outline" :disabled="downloadingDocumentId === resultLetterDocument.id"
            @click="handleDownloadDocument(resultLetterDocument)">
            <Download class="mr-2 size-4" />
            {{ downloadingDocumentId === resultLetterDocument.id ? 'Memuat turun...' : 'Muat Turun' }}
          </Button>
        </div>
      </Box>

      <TabsRoot defaultValue="personal" class="w-full">
        <Box raised="single" class="w-full p-0">
          <div class="w-full px-5 py-4">
            <TabsList class="mb-0 flex w-full">
              <TabsTrigger
                class="inline-flex min-w-0 flex-1 items-center justify-center whitespace-nowrap px-2 text-xs sm:px-3 sm:text-sm"
                value="personal">
                Maklumat Peribadi
              </TabsTrigger>
              <TabsTrigger
                class="inline-flex min-w-0 flex-1 items-center justify-center whitespace-nowrap px-2 text-xs sm:px-3 sm:text-sm"
                value="contact">
                Hubungan & Alamat
              </TabsTrigger>
              <TabsTrigger
                class="inline-flex min-w-0 flex-1 items-center justify-center whitespace-nowrap px-2 text-xs sm:px-3 sm:text-sm"
                value="employment">
                Pekerjaan & Caruman
              </TabsTrigger>
              <TabsTrigger
                class="inline-flex min-w-0 flex-1 items-center justify-center whitespace-nowrap px-2 text-xs sm:px-3 sm:text-sm"
                value="heirs">
                Penama
              </TabsTrigger>
              <TabsTrigger
                class="inline-flex min-w-0 flex-1 items-center justify-center whitespace-nowrap px-2 text-xs sm:px-3 sm:text-sm"
                value="references">
                Pencadang & Penyokong
              </TabsTrigger>
              <TabsTrigger
                class="inline-flex min-w-0 flex-1 items-center justify-center whitespace-nowrap px-2 text-xs sm:px-3 sm:text-sm"
                value="documents">
                Dokumen
              </TabsTrigger>
              <TabsTrigger v-if="application.reviews.length"
                class="inline-flex min-w-0 flex-1 items-center justify-center whitespace-nowrap px-2 text-xs sm:px-3 sm:text-sm"
                value="reviews">
                Sejarah Semakan
              </TabsTrigger>
            </TabsList>
          </div>
        </Box>

        <TabsContent value="personal" class="mt-6">
          <div v-if="!applicant" class="opacity-70">Tiada maklumat pemohon.</div>
          <div v-else class="grid grid-cols-12 gap-4 gap-y-5">
            <Field class="col-span-12 sm:col-span-6">
              <FieldLabel>Nama Penuh</FieldLabel>
              <Input :model-value="displayValue(applicant.name)" type="text" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-6">
              <FieldLabel>Emel</FieldLabel>
              <Input :model-value="displayValue(applicant.email)" type="text" disabled class="lowercase" />
            </Field>
            <Field class="col-span-12 sm:col-span-6">
              <FieldLabel>No. Kad Pengenalan</FieldLabel>
              <Input :model-value="displayValue(applicant.ic_number)" type="text" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-6">
              <FieldLabel>Tarikh Lahir</FieldLabel>
              <Input :model-value="formatDate(applicant.birth_date)" type="text" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-6">
              <FieldLabel>Tempat Lahir</FieldLabel>
              <Input :model-value="displayValue(applicant.birth_place)" type="text" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-6">
              <FieldLabel>Jantina</FieldLabel>
              <Input :model-value="displayValue(applicant.gender)" type="text" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-6">
              <FieldLabel>Status Perkahwinan</FieldLabel>
              <Input :model-value="displayValue(applicant.marriage_status)" type="text" disabled />
            </Field>
          </div>
        </TabsContent>

        <TabsContent value="contact" class="mt-6">
          <div v-if="!applicant" class="opacity-70">Tiada maklumat pemohon.</div>
          <div v-else class="grid grid-cols-12 gap-4 gap-y-5">
            <Field class="col-span-12">
              <FieldLabel>Alamat</FieldLabel>
              <Textarea :model-value="displayValue(applicant.address)" rows="3" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-4">
              <FieldLabel>No. Telefon</FieldLabel>
              <Input :model-value="displayValue(applicant.phone_number)" type="text" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-4">
              <FieldLabel>No. Pejabat</FieldLabel>
              <Input :model-value="displayValue(applicant.office_number)" type="text" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-4">
              <FieldLabel>Poskod</FieldLabel>
              <Input :model-value="displayValue(applicant.postcode)" type="text" disabled />
            </Field>
          </div>
        </TabsContent>

        <TabsContent value="employment" class="mt-6">
          <div v-if="!applicant" class="opacity-70">Tiada maklumat pemohon.</div>
          <div v-else class="grid grid-cols-12 gap-4 gap-y-5">
            <Field class="col-span-12 sm:col-span-6">
              <FieldLabel>Nama Majikan</FieldLabel>
              <Input :model-value="displayValue(applicant.employer_name)" type="text" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-6">
              <FieldLabel>Jawatan Semasa</FieldLabel>
              <Input :model-value="displayValue(applicant.current_position)" type="text" disabled />
            </Field>
            <Field class="col-span-12">
              <FieldLabel>Alamat Majikan</FieldLabel>
              <Textarea :model-value="displayValue(applicant.employer_address)" rows="3" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-4">
              <FieldLabel>Tarikh Mula Berkhidmat</FieldLabel>
              <Input :model-value="formatDate(applicant.start_work_date)" type="text" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-4">
              <FieldLabel>Caruman Saham (RM)</FieldLabel>
              <Input :model-value="formatCurrency(applicant.stock_monthly_contribution)" type="text" disabled />
            </Field>
            <Field class="col-span-12 sm:col-span-4">
              <FieldLabel>Caruman Yuran (RM)</FieldLabel>
              <Input :model-value="formatCurrency(applicant.fee_monthly_contribution)" type="text" disabled />
            </Field>
          </div>
        </TabsContent>

        <TabsContent value="heirs" class="mt-6">
          <div v-if="!application.heirs.length" class="opacity-70">Tiada maklumat waris.</div>
          <div v-else class="space-y-4">
            <div v-for="(heir, index) in application.heirs" :key="heir.id"
              class="rounded-lg border border-foreground/10 p-4">
              <div class="mb-4 font-medium">Penama</div>
              <div class="grid grid-cols-12 gap-4">
                <Field class="col-span-12 sm:col-span-6">
                  <FieldLabel>Nama</FieldLabel>
                  <Input :model-value="displayValue(heir.name)" type="text" disabled />
                </Field>
                <Field class="col-span-12 sm:col-span-6">
                  <FieldLabel>No. Kad Pengenalan</FieldLabel>
                  <Input :model-value="displayValue(heir.ic_number)" type="text" disabled />
                </Field>
                <Field class="col-span-12 sm:col-span-6">
                  <FieldLabel>Hubungan</FieldLabel>
                  <Input :model-value="displayValue(heir.relationship)" type="text" disabled />
                </Field>
                <Field class="col-span-12 sm:col-span-6">
                  <FieldLabel>No. Telefon</FieldLabel>
                  <Input :model-value="displayValue(heir.phone_number)" type="text" disabled />
                </Field>
              </div>
            </div>
          </div>
        </TabsContent>

        <TabsContent value="references" class="mt-6">
          <div class="grid gap-4 sm:grid-cols-2">
            <div class="rounded-lg border border-foreground/10 p-4">
              <div class="font-medium">Pencadang</div>
              <div class="mt-3 space-y-1 text-sm">
                <div>
                  <span class="opacity-70">Nama:</span>
                  {{ displayValue(getReference('PROPOSER')?.member?.name) }}
                </div>
                <div>
                  <span class="opacity-70">No. IC:</span>
                  {{ displayValue(getReference('PROPOSER')?.member?.ic_number) }}
                </div>
              </div>
            </div>
            <div class="rounded-lg border border-foreground/10 p-4">
              <div class="font-medium">Penyokong</div>
              <div class="mt-3 space-y-1 text-sm">
                <div>
                  <span class="opacity-70">Nama:</span>
                  {{ displayValue(getReference('SUPPORTER')?.member?.name) }}
                </div>
                <div>
                  <span class="opacity-70">No. IC:</span>
                  {{ displayValue(getReference('SUPPORTER')?.member?.ic_number) }}
                </div>
              </div>
            </div>
          </div>
        </TabsContent>

        <TabsContent value="documents" class="mt-6">
          <div v-if="!uploadedDocuments.length" class="opacity-70">Tiada dokumen dimuat naik.</div>
          <template v-else>
            <div v-if="applicantDocuments.length" class="space-y-3">
              <div class="font-medium">Dokumen Pemohon</div>
              <div v-for="document in applicantDocuments" :key="document.id"
                class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-foreground/10 p-4">
                <div>
                  <div class="font-medium">{{ documentLabel(document.type, document.name) }}</div>
                  <div class="mt-1 text-sm opacity-70">
                    {{ document.name }} · {{ formatFileSize(document.file_size) }}
                  </div>
                </div>
                <div class="flex flex-wrap items-center gap-2">
                  <Button type="button" look="outline" size="sm"
                    :disabled="previewLoading && previewDocument?.id === document.id"
                    @click="handleViewDocument(document)">
                    <Eye class="mr-2 size-4" />
                    {{ previewLoading && previewDocument?.id === document.id ? 'Memuatkan...' : 'Lihat' }}
                  </Button>
                  <Button type="button" look="outline" size="sm" :disabled="downloadingDocumentId === document.id"
                    @click="handleDownloadDocument(document)">
                    <Download class="mr-2 size-4" />
                    {{ downloadingDocumentId === document.id ? 'Memuat turun...' : 'Muat Turun' }}
                  </Button>
                </div>
              </div>
            </div>

            <div v-if="adminAttachments.length" class="mt-8 space-y-3">
              <div class="font-medium">Lampiran Pentadbir</div>
              <div v-for="document in adminAttachments" :key="document.id"
                class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-foreground/10 p-4">
                <div>
                  <div class="font-medium">{{ document.name }}</div>
                  <div class="mt-1 text-sm opacity-70">{{ formatFileSize(document.file_size) }}</div>
                </div>
                <div class="flex flex-wrap items-center gap-2">
                  <Button type="button" look="outline" size="sm"
                    :disabled="previewLoading && previewDocument?.id === document.id"
                    @click="handleViewDocument(document)">
                    <Eye class="mr-2 size-4" />
                    {{ previewLoading && previewDocument?.id === document.id ? 'Memuatkan...' : 'Lihat' }}
                  </Button>
                  <Button type="button" look="outline" size="sm" :disabled="downloadingDocumentId === document.id"
                    @click="handleDownloadDocument(document)">
                    <Download class="mr-2 size-4" />
                    {{ downloadingDocumentId === document.id ? 'Memuat turun...' : 'Muat Turun' }}
                  </Button>
                  <Button v-if="canEditAdminAttachments" type="button" look="outline" size="sm" variant="danger"
                    :disabled="deletingDocumentId === document.id" @click="handleDeleteAdminAttachment(document)">
                    <Trash2 class="mr-2 size-4" />
                    {{ deletingDocumentId === document.id ? 'Memadam...' : 'Padam' }}
                  </Button>
                </div>
              </div>
            </div>
          </template>
        </TabsContent>

        <TabsContent v-if="application.reviews.length" value="reviews" class="mt-6">
          <div class="relative ms-3 ps-8">
            <div v-for="(review, index) in sortedReviews" :key="review.id" class="relative pb-8 last:pb-0">
              <span class="absolute -inset-s-8 top-1.5 flex size-3.5 rounded-full border-2 ring-4 ring-background"
                :class="reviewTimelineDotClass(review)" />
              <span v-if="index < sortedReviews.length - 1"
                class="absolute -inset-s-3.5 top-5 h-[calc(100%-0.25rem)] w-px bg-foreground/15" />

              <div class="rounded-lg border border-foreground/10 p-4">
                <div class="flex flex-wrap items-start justify-between gap-2">
                  <div>
                    <div class="font-medium">{{ reviewStageLabel(review.stage) }}</div>
                    <div class="mt-1 text-sm opacity-70">
                      {{ formatDateTime(review.reviewed_at) }}
                    </div>
                  </div>
                  <Badge :variant="reviewDecisionBadgeVariant(review.decision, review.stage)"
                    :look="reviewDecisionBadgeLook(review.decision, review.stage)" class="whitespace-nowrap">
                    {{ reviewDecisionLabel(review.decision, review.stage) }}
                  </Badge>
                </div>

                <div class="mt-3 space-y-2 text-sm">
                  <div>
                    <span class="opacity-70">Disemak oleh:</span>
                    {{ displayValue(review.reviewer?.name) }}
                  </div>
                  <div v-if="review.remarks">
                    <span class="opacity-70">Catatan:</span>
                    {{ review.remarks }}
                  </div>
                </div>
              </div>
            </div>
          </div>
        </TabsContent>
      </TabsRoot>
    </template>

    <DialogRoot :open="confirmDialogOpen"
      @openChange="(details) => { if (!details.open) closeConfirmDialog(); else confirmDialogOpen = details.open }">
      <DialogContent>
        <div class="p-5 text-center">
          <div class="mt-2 text-2xl font-medium">{{ confirmDialogTitle }}</div>
          <div class="mt-2 opacity-70">{{ confirmDialogDescription }}</div>
          <Field v-if="pendingAction?.type === 'complete'" class="mt-5 text-left">
            <FieldLabel for="detail-board-meeting-reference">Rujukan Mesyuarat Lembaga</FieldLabel>
            <div class="flex">
              <span
                class="inline-flex items-center rounded-l-lg border border-r-0 border-foreground/10 bg-foreground/5 px-3 text-sm opacity-80">
                {{ BOARD_MEETING_REFERENCE_PREFIX }}
              </span>
              <Input id="detail-board-meeting-reference" v-model="boardMeetingReference" type="text"
                class="rounded-l-none" placeholder="Contoh: 3/2026" :disabled="completeSubmitting"
                @input="boardMeetingReferenceError = null" />
            </div>
            <FieldError v-if="boardMeetingReferenceError">{{ boardMeetingReferenceError }}</FieldError>
          </Field>
          <Field v-if="pendingAction?.type === 'complete'" class="mt-4 text-left">
            <FieldLabel for="detail-board-meeting-date">Tarikh Mesyuarat Lembaga</FieldLabel>
            <Input id="detail-board-meeting-date" v-model="boardMeetingDate" type="date" :disabled="completeSubmitting"
              @input="boardMeetingDateError = null" />
            <FieldError v-if="boardMeetingDateError">{{ boardMeetingDateError }}</FieldError>
          </Field>
        </div>
        <div class="px-5 pb-8 text-center">
          <DialogCloseTrigger class="mr-2 w-28" :disabled="reviewSubmitting || completeSubmitting">
            Batal
          </DialogCloseTrigger>
          <Button class="w-28" type="button" variant="primary" :disabled="reviewSubmitting || completeSubmitting"
            @click="confirmPendingAction">
            {{ reviewSubmitting || completeSubmitting ? 'Memproses...' : 'Sahkan' }}
          </Button>
        </div>
      </DialogContent>
    </DialogRoot>

    <DialogRoot :open="deleteConfirmDialogOpen"
      @openChange="(details) => { if (!details.open) closeDeleteConfirmDialog() }">
      <DialogContent>
        <div class="p-5 text-center">
          <div class="mt-2 text-2xl font-medium">Padam Lampiran Pentadbir?</div>
          <div class="mt-2 opacity-70">
            {{ pendingDeleteDocument?.name ?? 'Lampiran pentadbir' }} akan dipadam secara kekal.
          </div>
        </div>
        <div class="px-5 pb-8 text-center">
          <DialogCloseTrigger class="mr-2 w-28" :disabled="!!deletingDocumentId" @click="closeDeleteConfirmDialog">
            Batal
          </DialogCloseTrigger>
          <Button class="w-28" type="button" variant="danger" :disabled="!!deletingDocumentId"
            @click="confirmDeleteAdminAttachment">
            {{ deletingDocumentId ? 'Memadam...' : 'Padam' }}
          </Button>
        </div>
      </DialogContent>
    </DialogRoot>

    <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 ? documentLabel(previewDocument.type, previewDocument.name) : 'Pratonton dokumen'">
        <button type="button" class="absolute inset-0 bg-black/80" aria-label="Tutup pratonton"
          @click="handlePreviewOpenChange(false)" />

        <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">
              {{ previewDocument ? documentLabel(previewDocument.type, previewDocument.name) : 'Pratonton Dokumen' }}
            </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 dokumen...
            </div>

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

            <iframe v-else-if="previewUrl && isPreviewPdf" :src="previewUrl"
              class="block w-full rounded-lg border border-foreground/10" style="height: min(70vh, 720px)"
              :title="previewDocument?.name ?? 'Pratonton dokumen'" />

            <div v-else-if="previewUrl" class="py-12 text-center opacity-70">
              Pratonton tidak tersedia untuk jenis fail ini. Sila muat turun dokumen.
            </div>
          </div>

          <div class="flex justify-end gap-2 border-t border-foreground/10 px-5 py-4">
            <Button type="button" look="outline" @click="handlePreviewOpenChange(false)">
              Tutup
            </Button>
            <Button v-if="previewDocument" type="button" look="outline"
              :disabled="downloadingDocumentId === previewDocument.id" @click="handleDownloadDocument(previewDocument)">
              <Download class="mr-2 size-4" />
              Muat Turun
            </Button>
          </div>
        </div>
      </div>
    </Teleport>
  </div>
</template>
