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

import {
  DEFAULT_PHONE_COUNTRY_CODE,
  isValidPhoneNumber,
  normalizePhoneNumber,
  type PublicContact
} from '~~/shared/auth'
import type { BookingModeOption, CreateBookingResponse, PublicBookingConfig, TicketCatalogItem } from '~~/shared/booking'
import {
  formatBookingCurrency,
  getSeatCount,
  type BookingMode,
  type TicketType
} from '~~/shared/booking'

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


const toast = useToast()
const apiClient = useApiClient()
const { locale, t } = useLocale()

const [bookingConfig, contactsResponse] = await Promise.all([
  apiClient<PublicBookingConfig>('/api/public/booking-config'),
  apiClient<{ contacts: PublicContact[] }>('/api/public/contacts')
])

const eventDetails = computed(() => [
  {
    label: t('common.date'),
    value: bookingConfig.event.dateLabel,
    icon: 'lucide:calendar-days'
  },
  {
    label: t('common.time'),
    value: bookingConfig.event.timeLabel,
    icon: 'lucide:clock-6'
  },
  {
    label: t('common.venue'),
    value: bookingConfig.event.venue,
    icon: 'lucide:map-pin'
  }
])

const bookingModeOptions = computed(() => {
  return bookingConfig.bookingModes.map((mode) => ({
    value: mode.value,
    label: locale.value === 'zh' ? translateBookingModeLabel(mode.label) : mode.label
  }))
})

const ticketCatalogOptions = computed(() => {
  return bookingConfig.ticketCatalog.map((ticket) => ({
    value: ticket.value,
    label: locale.value === 'zh' ? translateTicketLabel(ticket.label) : ticket.label,
    description: locale.value === 'zh' ? translateTicketDescription(ticket.description) : ticket.description
  }))
})

const personInCharge = computed(() => {
  return contactsResponse.contacts.map((contact) => ({
    label: contact.fullName,
    value: contact.id
  }))
})

const form = reactive({
  name: '',
  phone: DEFAULT_PHONE_COUNTRY_CODE,
  bookingMode: (bookingConfig.bookingModes[0]?.value ?? '') as BookingMode,
  quantity: 1,
  ticketType: (bookingConfig.ticketCatalog[0]?.value ?? '') as TicketType
})

const selectedPersonInCharge = ref(contactsResponse.contacts[0]?.id ?? '')

const selectedPersonInChargeRecord = computed(() => {
  return contactsResponse.contacts.find((contact) => contact.id === selectedPersonInCharge.value) ?? null
})

const selectedBookingMode = computed<BookingModeOption | null>(() => {
  return bookingConfig.bookingModes.find((mode) => mode.value === form.bookingMode) ?? bookingConfig.bookingModes[0] ?? null
})

const selectedTicket = computed<TicketCatalogItem | null>(() => {
  return bookingConfig.ticketCatalog.find((ticket) => ticket.value === form.ticketType) ?? bookingConfig.ticketCatalog[0] ?? null
})

const submittingBooking = ref(false)

const quantityLabel = computed(() => {
  if (selectedBookingMode.value?.quantityLabel) {
    return locale.value === 'zh'
      ? translateBookingModeQuantityLabel(selectedBookingMode.value.quantityLabel)
      : selectedBookingMode.value.quantityLabel
  }

  return t('booking.quantity')
})

const seatCount = computed(() => getSeatCount(selectedBookingMode.value, form.quantity))
const totalPrice = computed(() => seatCount.value * (selectedTicket.value?.price ?? 0))

const totalFormatted = computed(() => formatBookingCurrency(totalPrice.value, locale.value))

function translateBookingModeLabel(label: string) {
  const normalized = label.toLowerCase()

  if (normalized.includes('table')) {
    return '桌席（10 个座位）'
  }

  if (normalized.includes('seat')) {
    return '座位'
  }

  return label
}

