<script lang="ts" setup>
import type {
  CancelBookingConfirmationResponse,
  ConfirmBookingResponse,
  PaymentMethod,
  PublicBooking,
  UpdateBookingDetailsResponse
} from '~~/shared/booking'

import {
  formatBookingCurrency,
  getBookingStatusLabel
} from '~~/shared/booking'

import { getErrorMessage } from '../../utils/errors'
import { formatDateTime } from '../../utils/formatters'

const route = useRoute()
const toast = useToast()
const apiClient = useApiClient()
const auth = useAuth()
const { locale, t } = useLocale()

const token = String(route.params.token || '')
const confirming = ref(false)
const cancelling = ref(false)
const savingPayment = ref(false)
const uploadingTransactionDocument = ref(false)
const deletingTransactionDocument = ref(false)

await auth.fetchSession()

let initialBooking: PublicBooking

try {
  const response = await apiClient<{ booking: PublicBooking }>(`/api/public/bookings/${token}`)
  initialBooking = response.booking
} catch (error: any) {
  throw createError({
    statusCode: error?.statusCode || error?.data?.statusCode || 404,
    statusMessage: error?.data?.statusMessage || error?.message || 'Booking not found'
  })
}

const booking = ref(initialBooking)
const paymentForm = reactive({
  paymentMethod: initialBooking.paymentMethod
})
const transactionDocumentFile = ref<File | null>(null)
const transactionDocumentInputKey = ref(0)

const statusColor = computed(() => booking.value.status === 'confirmed' ? 'success' : 'warning')
const ticketLabel = computed(() => booking.value.ticketLabel || booking.value.ticketType.toUpperCase())
const totalFormatted = computed(() => formatBookingCurrency(booking.value.totalPrice, locale.value))
const receiptPath = computed(() => `/receipt/${booking.value.receiptToken}`)
const signInPath = computed(() => `/login?redirect=${encodeURIComponent(route.fullPath)}`)
const canManageBooking = computed(() => {
  const user = auth.user.value

  return Boolean(user && (user.role === 'super_admin' || user.id === booking.value.personInChargeId))
})
const transactionDocumentLimit = 10 * 1024 * 1024
const transactionDocumentAccept = '.pdf,.jpg,.jpeg,.png,.webp,.heic,.heif,application/pdf,image/jpeg,image/png,image/webp,image/heic,image/heif'
const selectedTransactionDocumentName = computed(() => transactionDocumentFile.value?.name || '')
const paymentMethodItems = computed<{ label: string, value: PaymentMethod }[]>(() => [
  {
    label: t('confirm.paymentCash'),
    value: 'cash'
  },
  {
    label: t('confirm.paymentBank'),
    value: 'bank'
  }
])

watch(
  () => paymentForm.paymentMethod,
  (paymentMethod) => {
    if (paymentMethod === 'cash') {
      transactionDocumentFile.value = null
      transactionDocumentInputKey.value += 1
    }
  }
)

useSeoMeta({
  title: () => `${t('confirm.title')} - ${booking.value.customerName}`,
  description: () => t('confirm.description'),
  ogTitle: () => `${t('confirm.title')} - ${booking.value.customerName}`,
  ogDescription: () => t('confirm.description'),
  robots: 'noindex,nofollow'
})

const detailRows = computed(() => {
  const rows = [
    {
      label: t('confirm.guestOrganizer'),
      value: booking.value.customerName
    },
    {
      label: t('confirm.contactNumber'),
      value: booking.value.customerPhone
    },
    {
      label: t('confirm.pic'),
      value: booking.value.personInChargeName
    },
    {
      label: t('confirm.picPhone'),
      value: booking.value.personInChargePhoneNumber
    },
    {
      label: t('confirm.ticketCategory'),
      value: ticketLabel.value
    },
    {
      label: t('confirm.seatsCovered'),
      value: String(booking.value.seatCount)
    },
    {
      label: t('confirm.payment'),
      value: booking.value.paymentMethod === 'bank' ? t('confirm.paymentBank') : t('confirm.paymentCash')
    },
    {
      label: t('confirm.submittedLabel'),
      value: formatDateTime(booking.value.createdAt, t('date.notAvailable'), locale.value)
    }
  ]

  if (booking.value.confirmedAt) {
    rows.push({
      label: t('confirm.confirmedAt'),
      value: formatDateTime(booking.value.confirmedAt, t('date.notAvailable'), locale.value)
    })
  }

  return rows
})

const isTableBooking = computed(() => {
  return booking.value.bookingMode.toLowerCase().includes('table')
    || booking.value.bookingModeLabel.toLowerCase().includes('table')
})

