<template>
  <UContainer class="page-shell">
    <div class="space-y-5">
      <div class="page-header lg:flex-row lg:items-end lg:justify-between">
        <div class="space-y-1">
          <UBadge label="Bookings" color="primary" variant="soft" size="sm" class="page-eyebrow" />
          <h1 class="page-title">
            Booking list
          </h1>
          <p class="page-description">
            {{ auth.isSuperAdmin.value ? 'All submitted bookings across every PIC.' : 'Bookings assigned to you as PIC.' }}
          </p>
        </div>

        <UBadge
          :label="auth.isSuperAdmin.value ? 'Super Admin View' : 'Staff View'"
          :color="auth.isSuperAdmin.value ? 'primary' : 'neutral'"
          variant="soft"
          size="sm"
          class="rounded-full"
        />
      </div>

      <UAlert
        title="Inventory counting rule"
        :description="inventoryDescription"
        color="info"
        icon="i-lucide-info"
        variant="soft"
        :ui="{ root: 'py-3', title: 'text-sm font-medium', description: 'text-xs sm:text-sm' }"
      />

      <div v-if="auth.isSuperAdmin.value" class="grid gap-4 xl:grid-cols-[minmax(0,1.2fr)_18rem]">
        <UCard class="surface-card rounded-lg" :ui="{ header: 'px-4 py-3', body: 'px-4 py-3' }">
          <template #header>
            <div class="space-y-1">
              <h2 class="text-base font-semibold text-highlighted">
                Capacity settings
              </h2>
              <p class="text-xs text-muted sm:text-sm">
                Configure the event capacity used to calculate sold and left inventory.
              </p>
            </div>
          </template>

          <UForm :state="capacityForm" class="space-y-3" @submit="saveCapacity">
            <div class="flex flex-col gap-3 sm:flex-row sm:items-end">
              <UFormField name="totalSeats" label="Total Seats" class="flex-1">
                <UInput
                  v-model="capacityForm.totalSeats"
                  type="number"
                  inputmode="numeric"
                  min="0"
                  size="md"
                  class="w-full"
                  placeholder="Leave blank for no seat limit"
                />
              </UFormField>

              <UButton
                type="submit"
                label="Save Capacity"
                icon="i-lucide-save"
                size="md"
                :loading="savingCapacity"
                class="justify-center sm:min-w-32"
              />
            </div>

            <div class="flex items-center justify-between gap-3 border-t border-default pt-3">
              <p class="text-xs text-muted sm:text-sm">
                Last updated {{ settings.updatedAt ? formatDateTime(settings.updatedAt) : 'Not set yet' }}
              </p>
            </div>
          </UForm>
        </UCard>

        <UCard class="surface-card rounded-lg" :ui="{ header: 'px-4 py-3', body: 'px-4 py-3' }">
          <template #header>
            <div class="space-y-1">
              <h2 class="text-base font-semibold text-highlighted">
                Pending reservations
              </h2>
              <p class="text-xs text-muted sm:text-sm">
                Pending bookings are not counted as sold until the PIC confirms them.
              </p>
            </div>
          </template>

          <div class="grid gap-4 sm:grid-cols-2">
            <div class="surface-panel rounded-lg p-3">
              <p class="text-xs uppercase tracking-wide text-muted">
                Pending bookings
              </p>
              <p class="mt-1 text-xl font-semibold text-highlighted">
                {{ pendingCount }}
              </p>
            </div>

            <div class="surface-panel rounded-lg p-3">
              <p class="text-xs uppercase tracking-wide text-muted">
                Pending seats
              </p>
              <p class="mt-1 text-xl font-semibold text-highlighted">
                {{ summary.pendingSeats }}
              </p>
            </div>
          </div>
        </UCard>
      </div>

      <div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
        <UCard
          v-for="item in inventoryCards"
          :key="item.label"
          class="metric-card rounded-lg"
          :ui="{ body: 'px-4 py-3' }"
        >
          <div class="space-y-0.5">
            <p class="text-[11px] font-medium uppercase tracking-wide text-muted">
              {{ item.label }}
            </p>
            <p class="text-2xl font-semibold leading-none text-highlighted">
              {{ item.value }}
            </p>
          </div>
        </UCard>
      </div>

      <div class="grid gap-3 md:grid-cols-3">
        <UCard class="metric-card rounded-lg" :ui="{ body: 'px-4 py-3' }">
          <div class="space-y-0.5">
            <p class="text-[11px] font-medium uppercase tracking-wide text-muted">
              Total Seats
            </p>
            <p class="text-2xl font-semibold leading-none text-highlighted">
              {{ formatInventoryNumber(summary.totalSeats) }}
            </p>
          </div>
        </UCard>

        <UCard class="metric-card rounded-lg" :ui="{ body: 'px-4 py-3' }">
          <div class="space-y-0.5">
            <p class="text-[11px] font-medium uppercase tracking-wide text-muted">
              Total bookings
            </p>
            <p class="text-2xl font-semibold leading-none text-highlighted">
              {{ bookings.length }}
            </p>
          </div>
        </UCard>

        <UCard class="metric-card rounded-lg" :ui="{ body: 'px-4 py-3' }">
          <div class="space-y-0.5">
            <p class="text-[11px] font-medium uppercase tracking-wide text-muted">
              Booking status
            </p>
            <p class="text-sm font-medium text-default">
              {{ confirmedCount }} confirmed, {{ pendingCount }} pending
            </p>
          </div>
        </UCard>
      </div>

      <UCard
        class="surface-card overflow-hidden rounded-lg"
        :ui="{ header: 'px-4 py-3', body: 'p-0 sm:p-0' }"
      >
        <template #header>
          <div class="flex flex-col gap-2 lg:flex-row lg:items-center lg:justify-between">
            <div class="flex flex-col gap-2 sm:flex-row sm:items-center">
              <UInput
                v-model="searchQuery"
                size="md"
                class="w-full sm:w-72"
                placeholder="Search guest, phone, PIC, ticket, or remark"
              />

              <UButton
                label="Refresh"
                color="neutral"
                variant="outline"
                icon="i-lucide-refresh-cw"
                size="md"
                :loading="loadingBookings"
                @click="refreshBookings"
              />
            </div>
          </div>
        </template>

        <div>
          <UTable
            :data="filteredBookings"
            :columns="columns"
            :loading="loadingBookings"
            :empty="searchQuery.trim() ? 'No matching bookings found.' : 'No bookings available yet.'"
            sticky="header"
            caption="Bookings"
            class="compact-table"
            :ui="{ base: 'min-w-[1280px]' }"
          >
            <template #customerName-cell="{ row }">
              <div class="min-w-0 space-y-0.5 py-0.5">
                <div class="text-sm font-semibold leading-tight text-highlighted">
                  {{ row.original.customerName }}
                </div>
                <div class="text-xs text-muted">
                  {{ row.original.customerPhone }}
                </div>
              </div>
            </template>

            <template #quantity-cell="{ row }">
              <div class="space-y-0.5 py-0.5">
                <div class="text-sm font-medium text-default">
                  {{ ticketLabel(row.original) }}
                </div>
              </div>
            </template>

            <template #seatCount-cell="{ row }">
              <div class="space-y-0.5 py-0.5">
                <div class="text-sm font-medium text-default">
                  {{ row.original.seatCount }} seats
                </div>
                <div class="text-xs text-muted">
                  {{ formatBookingCurrency(row.original.totalPrice) }}
                </div>
              </div>
            </template>

            <template #personInChargeName-cell="{ row }">
              <div class="min-w-0 space-y-0.5 py-0.5">
                <div class="text-sm font-medium text-default">
                  {{ row.original.personInChargeName }}
                </div>
                <div class="text-xs text-muted">
                  {{ row.original.personInChargePhoneNumber }}
                </div>
              </div>
            </template>

            <template #paymentMethod-cell="{ row }">
              <div class="space-y-1 py-0.5">
                <UBadge
                  :label="getPaymentMethodLabel(row.original.paymentMethod)"
                  :color="row.original.paymentMethod === 'bank' ? 'info' : 'neutral'"
                  variant="soft"
                  size="sm"
                />

                <UButton
                  v-if="row.original.transactionDocument"
                  :to="row.original.transactionDocument.url"
                  external
                  :label="row.original.transactionDocument.originalName"
                  color="neutral"
                  variant="link"
                  icon="i-lucide-file-down"
                  size="xs"
                  class="-ms-2 max-w-44 justify-start truncate"
                />
                <div v-else-if="row.original.paymentMethod === 'bank'" class="text-xs text-muted">
                  No document
                </div>
              </div>
            </template>

            <template #remark-cell="{ row }">
              <div class="max-w-64 space-y-1 py-0.5">
                <p
                  v-if="row.original.remark"
                  class="whitespace-pre-wrap break-words text-sm leading-snug text-default"
                >
                  {{ remarkPreview(row.original.remark) }}
                </p>
                <p v-else class="text-xs text-muted">
                  No remark
                </p>

                <UButton
                  :label="row.original.remark ? 'Edit remark' : 'Add remark'"
                  color="neutral"
                  variant="ghost"
                  icon="i-lucide-message-square-text"
                  size="xs"
                  class="-ms-2"
                  @click="openRemarkEditor(row.original)"
                />
              </div>
            </template>

            <template #status-cell="{ row }">
              <div class="space-y-1 py-0.5">
                <UBadge
                  :label="getBookingStatusLabel(row.original.status, row.original.statusLabel)"
                  :color="row.original.status === 'confirmed' ? 'success' : 'warning'"
                  variant="soft"
                  size="sm"
                />
                <div class="text-xs text-muted">
                  {{ row.original.status === 'confirmed' ? `Confirmed ${formatDateTime(row.original.confirmedAt)}` : 'Awaiting PIC confirmation' }}
                </div>
              </div>
            </template>

            <template #createdAt-cell="{ row }">
              <span class="text-xs text-muted">
                {{ formatDateTime(row.original.createdAt) }}
              </span>
            </template>

            <template #actions-cell="{ row }">
              <div class="flex flex-nowrap items-center justify-end gap-1 py-0.5 whitespace-nowrap">
                <UTooltip text="Open booking">
                  <UButton
                    :to="confirmationPath(row.original)"
                    color="neutral"
                    variant="ghost"
                    icon="i-lucide-external-link"
                    size="sm"
                    class="min-w-9 justify-center"
                    aria-label="Open booking"
                  />
                </UTooltip>

                <UTooltip text="Open receipt">
                  <UButton
                    :to="receiptPath(row.original)"
                    color="neutral"
                    variant="ghost"
                    icon="i-lucide-receipt"
                    size="sm"
                    class="min-w-9 justify-center"
                    aria-label="Open receipt"
                  />
                </UTooltip>

                <UDropdownMenu :items="bookingActionMenuItems(row.original)">
                  <UButton
                    color="neutral"
                    variant="ghost"
                    icon="i-lucide-ellipsis-vertical"
                    size="sm"
                    class="min-w-9 justify-center"
                    :loading="isBookingRowActionBusy(row.original)"
                    :disabled="isBookingRowActionBusy(row.original)"
                    aria-label="More actions"
                  />
                </UDropdownMenu>
              </div>
            </template>
          </UTable>
        </div>
      </UCard>

      <UModal
        v-model:open="detailsModalOpen"
        title="Edit Booking"
        description="Update guest details, payment, ticket selection, quantity, and handling note."
        :dismissible="!savingDetails && !deletingTransactionDocument"
        :close="!savingDetails && !deletingTransactionDocument"
        :content="{ class: 'sm:max-w-2xl' }"
      >
        <template #body>
          <UForm
            id="bookingDetailsForm"
            :state="detailsForm"
            :validate="validateBookingDetails"
            class="space-y-4"
            @submit="saveBookingDetails"
          >
            <div v-if="detailsBooking" class="surface-panel rounded-lg px-3 py-2">
              <p class="text-sm font-medium text-highlighted">
                {{ detailsBooking.customerName }}
              </p>
              <p class="text-xs text-muted">
                Current: {{ ticketLabel(detailsBooking) }} - {{ detailsBooking.seatCount }} seats
              </p>
            </div>

            <div class="grid gap-4 sm:grid-cols-2">
              <UFormField name="customerName" label="Guest / Organizer" required>
                <UInput
                  v-model="detailsForm.customerName"
                  size="lg"
                  class="w-full"
                  placeholder="e.g. John Doe"
                />
              </UFormField>

              <UFormField name="customerPhone" label="Phone Number" required>
                <UInput
                  v-model="detailsForm.customerPhone"
                  size="lg"
                  type="tel"
                  class="w-full"
                  placeholder="e.g. +60123456789"
                />
              </UFormField>
            </div>

            <div class="grid gap-4 sm:grid-cols-2">
              <UFormField name="bookingMode" label="Booking Mode" required>
                <USelect
                  v-model="detailsForm.bookingMode"
                  :items="bookingModeItems"
                  :disabled="savingDetails || !bookingModeItems.length"
                  size="lg"
                  class="w-full"
                />
              </UFormField>

              <UFormField name="quantity" :label="selectedDetailsBookingMode?.quantityLabel || 'Quantity'" required>
                <UInputNumber
                  v-model="detailsForm.quantity"
                  :min="1"
                  :step="1"
                  :disabled="savingDetails || deletingTransactionDocument"
                  size="lg"
                  class="w-full"
                />
              </UFormField>
            </div>

            <UFormField name="ticketType" label="Ticket Category" required>
              <URadioGroup
                v-model="detailsForm.ticketType"
                orientation="horizontal"
                variant="card"
                indicator="hidden"
                :items="ticketCatalogItems"
                :disabled="savingDetails || !ticketCatalogItems.length"
                :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'
                }"
              />
            </UFormField>

            <UFormField name="paymentMethod" label="Payment Method" required>
              <URadioGroup
                v-model="detailsForm.paymentMethod"
                orientation="horizontal"
                variant="card"
                indicator="hidden"
                :items="paymentMethodItems"
                :disabled="savingDetails"
                :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'
                }"
              />
            </UFormField>

            <div v-if="detailsForm.paymentMethod === 'bank'" class="space-y-3">
              <div
                v-if="detailsBooking?.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">
                    {{ detailsBooking.transactionDocument.originalName }}
                  </p>
                  <p class="text-xs text-muted">
                    {{ formatFileSize(detailsBooking.transactionDocument.size) }} - {{ formatDateTime(detailsBooking.transactionDocument.uploadedAt) }}
                  </p>
                </div>

                <div class="flex flex-col gap-2 sm:flex-row sm:items-center">
                  <UButton
                    :to="detailsBooking.transactionDocument.url"
                    external
                    label="Download"
                    color="neutral"
                    variant="outline"
                    icon="i-lucide-file-down"
                    size="sm"
                    class="justify-center"
                    :disabled="deletingTransactionDocument"
                  />
                  <UButton
                    label="Delete"
                    color="error"
                    variant="outline"
                    icon="i-lucide-trash-2"
                    size="sm"
                    class="justify-center"
                    :loading="deletingTransactionDocument"
                    :disabled="savingDetails"
                    @click="deleteTransactionDocumentForDetails"
                  />
                </div>
              </div>

              <UFormField name="transactionDocument" label="Transaction Document">
                <UInput
                  :key="transactionDocumentInputKey"
                  type="file"
                  :accept="transactionDocumentAccept"
                  :disabled="savingDetails || deletingTransactionDocument"
                  size="lg"
                  class="w-full"
                  @change="onTransactionDocumentChange"
                />
                <template #help>
                  {{ selectedTransactionDocumentName || 'PDF, JPG, PNG, WEBP, HEIC, or HEIF - max 10MB.' }}
                </template>
              </UFormField>
            </div>

            <div class="surface-panel rounded-lg px-4 py-3">
              <div class="flex items-center justify-between gap-4">
                <span class="text-sm font-medium text-muted">Updated total</span>
                <div class="text-right">
                  <div class="text-lg font-semibold text-highlighted">
                    {{ detailsTotalFormatted }}
                  </div>
                  <div class="text-xs text-muted">
                    {{ detailsSeatCount }} seats
                  </div>
                </div>
              </div>
            </div>

            <UFormField name="remark" label="Remark">
              <UTextarea
                v-model="detailsForm.remark"
                :rows="4"
                :maxlength="remarkLimit"
                autoresize
                class="w-full"
                placeholder="Handling note"
              />
              <template #help>
                {{ detailsForm.remark.length }}/{{ remarkLimit }}
              </template>
            </UFormField>
          </UForm>
        </template>

        <template #footer>
          <div class="flex w-full flex-col-reverse gap-2 sm:flex-row sm:justify-end">
            <UButton
              label="Cancel"
              color="neutral"
              variant="ghost"
              class="justify-center"
              :disabled="savingDetails || deletingTransactionDocument"
              @click="closeBookingEditor"
            />
            <UButton
              type="submit"
              form="bookingDetailsForm"
              label="Save Booking"
              icon="i-lucide-save"
              class="justify-center"
              :loading="savingDetails"
              :disabled="deletingTransactionDocument"
            />
          </div>
        </template>
      </UModal>

      <UModal
        v-model:open="remarkModalOpen"
        title="Booking Remark"
        description="Management-only note for this booking."
      >
        <template #body>
          <div class="space-y-4">
            <div v-if="editingBooking" class="surface-panel rounded-lg px-3 py-2">
              <p class="text-sm font-medium text-highlighted">
                {{ editingBooking.customerName }}
              </p>
              <p class="text-xs text-muted">
                {{ ticketLabel(editingBooking) }} - {{ editingBooking.seatCount }} seats
              </p>
            </div>

            <UFormField name="remark" label="Remark">
              <UTextarea
                v-model="remarkForm.remark"
                :rows="5"
                :maxlength="remarkLimit"
                autoresize
                class="w-full"
                placeholder="Handling note"
              />
              <template #help>
                {{ remarkForm.remark.length }}/{{ remarkLimit }}
              </template>
            </UFormField>
          </div>
        </template>

        <template #footer>
          <div class="flex w-full flex-col-reverse gap-2 sm:flex-row sm:justify-end">
            <UButton
              label="Cancel"
              color="neutral"
              variant="ghost"
              class="justify-center"
              :disabled="savingRemark"
              @click="closeRemarkEditor"
            />
            <UButton
              label="Save Remark"
              icon="i-lucide-save"
              class="justify-center"
              :loading="savingRemark"
              @click="saveRemark"
            />
          </div>
        </template>
      </UModal>

      <UModal
        v-model:open="transferModalOpen"
        title="Transfer Booking"
        description="Assign this booking to another PIC."
      >
        <template #body>
          <div class="space-y-4">
            <div v-if="transferringBooking" class="surface-panel rounded-lg px-3 py-2">
              <p class="text-sm font-medium text-highlighted">
                {{ transferringBooking.customerName }}
              </p>
              <p class="text-xs text-muted">
                Current PIC: {{ transferringBooking.personInChargeName }}
              </p>
            </div>

            <UFormField name="personInChargeId" label="New PIC">
              <USelect
                v-model="transferForm.personInChargeId"
                :items="transferPersonInChargeItems"
                :disabled="savingTransfer || !transferPersonInChargeItems.length"
                class="w-full"
              />
            </UFormField>
          </div>
        </template>

        <template #footer>
          <div class="flex w-full flex-col-reverse gap-2 sm:flex-row sm:justify-end">
            <UButton
              label="Cancel"
              color="neutral"
              variant="ghost"
              class="justify-center"
              :disabled="savingTransfer"
              @click="closeTransferEditor"
            />
            <UButton
              label="Transfer Booking"
              icon="i-lucide-send"
              class="justify-center"
              :loading="savingTransfer"
              :disabled="!transferForm.personInChargeId"
              @click="saveTransfer"
            />
          </div>
        </template>
      </UModal>
    </div>
  </UContainer>