function translateBookingModeQuantityLabel(label: string) {
  const normalized = label.toLowerCase()

  if (normalized.includes('table')) {
    return '桌数'
  }

  if (normalized.includes('seat')) {
    return '座位数量'
  }

  return label
}

function translateTicketLabel(label: string) {
  const normalized = label.toLowerCase()

  if (normalized.includes('supporter')) {
    return '支持者'
  }

  return label
}

function translateTicketDescription(description: string) {
  return description.replace(/seat/gi, locale.value === 'zh' ? '座位' : 'seat')
}

function validateBooking(state: typeof form): FormError[] {
  const errors: FormError[] = []

  if (!state.name.trim()) {
    errors.push({ name: 'name', message: t('booking.nameRequired') })
  }

  if (!state.phone.trim()) {
    errors.push({ name: 'phone', message: t('booking.phoneRequired') })
  } else if (!isValidPhoneNumber(state.phone.trim())) {
    errors.push({ name: 'phone', message: t('booking.phoneInvalid') })
  }

  if (state.quantity < 1) {
    errors.push({ name: 'quantity', message: t('booking.quantityMin', { label: quantityLabel.value }) })
  }

  if (!selectedBookingMode.value) {
    errors.push({ name: 'bookingMode', message: t('booking.modeRequired') })
  }

  if (!selectedTicket.value) {
    errors.push({ name: 'ticketType', message: t('booking.ticketRequired') })
  }

  return errors
}