function getDetailValueClass(row: { label: string }) {
  return [
    'min-w-0 font-medium text-highlighted break-words',
    row.label === t('confirm.guestOrganizer') && isTableBooking.value ? 'max-h-16 overflow-hidden' : ''
  ]
}

function formatFileSize(size: number) {
  if (size >= 1024 * 1024) {
    return `${(size / (1024 * 1024)).toFixed(1)} MB`
  }

  return `${Math.max(Math.round(size / 1024), 1)} KB`
}

function onTransactionDocumentChange(event: Event) {
  if (!canManageBooking.value) {
    return
  }

  const input = event.target as HTMLInputElement
  transactionDocumentFile.value = input.files?.[0] ?? null
}

function applyBooking(nextBooking: PublicBooking) {
  booking.value = nextBooking
  paymentForm.paymentMethod = nextBooking.paymentMethod
}

async function savePaymentDetails() {
  if (!canManageBooking.value || savingPayment.value || booking.value.status !== 'pending') {
    return booking.value
  }

  savingPayment.value = true

  try {
    const response = await apiClient<UpdateBookingDetailsResponse>(
      `/api/public/bookings/${token}/payment`,
      {
        method: 'PATCH',
        body: {
          paymentMethod: paymentForm.paymentMethod
        }
      }
    )

    applyBooking(response.booking)

    return response.booking
  } finally {
    savingPayment.value = false
  }
}