</template>

<script lang="ts" setup>
import type { FormError, FormSubmitEvent } from '@nuxt/ui'

import type { PublicContact } from '~~/shared/auth'
import type {
  BookingCapacitySettings,
  BookingInventorySummary,
  BookingModeOption,
  BookingMode,
  CancelBookingConfirmationResponse,
  DeleteBookingResponse,
  PaymentMethod,
  PublicBooking,
  PublicBookingConfig,
  TicketCatalogItem,
  TicketType,
  TransferBookingPicResponse,
  UpdateBookingDetailsResponse
} from '~~/shared/booking'

import {
  DEFAULT_PHONE_COUNTRY_CODE,
  isValidPhoneNumber,
  normalizePhoneNumber
} from '~~/shared/auth'
import {
  formatBookingCurrency,
  getBookingStatusLabel,
  getSeatCount
} from '~~/shared/booking'

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

definePageMeta({
  middleware: 'auth'
})

useSeoMeta({
  title: 'Bookings',
  description: 'Manage dinner ticket bookings, confirmations, receipts, and seat allocation.',
  robots: 'noindex,nofollow'
})

const toast = useToast()
const apiClient = useApiClient()
const auth = useAuth()

const bookings = ref<PublicBooking[]>([])
const bookingConfig = ref<PublicBookingConfig | null>(null)
const contacts = ref<PublicContact[]>([])
const loadingBookings = ref(false)
const loadingBookingConfig = ref(false)
const loadingContacts = ref(false)
const savingCapacity = ref(false)
const savingDetails = ref(false)
const savingRemark = ref(false)
const savingTransfer = ref(false)
const deletingTransactionDocument = ref(false)
const cancellingBookingId = ref<string | null>(null)
const deletingBookingId = ref<string | null>(null)
const detailsModalOpen = ref(false)
const remarkModalOpen = ref(false)
const transferModalOpen = ref(false)
const detailsBooking = ref<PublicBooking | null>(null)
const editingBooking = ref<PublicBooking | null>(null)
const transferringBooking = ref<PublicBooking | null>(null)
const searchQuery = ref('')
const settings = reactive<BookingCapacitySettings>({
  totalSeats: null,
  updatedAt: null
})
const summary = reactive<BookingInventorySummary>({
  totalSeats: null,
  soldSeats: 0,
  pendingSeats: 0,
  leftSeats: null
})
const capacityForm = reactive({
  totalSeats: ''
})
const detailsForm = reactive({
  customerName: '',
  customerPhone: DEFAULT_PHONE_COUNTRY_CODE,
  bookingMode: '' as BookingMode,
  quantity: 1,
  ticketType: '' as TicketType,
  paymentMethod: 'cash' as PaymentMethod,
  remark: ''
})
const detailsTransactionDocumentFile = ref<File | null>(null)
const transactionDocumentInputKey = ref(0)
const remarkForm = reactive({
  remark: ''
})
const transferForm = reactive({
  personInChargeId: ''
})
const remarkLimit = 1000
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 paymentMethodItems: { label: string, value: PaymentMethod }[] = [
  {
    label: 'Cash',
    value: 'cash'
  },
  {
    label: 'Bank',
    value: 'bank'
  }
]