async function bookTicket(event: FormSubmitEvent<typeof form>) {
  const selectedPic = selectedPersonInChargeRecord.value

  if (!selectedPic) {
    toast.add({
      title: t('booking.noPicTitle'),
      description: t('booking.noPicDescription'),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
    return
  }

  event.preventDefault()

  submittingBooking.value = true

  try {
    const response = await apiClient<CreateBookingResponse>('/api/public/bookings', {
      method: 'POST',
      body: {
        customerName: form.name.trim(),
        customerPhone: normalizePhoneNumber(form.phone),
        bookingMode: form.bookingMode,
        quantity: form.quantity,
        ticketType: form.ticketType,
        personInChargeId: selectedPic.id,
        locale: locale.value
      }
    })

    const bookingWindow = window.open(response.whatsappUrl, '_blank', 'noopener,noreferrer')

    if (!bookingWindow) {
      window.location.assign(response.whatsappUrl)
      return
    }

    toast.add({
      title: t('booking.whatsappOpened'),
      description: t('booking.whatsappOpenedDescription', { name: selectedPic.fullName }),
      color: 'success',
      icon: 'i-lucide-check-circle-2'
    })
  } catch (error) {
    toast.add({
      title: t('booking.createFailed'),
      description: getErrorMessage(error, t('booking.tryAgain')),
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    submittingBooking.value = false
  }
}
</script>

<template>
  <UContainer class="page-shell-narrow">
    <div class="grid gap-5 xl:grid-cols-[22rem_minmax(0,1fr)] xl:items-start xl:gap-8">
      <UCard
        class="surface-card overflow-hidden rounded-lg xl:sticky xl:top-6"
        :ui="{ header: 'px-4 py-4 sm:px-5 sm:py-5', body: 'space-y-4 px-4 pb-4 pt-0 sm:px-5 sm:pb-5' }"
      >
        <template #header>
          <div class="space-y-1">
            <UBadge :label="t('layout.brand')" color="primary" variant="soft" class="page-eyebrow" />
            <h1 class="text-2xl font-bold leading-tight text-highlighted sm:text-3xl xl:text-2xl">
              {{ bookingConfig.event.title }}
            </h1>
            <p class="page-description">
              {{ t('booking.seatGeneration', { count: seatCount, seatLabel: locale === 'zh' ? '座位' : `seat${seatCount === 1 ? '' : 's'}` }) }}
            </p>
          </div>
        </template>

        <div class="grid gap-3">
          <div
            v-for="detail in eventDetails"
            :key="detail.label"
            class="surface-panel rounded-lg p-4"
          >
            <div class="flex items-start gap-3">
              <div class="flex size-10 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
                <UIcon :name="detail.icon" class="size-5" />
              </div>
              <div class="min-w-0">
                <p class="text-xs font-semibold uppercase text-muted">
                  {{ detail.label }}
                </p>
                <p class="mt-1 text-sm font-semibold leading-6 text-highlighted break-words">
                  {{ detail.value }}
                </p>
              </div>
            </div>
          </div>
        </div>
      </UCard>

      <UCard
        id="booking-form"
        class="surface-card overflow-hidden rounded-lg"
        :ui="{ body: 'space-y-5 p-4 sm:space-y-6 sm:p-6' }"
      >
        <div class="space-y-1">
          <p class="text-sm font-semibold text-primary">
            {{ t('booking.bookNow') }}
          </p>
          <h2 class="text-xl font-semibold text-highlighted">
            {{ t('booking.ticketCategory') }}
          </h2>
        </div>

        <UForm :state="form" :validate="validateBooking" class="space-y-5 sm:space-y-6" @submit="bookTicket">
          <UFormField name="name" :label="t('booking.name')" required>
            <UInput v-model="form.name" size="xl" class="w-full" :placeholder="t('booking.namePlaceholder')" />
          </UFormField>

          <UFormField name="phone" :label="t('common.phoneNumber')" required>
            <UInput v-model="form.phone" size="xl" type="tel" class="w-full" :placeholder="t('booking.phonePlaceholder')" />
          </UFormField>

          <UFormField :label="t('booking.bookingMode')" name="bookingMode">
            <URadioGroup
              v-model="form.bookingMode"
              orientation="horizontal"
              variant="card"
              indicator="hidden"
              :items="bookingModeOptions"
              :ui="{
                fieldset: 'grid grid-cols-2 gap-3',
                item: 'min-h-14 rounded-lg border border-default bg-default p-3 transition-colors data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
              }"
            />
          </UFormField>

          <UFormField :label="quantityLabel" name="quantity">
            <UInputNumber v-model="form.quantity" size="xl" class="w-full" :min="1" :step="1" />
            <template #help>
              {{ t('booking.seatGeneration', { count: seatCount, seatLabel: locale === 'zh' ? '座位' : `seat${seatCount === 1 ? '' : 's'}` }) }}
            </template>
          </UFormField>

          <UFormField :label="t('booking.personInCharge')">
            <USelect
              v-model="selectedPersonInCharge"
              size="xl"
              class="w-full"
              :items="personInCharge"
              :disabled="!personInCharge.length"
            />
          </UFormField>

          <UFormField :label="t('booking.ticketCategory')" name="ticketType">
            <URadioGroup
              v-model="form.ticketType"
              orientation="horizontal"
              variant="card"
              indicator="hidden"
              :items="ticketCatalogOptions"
              :ui="{
                fieldset: 'grid grid-cols-2 gap-3',
                item: 'min-h-14 rounded-lg border border-default bg-default p-3 transition-colors data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
              }"
            />
          </UFormField>

          <div class="surface-panel rounded-lg px-4 py-4">
            <div class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between sm:gap-4">
              <span class="text-sm font-medium text-muted">{{ t('common.totalPrice') }}</span>
              <span class="text-2xl font-bold tabular-nums text-highlighted">{{ totalFormatted }}</span>
            </div>
          </div>

          <UButton
            id="getTicketBtn"
            type="submit"
            :label="t('booking.bookNow')"
            size="xl"
            class="min-h-12 w-full justify-center"
            :disabled="!selectedPersonInCharge || !selectedBookingMode || !selectedTicket"
            :loading="submittingBooking"
          />
        </UForm>
      </UCard>
    </div>
  </UContainer>
</template>