async function uploadTransactionDocument(options: { silent?: boolean } = {}) {
  if (!canManageBooking.value || !transactionDocumentFile.value || uploadingTransactionDocument.value || booking.value.status !== 'pending') {
    return booking.value
  }

  if (transactionDocumentFile.value.size > transactionDocumentLimit) {
    toast.add({
      title: t('confirm.uploadFailed'),
      description: t('confirm.documentSizeInvalid'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
    return booking.value
  }

  uploadingTransactionDocument.value = true

  try {
    paymentForm.paymentMethod = 'bank'
    await savePaymentDetails()

    const formData = new FormData()
    formData.append('document', transactionDocumentFile.value)

    const response = await apiClient<UpdateBookingDetailsResponse>(
      `/api/public/bookings/${token}/transaction-document`,
      {
        method: 'POST',
        body: formData
      }
    )

    applyBooking(response.booking)
    transactionDocumentFile.value = null
    transactionDocumentInputKey.value += 1

    if (!options.silent) {
      toast.add({
        title: t('confirm.documentUploaded'),
        description: t('confirm.documentUploadedDescription'),
        color: 'success',
        icon: 'i-lucide-file-check'
      })
    }

    return response.booking
  } catch (error) {
    if (options.silent) {
      throw error
    }

    toast.add({
      title: t('confirm.uploadFailed'),
      description: getErrorMessage(error, t('booking.tryAgain')),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })

    return booking.value
  } finally {
    uploadingTransactionDocument.value = false
  }
}

async function deleteTransactionDocument() {
  if (!canManageBooking.value || !booking.value.transactionDocument || deletingTransactionDocument.value || booking.value.status !== 'pending') {
    return
  }

  if (import.meta.client && !window.confirm(t('confirm.deleteDocumentPrompt'))) {
    return
  }

  deletingTransactionDocument.value = true

  try {
    const response = await apiClient<UpdateBookingDetailsResponse>(
      `/api/public/bookings/${token}/transaction-document`,
      {
        method: 'DELETE'
      }
    )

    applyBooking(response.booking)
    transactionDocumentFile.value = null
    transactionDocumentInputKey.value += 1

    toast.add({
      title: t('confirm.documentDeleted'),
      description: t('confirm.documentDeletedDescription'),
      color: 'success',
      icon: 'i-lucide-trash-2'
    })
  } catch (error) {
    toast.add({
      title: t('confirm.deleteFailed'),
      description: getErrorMessage(error, t('booking.tryAgain')),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    deletingTransactionDocument.value = false
  }
}

async function confirmBooking() {
  if (!canManageBooking.value || booking.value.status === 'confirmed') {
    return
  }

  confirming.value = true

  try {
    await savePaymentDetails()
    if (paymentForm.paymentMethod === 'bank') {
      await uploadTransactionDocument({ silent: true })
    }

    const response = await apiClient<ConfirmBookingResponse>(
      `/api/public/bookings/${token}/confirm`,
      {
        method: 'POST'
      }
    )

    applyBooking(response.booking)

    toast.add({
      title: response.alreadyConfirmed ? t('confirm.alreadyConfirmedToast') : t('confirm.confirmedToast'),
      description: response.alreadyConfirmed
        ? t('confirm.alreadyConfirmedDescription')
        : response.ticketReceiptWhatsApp.sent
          ? t('confirm.receiptSent', { phone: response.ticketReceiptWhatsApp.recipientPhone })
          : t('confirm.receiptNotSent', { error: response.ticketReceiptWhatsApp.error }),
      color: response.alreadyConfirmed || response.ticketReceiptWhatsApp.sent ? 'success' : 'warning',
      icon: 'i-lucide-check-circle-2'
    })
  } catch (error) {
    toast.add({
      title: t('confirm.failed'),
      description: getErrorMessage(error, t('booking.tryAgain')),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    confirming.value = false
  }
}

async function cancelBookingConfirmation() {
  if (!canManageBooking.value || booking.value.status !== 'confirmed') {
    return
  }

  if (import.meta.client && !window.confirm(t('confirm.cancelPrompt'))) {
    return
  }

  cancelling.value = true

  try {
    const response = await apiClient<CancelBookingConfirmationResponse>(
      `/api/public/bookings/${token}/cancel`,
      {
        method: 'POST'
      }
    )

    applyBooking(response.booking)

    toast.add({
      title: response.alreadyPending ? t('confirm.alreadyPending') : t('confirm.cancelled'),
      description: response.alreadyPending
        ? t('confirm.alreadyPendingDescription')
        : t('confirm.cancelledDescription'),
      color: response.alreadyPending ? 'warning' : 'success',
      icon: 'i-lucide-x-circle'
    })
  } catch (error) {
    toast.add({
      title: t('confirm.cancelFailed'),
      description: getErrorMessage(error, t('booking.tryAgain')),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    cancelling.value = false
  }
}
</script>

<template>
  <UContainer class="page-shell-compact">
    <div class="space-y-5">
      <div class="page-header text-center sm:items-center">
        <UBadge :label="t('confirm.badge')" color="primary" variant="soft" class="page-eyebrow" />
        <h1 class="page-title">
          {{ t('confirm.title') }}
        </h1>
        <p class="page-description">
          {{ t('confirm.description') }}
        </p>
      </div>

      <UCard
        class="surface-card overflow-hidden rounded-lg"
        :ui="{ body: 'space-y-4 p-4 sm:p-5' }"
      >
        <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
          <div class="space-y-1">
            <p class="text-xs font-medium uppercase tracking-wide text-muted">
              {{ t('confirm.status') }}
            </p>
            <UBadge :label="getBookingStatusLabel(booking.status, booking.statusLabel, locale)" :color="statusColor" variant="soft" />
          </div>

          <div class="text-sm text-muted">
            {{ t('confirm.submitted', { date: formatDateTime(booking.createdAt, t('date.notAvailable'), locale) }) }}
          </div>
        </div>

        <UAlert
          v-if="booking.status === 'confirmed'"
          :title="t('confirm.alreadyConfirmed')"
          :description="t('confirm.confirmedOn', { date: formatDateTime(booking.confirmedAt, t('date.notAvailable'), locale) })"
          color="success"
          icon="i-lucide-badge-check"
        />

        <UAlert
          v-if="!canManageBooking"
          :title="auth.user.value ? t('confirm.managementRestricted') : t('confirm.signInToManage')"
          color="warning"
          icon="i-lucide-lock-keyhole"
        />

        <div class="overflow-hidden rounded-lg border border-default">
          <div
            v-for="row in detailRows"
            :key="row.label"
            class="grid gap-1 border-b border-default px-4 py-3 text-sm last:border-b-0 sm:grid-cols-[11rem_minmax(0,1fr)] sm:gap-3"
          >
            <div class="text-xs font-medium uppercase tracking-wide text-muted sm:text-sm sm:normal-case sm:tracking-normal">
              {{ row.label }}
            </div>
            <div :class="getDetailValueClass(row)">
              {{ row.value }}
            </div>
          </div>

          <div class="grid gap-1 bg-primary/5 px-4 py-3 sm:grid-cols-[11rem_minmax(0,1fr)] sm:gap-3">
            <div class="text-xs font-semibold uppercase tracking-wide text-primary sm:text-sm sm:normal-case sm:tracking-normal">
              {{ t('common.totalPrice') }}
            </div>
            <div class="min-w-0 text-lg font-bold text-highlighted sm:text-xl">
              {{ totalFormatted }}
            </div>
          </div>
        </div>

        <div v-if="canManageBooking" class="space-y-3 rounded-lg border border-default p-4">
          <div class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between">
            <div>
              <h2 class="text-sm font-semibold text-highlighted">
                {{ t('confirm.payment') }}
              </h2>
              <p class="text-xs text-muted">
                {{ booking.status === 'pending' ? t('confirm.paymentPendingDescription') : t('confirm.paymentConfirmedDescription') }}
              </p>
            </div>
            <UBadge :label="paymentForm.paymentMethod === 'bank' ? t('confirm.paymentBank') : t('confirm.paymentCash')" :color="paymentForm.paymentMethod === 'bank' ? 'info' : 'neutral'" variant="soft" />
          </div>

          <URadioGroup
            v-model="paymentForm.paymentMethod"
            orientation="horizontal"
            variant="card"
            indicator="hidden"
            :items="paymentMethodItems"
            :disabled="booking.status !== 'pending' || confirming || savingPayment || uploadingTransactionDocument || deletingTransactionDocument"
            :ui="{
              fieldset: 'grid grid-cols-2 gap-3',
              item: 'rounded-lg border border-default bg-default p-3 transition-colors data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
            }"
          />

          <div v-if="paymentForm.paymentMethod === 'bank'" class="space-y-3">
            <div
              v-if="booking.transactionDocument"
              class="surface-panel flex flex-col gap-2 rounded-lg px-3 py-3 sm:flex-row sm:items-center sm:justify-between"
            >
              <div class="min-w-0">
                <p class="truncate text-sm font-medium text-highlighted">
                  {{ booking.transactionDocument.originalName }}
                </p>
                <p class="text-xs text-muted">
                  {{ formatFileSize(booking.transactionDocument.size) }} - {{ formatDateTime(booking.transactionDocument.uploadedAt, t('date.notAvailable'), locale) }}
                </p>
              </div>

              <div class="flex flex-col gap-2 sm:flex-row sm:items-center">
                <UButton
                  :to="booking.transactionDocument.url"
                  :label="t('confirm.download')"
                  color="neutral"
                  variant="outline"
                  icon="i-lucide-file-down"
                  size="sm"
                  class="justify-center"
                  :disabled="confirming || deletingTransactionDocument"
                />
                <UButton
                  v-if="booking.status === 'pending'"
                  :label="t('confirm.delete')"
                  color="error"
                  variant="outline"
                  icon="i-lucide-trash-2"
                  size="sm"
                  class="justify-center"
                  :loading="deletingTransactionDocument"
                  :disabled="confirming || uploadingTransactionDocument"
                  @click="deleteTransactionDocument"
                />
              </div>
            </div>

            <UFormField v-if="booking.status === 'pending'" name="transactionDocument" :label="t('confirm.transactionDocument')">
              <div class="grid gap-2 sm:grid-cols-[minmax(0,1fr)_auto]">
                <UInput
                  :key="transactionDocumentInputKey"
                  type="file"
                  :accept="transactionDocumentAccept"
                  :disabled="confirming || uploadingTransactionDocument || deletingTransactionDocument"
                  size="lg"
                  class="w-full"
                  @change="onTransactionDocumentChange"
                />
                <UButton
                  :label="t('confirm.upload')"
                  icon="i-lucide-upload"
                  class="justify-center"
                  :loading="uploadingTransactionDocument"
                  :disabled="!transactionDocumentFile || confirming || deletingTransactionDocument"
                  @click="uploadTransactionDocument()"
                />
              </div>
              <template #help>
                {{ selectedTransactionDocumentName || t('confirm.documentHelp') }}
              </template>
            </UFormField>
          </div>
        </div>

        <div class="action-row">
          <UButton
            to="/"
            :label="t('confirm.backToForm')"
            color="neutral"
            variant="ghost"
            class="justify-center"
          />

          <UButton
            v-if="canManageBooking && booking.status === 'confirmed'"
            :to="receiptPath"
            :label="t('confirm.openReceipt')"
            color="neutral"
            variant="outline"
            icon="i-lucide-receipt"
            class="justify-center"
          />

          <UButton
            v-if="canManageBooking && booking.status === 'pending'"
            :label="t('confirm.confirmBooking')"
            icon="i-lucide-check-check"
            class="justify-center"
            :loading="confirming"
            :disabled="savingPayment || uploadingTransactionDocument || deletingTransactionDocument"
            @click="confirmBooking"
          />

          <UButton
            v-else-if="canManageBooking"
            :label="t('confirm.cancelConfirmation')"
            color="error"
            variant="outline"
            icon="i-lucide-x-circle"
            class="justify-center"
            :loading="cancelling"
            :disabled="savingPayment || uploadingTransactionDocument || deletingTransactionDocument"
            @click="cancelBookingConfirmation"
          />

          <UButton
            v-else-if="!auth.user.value"
            :to="signInPath"
            :label="t('confirm.signInToManage')"
            color="primary"
            variant="outline"
            icon="i-lucide-log-in"
            class="justify-center"
          />
        </div>
      </UCard>
    </div>
  </UContainer>
</template>