const bookingModeItems = computed(() => {
  return bookingConfig.value?.bookingModes.map((mode: BookingModeOption) => ({
    label: mode.label,
    value: mode.value
  })) || []
})

const ticketCatalogItems = computed(() => {
  return bookingConfig.value?.ticketCatalog.map((ticket: TicketCatalogItem) => ({
    label: ticket.label,
    value: ticket.value,
    description: ticket.description
  })) || []
})

const selectedDetailsBookingMode = computed(() => {
  return bookingConfig.value?.bookingModes.find((mode) => mode.value === detailsForm.bookingMode) ?? null
})

const selectedDetailsTicket = computed(() => {
  return bookingConfig.value?.ticketCatalog.find((ticket) => ticket.value === detailsForm.ticketType) ?? null
})

const detailsSeatCount = computed(() => getSeatCount(selectedDetailsBookingMode.value, detailsForm.quantity))
const detailsTotalFormatted = computed(() => formatBookingCurrency(detailsSeatCount.value * (selectedDetailsTicket.value?.price ?? 0)))
const selectedTransactionDocumentName = computed(() => detailsTransactionDocumentFile.value?.name || '')

const columns = [
  { accessorKey: 'customerName', header: 'Guest' },
  { accessorKey: 'quantity', header: 'Booking' },
  { accessorKey: 'seatCount', header: 'Seats / Total' },
  { accessorKey: 'personInChargeName', header: 'PIC' },
  { accessorKey: 'paymentMethod', header: 'Payment' },
  { accessorKey: 'remark', header: 'Remark' },
  { id: 'status', header: 'Status' },
  { accessorKey: 'createdAt', header: 'Submitted' },
  { id: 'actions', header: 'Actions' }
]

const inventoryDescription = computed(() => {
  return 'Every booking is converted into seats immediately, so sold and remaining capacity are tracked only in seats.'
})

const inventoryCards = computed(() => {
  return [
    {
      label: 'Seats Sold',
      value: String(summary.soldSeats)
    },
    {
      label: 'Pending Seats',
      value: String(summary.pendingSeats)
    },
    {
      label: 'Seats Left',
      value: formatInventoryNumber(summary.leftSeats)
    }
  ]
})

const filteredBookings = computed(() => {
  const keyword = searchQuery.value.trim().toLowerCase()

  if (!keyword) {
    return bookings.value
  }

  return bookings.value.filter((booking) => {
    return [
      booking.customerName,
      booking.customerPhone,
      booking.personInChargeName,
      booking.personInChargePhoneNumber,
      booking.ticketType,
      booking.ticketLabel,
      getPaymentMethodLabel(booking.paymentMethod),
      booking.transactionDocument?.originalName || '',
      booking.remark || '',
      booking.status
    ].some((value) => value.toLowerCase().includes(keyword))
  })
})

const pendingCount = computed(() => {
  return bookings.value.filter((booking) => booking.status === 'pending').length
})

const confirmedCount = computed(() => {
  return bookings.value.filter((booking) => booking.status === 'confirmed').length
})

const transferPersonInChargeItems = computed(() => {
  const currentPicId = transferringBooking.value?.personInChargeId

  return contacts.value
    .filter((contact) => contact.id !== currentPicId)
    .map((contact) => ({
      label: contact.fullName,
      value: contact.id
    }))
})

await Promise.all([
  refreshBookingConfig(),
  refreshBookings(),
  refreshContacts()
])

function ticketLabel(booking: PublicBooking) {
  return booking.ticketLabel || booking.ticketType.toUpperCase()
}

function getPaymentMethodLabel(paymentMethod: PaymentMethod) {
  return paymentMethod === 'bank' ? 'Bank' : 'Cash'
}

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 remarkPreview(remark: string) {
  const normalized = remark.trim()
  return normalized.length > 120 ? `${normalized.slice(0, 120)}...` : normalized
}

function confirmationPath(booking: PublicBooking) {
  return `/confirmation/${booking.confirmationToken}`
}

function receiptPath(booking: PublicBooking) {
  return `/receipt/${booking.receiptToken}`
}

function isBookingRowActionBusy(booking: PublicBooking) {
  return Boolean(
    (deletingBookingId.value && deletingBookingId.value !== booking.id)
    || (cancellingBookingId.value && cancellingBookingId.value !== booking.id)
    || deletingBookingId.value === booking.id
    || cancellingBookingId.value === booking.id
  )
}

function bookingActionMenuItems(booking: PublicBooking) {
  const busy = isBookingRowActionBusy(booking)
  const items = [
    [
      {
        label: 'Edit booking',
        icon: 'i-lucide-pencil-line',
        disabled: busy || !bookingConfig.value,
        onSelect: () => openBookingEditor(booking)
      },
      {
        label: 'Transfer booking',
        icon: 'i-lucide-send',
        disabled: busy || !hasTransferTargets(booking),
        onSelect: () => openTransferEditor(booking)
      }
    ]
  ]

  if (booking.status === 'confirmed') {
    items.push([
      {
        label: 'Unconfirm booking',
        icon: 'i-lucide-x-circle',
        color: 'error',
        disabled: busy,
        onSelect: () => cancelBookingConfirmation(booking)
      }
    ])
  }

  items.push([
    {
      label: 'Delete booking',
      icon: 'i-lucide-trash-2',
      color: 'error',
      disabled: busy,
      onSelect: () => deleteBooking(booking)
    }
  ])

  return items
}

function openBookingEditor(booking: PublicBooking) {
  detailsBooking.value = booking
  detailsForm.customerName = booking.customerName
  detailsForm.customerPhone = booking.customerPhone
  detailsForm.bookingMode = booking.bookingMode
  detailsForm.quantity = booking.quantity
  detailsForm.ticketType = booking.ticketType
  detailsForm.paymentMethod = booking.paymentMethod
  detailsForm.remark = booking.remark || ''
  detailsTransactionDocumentFile.value = null
  transactionDocumentInputKey.value += 1
  detailsModalOpen.value = true
}

function closeBookingEditor() {
  if (savingDetails.value || deletingTransactionDocument.value) {
    return
  }

  detailsModalOpen.value = false
  detailsBooking.value = null
  detailsForm.customerName = ''
  detailsForm.customerPhone = DEFAULT_PHONE_COUNTRY_CODE
  detailsForm.bookingMode = ''
  detailsForm.quantity = 1
  detailsForm.ticketType = ''
  detailsForm.paymentMethod = 'cash'
  detailsForm.remark = ''
  detailsTransactionDocumentFile.value = null
  transactionDocumentInputKey.value += 1
}

function validateBookingDetails(state: typeof detailsForm): FormError[] {
  const errors: FormError[] = []

  if (!state.customerName.trim()) {
    errors.push({ name: 'customerName', message: 'Please enter the guest or organizer name.' })
  }

  if (!state.customerPhone.trim()) {
    errors.push({ name: 'customerPhone', message: 'Please enter a contact number.' })
  } else if (!isValidPhoneNumber(state.customerPhone.trim())) {
    errors.push({ name: 'customerPhone', message: 'Use a valid phone number with country code, e.g. +60123456789.' })
  }

  if (state.quantity < 1) {
    errors.push({ name: 'quantity', message: 'Quantity must be at least 1.' })
  }

  if (!selectedDetailsBookingMode.value) {
    errors.push({ name: 'bookingMode', message: 'Please select a booking mode.' })
  }

  if (!selectedDetailsTicket.value) {
    errors.push({ name: 'ticketType', message: 'Please select a ticket category.' })
  }

  if (state.paymentMethod !== 'cash' && state.paymentMethod !== 'bank') {
    errors.push({ name: 'paymentMethod', message: 'Please select Cash or Bank.' })
  }

  if (detailsTransactionDocumentFile.value && detailsTransactionDocumentFile.value.size > transactionDocumentLimit) {
    errors.push({ name: 'transactionDocument', message: 'Transaction document must be 10MB or smaller.' })
  }

  return errors
}

async function refreshBookingConfig() {
  if (loadingBookingConfig.value) {
    return
  }

  loadingBookingConfig.value = true

  try {
    const response = await apiClient<PublicBookingConfig>('/api/public/booking-config')
    bookingConfig.value = response
  } catch (error: any) {
    toast.add({
      title: 'Unable to load booking settings',
      description: getErrorMessage(error, 'The booking editor could not load the active event settings.'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    loadingBookingConfig.value = false
  }
}

function formatInventoryNumber(value: number | null) {
  return value === null ? 'Not set' : String(value)
}

function normalizeCapacityValue(value: string | number | null | undefined) {
  if (typeof value === 'number') {
    return Number.isFinite(value) ? String(value) : null
  }

  const normalized = String(value || '').trim()
  return normalized || null
}

function syncCapacityForm(nextSettings: BookingCapacitySettings) {
  capacityForm.totalSeats = nextSettings.totalSeats === null ? '' : String(nextSettings.totalSeats)
}

function applySettings(nextSettings: BookingCapacitySettings) {
  settings.totalSeats = nextSettings.totalSeats
  settings.updatedAt = nextSettings.updatedAt
  syncCapacityForm(nextSettings)
}

function applySummary(nextSummary: BookingInventorySummary) {
  summary.totalSeats = nextSummary.totalSeats
  summary.soldSeats = nextSummary.soldSeats
  summary.pendingSeats = nextSummary.pendingSeats
  summary.leftSeats = nextSummary.leftSeats
}

function replaceBooking(updatedBooking: PublicBooking) {
  const index = bookings.value.findIndex((booking) => booking.id === updatedBooking.id)

  if (index === -1) {
    return
  }

  bookings.value.splice(index, 1, updatedBooking)
}

function removeBooking(bookingId: string) {
  bookings.value = bookings.value.filter((booking) => booking.id !== bookingId)
}

function openRemarkEditor(booking: PublicBooking) {
  editingBooking.value = booking
  remarkForm.remark = booking.remark || ''
  remarkModalOpen.value = true
}

function closeRemarkEditor() {
  if (savingRemark.value) {
    return
  }

  remarkModalOpen.value = false
  editingBooking.value = null
  remarkForm.remark = ''
}

function hasTransferTargets(booking: PublicBooking) {
  return contacts.value.some((contact) => contact.id !== booking.personInChargeId)
}

function openTransferEditor(booking: PublicBooking) {
  const nextTarget = contacts.value.find((contact) => contact.id !== booking.personInChargeId)

  transferringBooking.value = booking
  transferForm.personInChargeId = nextTarget?.id ?? ''
  transferModalOpen.value = true
}

function closeTransferEditor() {
  if (savingTransfer.value) {
    return
  }

  transferModalOpen.value = false
  transferringBooking.value = null
  transferForm.personInChargeId = ''
}

function onTransactionDocumentChange(event: Event) {
  const input = event.target as HTMLInputElement
  detailsTransactionDocumentFile.value = input.files?.[0] ?? null
}

async function deleteTransactionDocumentForDetails() {
  const booking = detailsBooking.value

  if (!booking?.transactionDocument || deletingTransactionDocument.value || savingDetails.value) {
    return
  }

  if (import.meta.client && !window.confirm(`Delete transaction document for ${booking.customerName}?`)) {
    return
  }

  deletingTransactionDocument.value = true

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

    replaceBooking(response.booking)
    detailsBooking.value = response.booking
    detailsTransactionDocumentFile.value = null
    transactionDocumentInputKey.value += 1
    await refreshBookings()

    toast.add({
      title: 'Document deleted',
      description: 'The transaction document has been removed.',
      color: 'success',
      icon: 'i-lucide-trash-2'
    })
  } catch (error: any) {
    toast.add({
      title: 'Delete failed',
      description: getErrorMessage(error, 'Unable to delete the transaction document.'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    deletingTransactionDocument.value = false
  }
}

async function refreshBookings() {
  if (loadingBookings.value) {
    return
  }

  loadingBookings.value = true

  try {
    const response = await apiClient<{
      bookings: PublicBooking[]
      settings: BookingCapacitySettings
      summary: BookingInventorySummary
    }>('/api/bookings')

    bookings.value = response.bookings
    applySettings(response.settings)
    applySummary(response.summary)
  } catch (error: any) {
    toast.add({
      title: 'Unable to load bookings',
      description: getErrorMessage(error, 'The booking list could not be loaded.'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    loadingBookings.value = false
  }
}

async function refreshContacts() {
  if (loadingContacts.value) {
    return
  }

  loadingContacts.value = true

  try {
    const response = await apiClient<{ contacts: PublicContact[] }>('/api/public/contacts')
    contacts.value = response.contacts
  } catch (error: any) {
    toast.add({
      title: 'Unable to load PIC list',
      description: getErrorMessage(error, 'The PIC transfer list could not be loaded.'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    loadingContacts.value = false
  }
}

async function saveBookingDetails(event: FormSubmitEvent<typeof detailsForm>) {
  const booking = detailsBooking.value

  event.preventDefault()

  if (!booking || savingDetails.value || deletingTransactionDocument.value) {
    return
  }

  savingDetails.value = true

  try {
    const response = await apiClient<UpdateBookingDetailsResponse>(`/api/bookings/${booking.id}`, {
      method: 'PATCH',
      body: {
        customerName: detailsForm.customerName.trim(),
        customerPhone: normalizePhoneNumber(detailsForm.customerPhone),
        bookingMode: detailsForm.bookingMode,
        quantity: detailsForm.quantity,
        ticketType: detailsForm.ticketType,
        paymentMethod: detailsForm.paymentMethod,
        remark: detailsForm.remark
      }
    })

    let updatedBooking = response.booking

    if (detailsForm.paymentMethod === 'bank' && detailsTransactionDocumentFile.value) {
      const formData = new FormData()
      formData.append('document', detailsTransactionDocumentFile.value)

      const uploadResponse = await apiClient<UpdateBookingDetailsResponse>(`/api/bookings/${booking.id}/transaction-document`, {
        method: 'POST',
        body: formData
      })

      updatedBooking = uploadResponse.booking
    }

    replaceBooking(updatedBooking)
    await refreshBookings()
    detailsModalOpen.value = false
    detailsBooking.value = null
    detailsTransactionDocumentFile.value = null
    transactionDocumentInputKey.value += 1

    toast.add({
      title: 'Booking updated',
      description: 'The booking details have been saved.',
      color: 'success',
      icon: 'i-lucide-check-circle-2'
    })
  } catch (error: any) {
    toast.add({
      title: 'Booking update failed',
      description: getErrorMessage(error, 'Unable to save the booking details.'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    savingDetails.value = false
  }
}

async function deleteBooking(booking: PublicBooking) {
  if (deletingBookingId.value) {
    return
  }

  if (import.meta.client && !window.confirm(`Delete booking for ${booking.customerName}? It will be archived and removed from the active list.`)) {
    return
  }

  deletingBookingId.value = booking.id

  try {
    const response = await apiClient<DeleteBookingResponse>(`/api/bookings/${booking.id}`, {
      method: 'DELETE'
    })

    removeBooking(response.booking.id)
    await refreshBookings()

    toast.add({
      title: 'Booking deleted',
      description: 'The booking has been moved to archived state.',
      color: 'success',
      icon: 'i-lucide-trash-2'
    })
  } catch (error: any) {
    toast.add({
      title: 'Delete failed',
      description: getErrorMessage(error, 'Unable to delete the booking.'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    deletingBookingId.value = null
  }
}

async function saveCapacity(event: Event) {
  event.preventDefault()

  if (!auth.isSuperAdmin.value || savingCapacity.value) {
    return
  }

  savingCapacity.value = true

  try {
    const response = await apiClient<{ settings: BookingCapacitySettings }>('/api/bookings/capacity', {
      method: 'PATCH',
      body: {
        totalSeats: normalizeCapacityValue(capacityForm.totalSeats)
      }
    })

    applySettings(response.settings)
    await refreshBookings()

    toast.add({
      title: 'Capacity updated',
      description: 'The booking inventory limits have been saved.',
      color: 'success',
      icon: 'i-lucide-check-circle-2'
    })
  } catch (error: any) {
    toast.add({
      title: 'Capacity update failed',
      description: getErrorMessage(error, 'Unable to save the booking inventory limits.'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    savingCapacity.value = false
  }
}

async function saveRemark() {
  const booking = editingBooking.value

  if (!booking || savingRemark.value) {
    return
  }

  savingRemark.value = true

  try {
    const response = await apiClient<{ booking: PublicBooking }>(`/api/bookings/${booking.id}/remark`, {
      method: 'PATCH',
      body: {
        remark: remarkForm.remark
      }
    })

    replaceBooking(response.booking)
    remarkModalOpen.value = false
    editingBooking.value = null
    remarkForm.remark = ''

    toast.add({
      title: 'Remark saved',
      description: 'The booking remark has been updated.',
      color: 'success',
      icon: 'i-lucide-check-circle-2'
    })
  } catch (error: any) {
    toast.add({
      title: 'Remark update failed',
      description: getErrorMessage(error, 'Unable to save the booking remark.'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    savingRemark.value = false
  }
}

async function saveTransfer() {
  const booking = transferringBooking.value
  const nextPersonInChargeId = transferForm.personInChargeId

  if (!booking || !nextPersonInChargeId || savingTransfer.value) {
    return
  }

  savingTransfer.value = true

  try {
    const response = await apiClient<TransferBookingPicResponse>(`/api/bookings/${booking.id}/pic`, {
      method: 'PATCH',
      body: {
        personInChargeId: nextPersonInChargeId
      }
    })

    const nextPicName = response.booking.personInChargeName || 'the selected PIC'

    if (auth.isSuperAdmin.value || response.booking.personInChargeId === auth.user.value?.id) {
      replaceBooking(response.booking)
    } else {
      removeBooking(response.booking.id)
    }

    transferModalOpen.value = false
    transferringBooking.value = null
    transferForm.personInChargeId = ''

    toast.add({
      title: 'Booking transferred',
      description: `${booking.customerName} is now assigned to ${nextPicName}.`,
      color: 'success',
      icon: 'i-lucide-check-circle-2'
    })
  } catch (error: any) {
    toast.add({
      title: 'Transfer failed',
      description: getErrorMessage(error, 'Unable to transfer this booking.'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    savingTransfer.value = false
  }
}

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

  if (import.meta.client && !window.confirm(`Cancel confirmation for ${booking.customerName}? The booking will return to pending and the seats will be released.`)) {
    return
  }

  cancellingBookingId.value = booking.id

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

    replaceBooking(response.booking)
    await refreshBookings()

    toast.add({
      title: response.alreadyPending ? 'Booking already pending' : 'Confirmation cancelled',
      description: response.alreadyPending
        ? `${booking.customerName} was already pending confirmation.`
        : `${booking.customerName} has been returned to pending status.`,
      color: response.alreadyPending ? 'warning' : 'success',
      icon: 'i-lucide-x-circle'
    })
  } catch (error: any) {
    toast.add({
      title: 'Cancellation failed',
      description: getErrorMessage(error, 'Unable to cancel the booking confirmation.'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    cancellingBookingId.value = null
  }
}
</script>
