import { createFileRoute, Link } from '@tanstack/react-router'
import { useEffect, useState } from 'react'
import { m } from '@/i18n/messages'
import { invoiceKind, invoiceStatus, emailKind } from '@/i18n/i18n-enum.gen'
import { useLocale, getLocale } from '@/i18n/config'
import { asI18n, type I18nNode } from '@pikku/react'
import { useQueryClient } from '@tanstack/react-query'
import {
  Alert,
  Badge,
  Button,
  Card,
  Container,
  Divider,
  Group,
  NumberInput,
  Paper,
  Select,
  SimpleGrid,
  Stack,
  Table,
  Tabs,
  Text,
  Textarea,
  TextInput,
  Title,
} from '@pikku/mantine/core'
import { usePikkuQuery, usePikkuMutation } from '@project/functions-sdk/pikku/api.gen'
import { FileUploader, type UploadedAsset } from '@project/components'
import { RoomsTab } from '../components/RoomsTab'
import { ParticipantsTab } from '../components/ParticipantsTab'
import { BookingStatusPanel } from '../components/BookingStatusPanel'
import { BookingActions } from '../components/BookingActions'
import { EventCard } from '../components/EventCard'
import type { BookingStatus } from '../lib/status'
import { fmtDate, toDateInputStr } from '../lib/dates'

type Patch = {
  eventName?: string
  startDate?: string
  endDate?: string
  halfHouse?: boolean
  onlineAd?: boolean
  expectedPersons?: number | null
  eventOutline?: string | null
  description?: string | null
  coverImageUrl?: string | null
  organiserWebsite?: string | null
  mealTimeBreakfast?: string | null
  mealTimeLunch?: string | null
  mealTimeDinner?: string | null
  arrivalTime?: string | null
  departureTime?: string | null
  dailyPlan?: string | null
  notes?: string | null
}

function AdminBookingDetailInner() {
  useLocale()
  const { bookingId } = Route.useParams()
  const qc = useQueryClient()
  const queryArgs = { bookingId }
  const { data, isLoading, error, isFetching } = usePikkuQuery('getAdminBookingDetail', queryArgs, {
    refetchOnMount: 'always',
  })

  const invalidate = async () => {
    await qc.invalidateQueries({ queryKey: ['getAdminBookingDetail', queryArgs] })
    await qc.invalidateQueries({ queryKey: ['getBookingAuditLog', queryArgs] })
    await qc.refetchQueries({ queryKey: ['getAdminBookingDetail', queryArgs], type: 'active' })
    return qc.refetchQueries({ queryKey: ['getBookingAuditLog', queryArgs], type: 'active' })
  }

  const updateMut = usePikkuMutation('updateAdminBooking', { onSuccess: invalidate })
  const assignMut = usePikkuMutation('assignRoom', { onSuccess: invalidate })
  const unassignMut = usePikkuMutation('unassignRoom', { onSuccess: invalidate })
  const createInvoiceMut = usePikkuMutation('createInvoice', { onSuccess: invalidate })
  const getUploadUrlMut = usePikkuMutation('getUploadUrl')
  const updateInvoiceMut = usePikkuMutation('updateInvoice', { onSuccess: invalidate })
  const recordPaymentMut = usePikkuMutation('recordInvoicePayment', { onSuccess: invalidate })
  const cancelInvoiceMut = usePikkuMutation('cancelInvoice', { onSuccess: invalidate })
  const updateOrgMut = usePikkuMutation('updateClient', { onSuccess: invalidate })
  const addParticipantMut = usePikkuMutation('addParticipant', { onSuccess: invalidate })
  const updateParticipantMut = usePikkuMutation('updateParticipant', { onSuccess: invalidate })
  const removeParticipantMut = usePikkuMutation('removeParticipant', { onSuccess: invalidate })
  const addAllergyMut = usePikkuMutation('addParticipantAllergy', { onSuccess: invalidate })
  const removeAllergyMut = usePikkuMutation('removeParticipantAllergy', { onSuccess: invalidate })

  const today = new Date().toISOString().slice(0, 10)
  const [invDraft, setInvDraft] = useState({
    kind: 'deposit' as 'deposit' | 'final',
    invoiceNumber: '',
    issuedOn: today,
    dueOn: '',
    amountCents: 0,
    pdfAssetKey: undefined as string | undefined,
    pdfName: '',
  })

  const [patch, setPatch] = useState<Patch>({})
  const [saved, setSaved] = useState(false)
  const [isInvoicePdfUploading, setIsInvoicePdfUploading] = useState(false)
  const [invoiceUploadError, setInvoiceUploadError] = useState(false)
  const [orgPatch, setOrgPatch] = useState<{
    name?: string
    contactEmail?: string | null
    contactPhone?: string | null
    billingAddress?: string | null
    website?: string | null
  }>({})
  const [orgSaved, setOrgSaved] = useState(false)

  useEffect(() => {
    if (data) setPatch({})
  }, [data?.booking.bookingId])

  if (isLoading) return <Container py="xl"><Text>{m.common__states__loading()}</Text></Container>
  if (error) return <Container py="xl"><Text c="red">{m.common__states__error()}</Text></Container>
  if (!data) return null

  const { booking, client, participants, rooms, invoices } = data

  const cur = <K extends keyof Patch>(key: K, fallback: Patch[K]): Patch[K] =>
    (key in patch ? patch[key] : fallback)

  const set = <K extends keyof Patch>(key: K, value: Patch[K]) => {
    setPatch((p) => ({ ...p, [key]: value }))
    setSaved(false)
  }

  const requestInvoicePdfUpload = async (file: File) =>
    getUploadUrlMut.mutateAsync({
      purpose: 'invoicePdf',
      bookingId,
      fileName: file.name,
      contentType: file.type || 'application/pdf',
      size: file.size,
    })

  const save = () => {
    if (Object.keys(patch).length === 0) return
    updateMut.mutate(
      { bookingId, patch },
      {
        onSuccess: () => {
          setPatch({})
          setSaved(true)
        },
      },
    )
  }

  const unassignedParticipants = participants.filter((p) => !p.roomId)
  const organizationForm = (
    <Paper withBorder radius="md" p="lg" data-testid="client-form">
      <Stack gap="md">
        <Title order={5}>{m.common__pages__admin_booking__client__title()}</Title>
        <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
          <TextInput
            data-testid="client-name"
            label={m.common__pages__admin_booking__client__name()}
            value={orgPatch.name ?? client.name ?? ''}
            onChange={(e) => {
              const v = e.currentTarget.value
              setOrgPatch((p) => ({ ...p, name: v }))
              setOrgSaved(false)
            }}
          />
          <TextInput
            data-testid="organisation-email"
            type="email"
            label={m.common__pages__admin_booking__client__contact_email()}
            value={
              orgPatch.contactEmail !== undefined
                ? orgPatch.contactEmail ?? ''
                : client.contactEmail ?? ''
            }
            onChange={(e) => {
              const v = e.currentTarget.value
              setOrgPatch((p) => ({ ...p, contactEmail: v || null }))
              setOrgSaved(false)
            }}
          />
          <TextInput
            data-testid="organisation-phone"
            label={m.common__pages__admin_booking__client__contact_phone()}
            value={
              orgPatch.contactPhone !== undefined
                ? orgPatch.contactPhone ?? ''
                : client.contactPhone ?? ''
            }
            onChange={(e) => {
              const v = e.currentTarget.value
              setOrgPatch((p) => ({ ...p, contactPhone: v || null }))
              setOrgSaved(false)
            }}
          />
          <TextInput
            data-testid="client-website"
            label={m.common__pages__admin_booking__client__website()}
            value={
              orgPatch.website !== undefined
                ? orgPatch.website ?? ''
                : client.website ?? ''
            }
            onChange={(e) => {
              const v = e.currentTarget.value
              setOrgPatch((p) => ({ ...p, website: v || null }))
              setOrgSaved(false)
            }}
          />
        </SimpleGrid>
        <Textarea
          data-testid="client-billing-address"
          label={m.common__pages__admin_booking__client__billing_address()}
          value={
            orgPatch.billingAddress !== undefined
              ? orgPatch.billingAddress ?? ''
              : client.billingAddress ?? ''
          }
          onChange={(e) => {
            const v = e.currentTarget.value
            setOrgPatch((p) => ({ ...p, billingAddress: v || null }))
            setOrgSaved(false)
          }}
          autosize
          minRows={2}
        />
        {updateOrgMut.error && (
          <Alert color="red">{m.common__pages__admin_booking__client__save_error()}</Alert>
        )}
        {orgSaved && (
          <Alert color="green">{m.common__pages__admin_booking__client__saved()}</Alert>
        )}
        <Group justify="flex-end">
          <Button
            data-testid="client-save"
            disabled={Object.keys(orgPatch).length === 0 || updateOrgMut.isPending}
            loading={updateOrgMut.isPending}
            onClick={() =>
              updateOrgMut.mutate(
                {
                  clientId: client.clientId,
                  patch: orgPatch,
                },
                {
                  onSuccess: () => {
                    setOrgPatch({})
                    setOrgSaved(true)
                  },
                },
              )
            }
          >
            {m.common__pages__admin_booking__client__save()}
          </Button>
        </Group>
      </Stack>
    </Paper>
  )

  return (
    <Container
      size="lg"
      py="xl"
      data-testid={!isFetching ? 'booking-detail-ready' : 'booking-detail-pending'}
      aria-busy={isFetching}
    >
      <Stack gap="lg">
        <Group align="flex-start" justify="space-between" wrap="nowrap">
          <Stack gap={4}>
            <Group gap="xs">
              <Button component={Link} to="/admin/bookings" size="xs" variant="subtle">
                {asI18n(`← ${m.common__pages__admin__bookings__title()}`)}
              </Button>
            </Group>
            <Title order={1}>
              {m.common__pages__admin_booking__title({ course: booking.eventName })}
            </Title>
            <Group gap={4}>
              <Text size="sm" c="dimmed">{asI18n(`${client.name}`)}</Text>
              {client.isStammgruppe ? (
                <Text size="sm" c="yellow.6" title={m.common__pages__admin_booking__client__regular_group()} style={{ cursor: 'default' }}>{asI18n('★')}</Text>
              ) : null}
            </Group>
          </Stack>

          <BookingActions
            booking={{
              bookingId: booking.bookingId,
              status: booking.status as BookingStatus,
              contractSentAt: booking.contractSentAt ?? null,
              contractResponse: booking.contractResponse ?? null,
            }}
            depositPaid={!!invoices.find((inv) => inv.kind === 'deposit')?.paidOn}
            onMutated={invalidate}
          />
        </Group>

        <BookingStatusPanel
          booking={{
            bookingId: booking.bookingId,
            status: booking.status as BookingStatus,
            startDate: booking.startDate,
            enquiryAt: booking.enquiryAt,
            reservedAt: booking.reservedAt ?? null,
            confirmedAt: booking.confirmedAt ?? null,
            endedAt: booking.endedAt ?? null,
            cancelledAt: booking.cancelledAt ?? null,
            contractSentAt: booking.contractSentAt ?? null,
            contractResponse: booking.contractResponse ?? null,
            contractResponseAt: booking.contractResponseAt ?? null,
            depositReminderSentAt: booking.depositReminderSentAt ?? null,
            flaggedAt: booking.flaggedAt ?? null,
            flagReason: booking.flagReason ?? null,
          }}
          invoices={invoices}
        />

        <Tabs defaultValue="details">
          <Tabs.List>
            <Tabs.Tab value="overview">{m.common__pages__admin_booking__tabs__overview()}</Tabs.Tab>
            <Tabs.Tab value="details">{m.common__pages__admin_booking__tabs__details()}</Tabs.Tab>
            <Tabs.Tab value="client">{m.common__pages__admin_booking__tabs__client()}</Tabs.Tab>
            <Tabs.Tab value="rooms">
              {asI18n(`${m.common__pages__admin_booking__tabs__rooms()} (${rooms.length})`)}
            </Tabs.Tab>
            <Tabs.Tab value="participants">
              {asI18n(`${m.common__pages__admin_booking__tabs__participants()} (${participants.length})`)}
            </Tabs.Tab>
            <Tabs.Tab value="invoices">
              {asI18n(`${m.common__pages__admin_booking__tabs__invoices()} (${invoices.length})`)}
            </Tabs.Tab>
            <Tabs.Tab value="audit">{asI18n(`${m.common__pages__admin_booking__tabs__audit()}`)}</Tabs.Tab>
          </Tabs.List>

          <Tabs.Panel value="overview" pt="md">
            <Stack gap="md">
              <EventCard
                eventName={booking.eventName}
                startDate={booking.startDate}
                endDate={booking.endDate}
                coverImageUrl={booking.coverImageUrl}
                eventOutline={booking.eventOutline}
                description={booking.description}
                organiserWebsite={booking.organiserWebsite}
              />
              <Paper withBorder radius="md" p="lg">
                <Stack gap="md">
                  <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__event_name()}</Text>
                      <Text size="sm">{asI18n(`${booking.eventName}`)}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__expected_persons()}</Text>
                      <Text size="sm">{asI18n(`${booking.expectedPersons ?? '—'}`)}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__start_date()}</Text>
                      <Text size="sm">{asI18n(`${fmtDate(booking.startDate, getLocale())}`)}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__end_date()}</Text>
                      <Text size="sm">{asI18n(`${fmtDate(booking.endDate, getLocale())}`)}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__half_house()}</Text>
                      <Text size="sm">{booking.halfHouse ? m.common__yes() : m.common__no()}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__online_ad()}</Text>
                      <Text size="sm">{booking.onlineAd ? m.common__yes() : m.common__no()}</Text>
                    </Stack>
                  </SimpleGrid>
                  {booking.eventOutline && (
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__event_outline()}</Text>
                      <Text size="sm">{asI18n(`${booking.eventOutline}`)}</Text>
                    </Stack>
                  )}
                  {booking.description && (
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__description()}</Text>
                      <Text size="sm" style={{ whiteSpace: 'pre-wrap' }}>{asI18n(`${booking.description}`)}</Text>
                    </Stack>
                  )}
                  {booking.organiserWebsite && (
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__organiser_website()}</Text>
                      <Text size="sm" component="a" href={booking.organiserWebsite} target="_blank" rel="noopener noreferrer">
                        {asI18n(`${booking.organiserWebsite}`)}
                      </Text>
                    </Stack>
                  )}
                </Stack>
              </Paper>

              <Paper withBorder radius="md" p="lg">
                <Stack gap="md">
                  <SimpleGrid cols={{ base: 2, sm: 3 }} spacing="md">
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__arrival_time()}</Text>
                      <Text size="sm">{asI18n(`${booking.arrivalTime ?? '—'}`)}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__departure_time()}</Text>
                      <Text size="sm">{asI18n(`${booking.departureTime ?? '—'}`)}</Text>
                    </Stack>
                    <div />
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__meal_time_breakfast()}</Text>
                      <Text size="sm">{asI18n(`${booking.mealTimeBreakfast ?? '—'}`)}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__meal_time_lunch()}</Text>
                      <Text size="sm">{asI18n(`${booking.mealTimeLunch ?? '—'}`)}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__meal_time_dinner()}</Text>
                      <Text size="sm">{asI18n(`${booking.mealTimeDinner ?? '—'}`)}</Text>
                    </Stack>
                  </SimpleGrid>
                  {booking.dailyPlan && (
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__daily_plan()}</Text>
                      <Text size="sm" style={{ whiteSpace: 'pre-wrap' }}>{asI18n(`${booking.dailyPlan}`)}</Text>
                    </Stack>
                  )}
                  {booking.notes && (
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__fields__notes()}</Text>
                      <Text size="sm" style={{ whiteSpace: 'pre-wrap' }}>{asI18n(`${booking.notes}`)}</Text>
                    </Stack>
                  )}
                </Stack>
              </Paper>

              <Paper withBorder radius="md" p="lg">
                <Stack gap="md">
                  <Text size="xs" c="dimmed" fw={600} tt="uppercase" style={{ letterSpacing: '0.08em' }}>
                    {m.common__pages__admin_booking__client__title()}
                  </Text>
                  <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__client__name()}</Text>
                      <Text size="sm">{asI18n(`${client.name}`)}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__client__contact_email()}</Text>
                      <Text size="sm">{asI18n(`${client.contactEmail ?? '—'}`)}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__client__contact_phone()}</Text>
                      <Text size="sm">{asI18n(`${client.contactPhone ?? '—'}`)}</Text>
                    </Stack>
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__client__website()}</Text>
                      {client.website
                        ? <Text size="sm" component="a" href={client.website} target="_blank" rel="noopener noreferrer">{asI18n(`${client.website}`)}</Text>
                        : <Text size="sm">{asI18n('—')}</Text>}
                    </Stack>
                  </SimpleGrid>
                  {client.billingAddress && (
                    <Stack gap={2}>
                      <Text size="xs" c="dimmed" fw={500}>{m.common__pages__admin_booking__client__billing_address()}</Text>
                      <Text size="sm" style={{ whiteSpace: 'pre-wrap' }}>{asI18n(`${client.billingAddress}`)}</Text>
                    </Stack>
                  )}
                </Stack>
              </Paper>

            </Stack>
          </Tabs.Panel>

          <Tabs.Panel value="details" pt="md">
            <Stack gap="md">
              <Paper withBorder radius="md" p="lg">
                <Stack gap="md">
                  <TextInput
                    data-testid="courseName"
                    label={m.common__pages__admin_booking__fields__event_name()}
                    value={cur('eventName', booking.eventName) ?? ''}
                    onChange={(e) => set('eventName', e.currentTarget.value)}
                  />
                  <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
                    <TextInput
                      data-testid="startDate"
                      type="date"
                      label={m.common__pages__admin_booking__fields__start_date()}
                      value={cur('startDate', toDateInputStr(booking.startDate)) ?? ''}
                      onChange={(e) => set('startDate', e.currentTarget.value)}
                    />
                    <TextInput
                      data-testid="endDate"
                      type="date"
                      label={m.common__pages__admin_booking__fields__end_date()}
                      value={cur('endDate', toDateInputStr(booking.endDate)) ?? ''}
                      onChange={(e) => set('endDate', e.currentTarget.value)}
                    />
                    <NumberInput
                      data-testid="expectedPersons"
                      label={m.common__pages__admin_booking__fields__expected_persons()}
                      value={cur('expectedPersons', booking.expectedPersons) ?? ''}
                      onChange={(v) => set('expectedPersons', v === '' ? null : Number(v))}
                      min={1}
                    />
                    <BooleanSelect
                      data-testid="halfHouse"
                      label={m.common__pages__admin_booking__fields__half_house()}
                      value={!!cur('halfHouse', booking.halfHouse === 1)}
                      onChange={(v) => set('halfHouse', v)}
                    />
                    <BooleanSelect
                      data-testid="onlineAd"
                      label={m.common__pages__admin_booking__fields__online_ad()}
                      value={!!cur('onlineAd', booking.onlineAd === 1)}
                      onChange={(v) => set('onlineAd', v)}
                    />
                  </SimpleGrid>
                  <TextInput
                    label={m.common__pages__admin_booking__fields__event_outline()}
                    value={cur('eventOutline', booking.eventOutline) ?? ''}
                    onChange={(e) => set('eventOutline', e.currentTarget.value || null)}
                  />
                  <Textarea
                    label={m.common__pages__admin_booking__fields__description()}
                    value={cur('description', booking.description) ?? ''}
                    onChange={(e) => set('description', e.currentTarget.value || null)}
                    autosize
                    minRows={4}
                  />
                  <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
                    <TextInput
                      label={m.common__pages__admin_booking__fields__cover_image_url()}
                      value={cur('coverImageUrl', booking.coverImageUrl) ?? ''}
                      onChange={(e) => set('coverImageUrl', e.currentTarget.value || null)}
                    />
                    <TextInput
                      label={m.common__pages__admin_booking__fields__organiser_website()}
                      value={cur('organiserWebsite', booking.organiserWebsite) ?? ''}
                      onChange={(e) => set('organiserWebsite', e.currentTarget.value || null)}
                    />
                  </SimpleGrid>
                </Stack>
              </Paper>

              <Paper withBorder radius="md" p="lg">
                <Stack gap="md">
                  <SimpleGrid cols={{ base: 1, sm: 3 }} spacing="md">
                    <TextInput
                      type="time"
                      label={m.common__pages__admin_booking__fields__arrival_time()}
                      value={cur('arrivalTime', booking.arrivalTime) ?? ''}
                      onChange={(e) => set('arrivalTime', e.currentTarget.value || null)}
                    />
                    <TextInput
                      type="time"
                      label={m.common__pages__admin_booking__fields__departure_time()}
                      value={cur('departureTime', booking.departureTime) ?? ''}
                      onChange={(e) => set('departureTime', e.currentTarget.value || null)}
                    />
                    <div />
                    <TextInput
                      type="time"
                      label={m.common__pages__admin_booking__fields__meal_time_breakfast()}
                      value={cur('mealTimeBreakfast', booking.mealTimeBreakfast) ?? ''}
                      onChange={(e) => set('mealTimeBreakfast', e.currentTarget.value || null)}
                    />
                    <TextInput
                      type="time"
                      label={m.common__pages__admin_booking__fields__meal_time_lunch()}
                      value={cur('mealTimeLunch', booking.mealTimeLunch) ?? ''}
                      onChange={(e) => set('mealTimeLunch', e.currentTarget.value || null)}
                    />
                    <TextInput
                      type="time"
                      label={m.common__pages__admin_booking__fields__meal_time_dinner()}
                      value={cur('mealTimeDinner', booking.mealTimeDinner) ?? ''}
                      onChange={(e) => set('mealTimeDinner', e.currentTarget.value || null)}
                    />
                  </SimpleGrid>
                  <Textarea
                    label={m.common__pages__admin_booking__fields__daily_plan()}
                    value={cur('dailyPlan', booking.dailyPlan) ?? ''}
                    onChange={(e) => set('dailyPlan', e.currentTarget.value || null)}
                    autosize
                    minRows={2}
                  />
                  <Textarea
                    label={m.common__pages__admin_booking__fields__notes()}
                    value={cur('notes', booking.notes) ?? ''}
                    onChange={(e) => set('notes', e.currentTarget.value || null)}
                    autosize
                    minRows={3}
                  />
                </Stack>
              </Paper>

              {updateMut.error && (
                <Alert color="red">{m.common__pages__admin_booking__save_error()}</Alert>
              )}
              {saved && <Alert color="green">{m.common__pages__admin_booking__saved()}</Alert>}

              <Group justify="flex-end">
                <Button
                  data-testid="save-booking"
                  onClick={save}
                  disabled={Object.keys(patch).length === 0 || updateMut.isPending}
                  loading={updateMut.isPending}
                >
                  {m.common__pages__admin_booking__save()}
                </Button>
              </Group>

              {organizationForm}
            </Stack>
          </Tabs.Panel>

          <Tabs.Panel value="client" pt="md">
            {organizationForm}
          </Tabs.Panel>

          <Tabs.Panel value="rooms" pt="md">
            <Stack gap="md">
              <Title order={5}>{m.common__pages__admin_booking__rooms__title()}</Title>
              <RoomsTab
                rooms={rooms}
                participants={participants}
                bookingId={bookingId}
                assign={(args) => assignMut.mutate(args)}
                unassign={(args) => unassignMut.mutate(args)}
              />
            </Stack>
          </Tabs.Panel>

          <Tabs.Panel value="participants" pt="md">
            <ParticipantsTab
              participants={participants}
              bookingId={bookingId}
              addParticipant={(args, opts) => addParticipantMut.mutate(args, opts)}
              updateParticipant={(args) => updateParticipantMut.mutate(args)}
              removeParticipant={(args) => removeParticipantMut.mutate(args)}
              addAllergy={(args, opts) => addAllergyMut.mutate(args, opts)}
              removeAllergy={(args) => removeAllergyMut.mutate(args)}
              isAddingParticipant={addParticipantMut.isPending}
            />
          </Tabs.Panel>

          <Tabs.Panel value="invoices" pt="md">
            <Stack gap="md">
              <Paper withBorder radius="md" p={0}>
                {invoices.length === 0 ? (
                  <Text size="sm" c="dimmed" ta="center" p="xl">
                    {m.common__pages__admin_booking__invoices__empty()}
                  </Text>
                ) : (
                  <Table>
                    <Table.Thead>
                      <Table.Tr>
                        <Table.Th>{m.common__pages__admin_booking__invoices__cols__number()}</Table.Th>
                        <Table.Th>{m.common__pages__admin_booking__invoices__cols__kind()}</Table.Th>
                        <Table.Th>{m.common__pages__admin_booking__invoices__cols__amount()}</Table.Th>
                        <Table.Th>{m.common__pages__admin_booking__invoices__cols__issued()}</Table.Th>
                        <Table.Th>{m.common__pages__admin_booking__invoices__cols__due()}</Table.Th>
                        <Table.Th>{m.common__pages__admin_booking__invoices__cols__paid()}</Table.Th>
                        <Table.Th>{m.common__pages__admin_booking__invoices__cols__status()}</Table.Th>
                        <Table.Th>{m.common__pages__admin_booking__invoices__cols__action()}</Table.Th>
                      </Table.Tr>
                    </Table.Thead>
                    <Table.Tbody>
                      {invoices.map((inv) => (
                        <Table.Tr key={inv.invoiceId}>
                          <Table.Td>
                            {inv.pdfUrl ? (
                              <Button
                                component="a"
                                href={inv.pdfUrl}
                                target="_blank"
                                rel="noreferrer"
                                download={`${inv.invoiceNumber}.pdf`}
                                variant="subtle"
                                px={0}
                                data-testid={`invoice-pdf-link-${inv.invoiceNumber}`}
                              >
                                {asI18n(`${inv.invoiceNumber}`)}
                              </Button>
                            ) : (
                              inv.invoiceNumber
                            )}
                          </Table.Td>
                          <Table.Td>
                            {invoiceKind[inv.kind as keyof typeof invoiceKind]()}
                          </Table.Td>
                          <Table.Td>{(inv.amountCents / 100).toFixed(2)} €</Table.Td>
                          <Table.Td>{fmtDate(inv.issuedOn, getLocale())}</Table.Td>
                          <Table.Td>{inv.dueOn ? fmtDate(inv.dueOn, getLocale()) : '—'}</Table.Td>
                          <Table.Td>{inv.paidOn ? fmtDate(inv.paidOn, getLocale()) : '—'}</Table.Td>
                          <Table.Td>
                            <Badge
                              color={inv.status === 'paid' ? 'green' : inv.status === 'cancelled' ? 'gray' : 'orange'}
                              variant="light"
                            >
                              {invoiceStatus[inv.status as keyof typeof invoiceStatus]()}
                            </Badge>
                          </Table.Td>
                          <Table.Td>
                            <Group gap="xs">
                              <FileUploader
                                compact
                                inputTestId={`invoice-pdf-upload-${inv.invoiceNumber}`}
                                accept={['application/pdf']}
                                idleLabel={(inv.pdfUrl
                                    ? m.common__pages__admin_booking__invoices__replace_pdf
                                    : m.common__pages__admin_booking__invoices__upload_pdf)()}
                                activeLabel={m.common__components__file_uploader__drop_now()}
                                uploadingLabel={m.common__components__file_uploader__uploading()}
                                uploadedLabel={m.common__components__file_uploader__uploaded()}
                                browseLabel={m.common__components__file_uploader__browse()}
                                requestUpload={requestInvoicePdfUpload}
                                onUploadStateChange={setIsInvoicePdfUploading}
                                onUploaded={(asset) => {
                                  setInvoiceUploadError(false)
                                  updateInvoiceMut.mutate({
                                    invoiceId: inv.invoiceId,
                                    patch: { pdfAssetKey: asset.contentKey },
                                  })
                                }}
                                onError={() => setInvoiceUploadError(true)}
                              />
                              {inv.pdfUrl && (
                                <Button
                                  component="a"
                                  href={inv.pdfUrl}
                                  target="_blank"
                                  rel="noreferrer"
                                  download={`${inv.invoiceNumber}.pdf`}
                                  size="xs"
                                  variant="light"
                                  data-testid={`invoice-pdf-open-${inv.invoiceNumber}`}
                                >
                                  {m.common__pages__admin_booking__invoices__open_pdf()}
                                </Button>
                              )}
                            {inv.status === 'outstanding' && (
                              <>
                                <Button
                                  size="xs"
                                  variant="light"
                                  data-testid={`invoice-mark-paid-${inv.invoiceNumber}`}
                                  onClick={() => {
                                    const paidOn = window.prompt(
                                      m.common__pages__admin_booking__invoices__paid_on_prompt(),
                                      today,
                                    )
                                    if (paidOn) {
                                      recordPaymentMut.mutate({ invoiceId: inv.invoiceId, paidOn })
                                    }
                                  }}
                                >
                                  {m.common__pages__admin_booking__invoices__mark_paid()}
                                </Button>
                                <Button
                                  size="xs"
                                  variant="subtle"
                                  color="red"
                                  data-testid={`invoice-cancel-${inv.invoiceNumber}`}
                                  onClick={() => {
                                    if (window.confirm(m.common__pages__admin_booking__invoices__cancel_confirm())) {
                                      cancelInvoiceMut.mutate({ invoiceId: inv.invoiceId })
                                    }
                                  }}
                                >
                                  {m.common__pages__admin_booking__invoices__cancel()}
                                </Button>
                              </>
                            )}
                            </Group>
                          </Table.Td>
                        </Table.Tr>
                      ))}
                    </Table.Tbody>
                  </Table>
                )}
              </Paper>

              <Paper withBorder radius="md" p="md" data-testid="invoice-create-form">
                <Stack gap="sm">
                  <Title order={4}>{m.common__pages__admin_booking__invoices__create_title()}</Title>
                  <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
                    <Select
                      data-testid="invoice-kind"
                      label={m.common__pages__admin_booking__invoices__cols__kind()}
                      value={invDraft.kind}
                      onChange={(v) =>
                        setInvDraft((d) => ({ ...d, kind: (v as 'deposit' | 'final') ?? 'deposit' }))
                      }
                      data={[
                        { value: 'deposit', label: invoiceKind.deposit() },
                        { value: 'final', label: invoiceKind.final() },
                      ]}
                    />
                    <TextInput
                      data-testid="invoice-number"
                      label={m.common__pages__admin_booking__invoices__cols__number()}
                      value={invDraft.invoiceNumber}
                      onChange={(e) => {
                        const v = e.currentTarget.value
                        setInvDraft((d) => ({ ...d, invoiceNumber: v }))
                      }}
                    />
                    <TextInput
                      data-testid="invoice-issued-on"
                      type="date"
                      label={m.common__pages__admin_booking__invoices__cols__issued()}
                      value={invDraft.issuedOn}
                      onChange={(e) => {
                        const v = e.currentTarget.value
                        setInvDraft((d) => ({ ...d, issuedOn: v }))
                      }}
                    />
                    <TextInput
                      data-testid="invoice-due-on"
                      type="date"
                      label={m.common__pages__admin_booking__invoices__cols__due()}
                      value={invDraft.dueOn}
                      onChange={(e) => {
                        const v = e.currentTarget.value
                        setInvDraft((d) => ({ ...d, dueOn: v }))
                      }}
                    />
                    <NumberInput
                      data-testid="invoice-amount"
                      label={m.common__pages__admin_booking__invoices__cols__amount()}
                      value={invDraft.amountCents / 100}
                      onChange={(v) =>
                        setInvDraft((d) => ({
                          ...d,
                          amountCents: Math.round((typeof v === 'number' ? v : Number(v) || 0) * 100),
                        }))
                      }
                      min={0}
                      decimalScale={2}
                      fixedDecimalScale
                      step={10}
                      suffix={asI18n(' €')}
                    />
                    <FileUploader
                      inputTestId="invoice-pdf"
                      accept={['application/pdf']}
                      idleLabel={m.common__pages__admin_booking__invoices__attach_pdf()}
                      activeLabel={m.common__components__file_uploader__drop_now()}
                      uploadingLabel={m.common__components__file_uploader__uploading()}
                      uploadedLabel={m.common__pages__admin_booking__invoices__pdf_selected()}
                      browseLabel={m.common__components__file_uploader__browse()}
                      hint={m.common__components__file_uploader__pdf_only()}
                      requestUpload={requestInvoicePdfUpload}
                      onUploadStateChange={setIsInvoicePdfUploading}
                      onUploaded={(asset: UploadedAsset) => {
                        setInvoiceUploadError(false)
                        setInvDraft((d) => ({
                          ...d,
                          pdfAssetKey: asset.contentKey,
                          pdfName: asset.fileName,
                        }))
                      }}
                      onError={() => setInvoiceUploadError(true)}
                    />
                  </SimpleGrid>
                  {createInvoiceMut.error && (
                    <Alert color="red" variant="light">
                      {m.common__pages__admin_booking__invoices__errors__create()}
                    </Alert>
                  )}
                  {(updateInvoiceMut.error || invoiceUploadError) && (
                    <Alert color="red" variant="light">
                      {m.common__pages__admin_booking__invoices__errors__upload_pdf()}
                    </Alert>
                  )}
                  <Group>
                    <Button
                      onClick={() => {
                        if (!invDraft.invoiceNumber || invDraft.amountCents <= 0) return
                        createInvoiceMut.mutate(
                          {
                            bookingId,
                            kind: invDraft.kind,
                            invoiceNumber: invDraft.invoiceNumber,
                            issuedOn: invDraft.issuedOn,
                            dueOn: invDraft.dueOn || undefined,
                            amountCents: invDraft.amountCents,
                            pdfAssetKey: invDraft.pdfAssetKey,
                          },
                          {
                            onSuccess: () =>
                              setInvDraft({
                                kind: 'deposit',
                                invoiceNumber: '',
                                issuedOn: today,
                                dueOn: '',
                                amountCents: 0,
                                pdfAssetKey: undefined,
                                pdfName: '',
                              }),
                          },
                        )
                      }}
                      disabled={
                        !invDraft.invoiceNumber ||
                        invDraft.amountCents <= 0 ||
                        createInvoiceMut.isPending ||
                        isInvoicePdfUploading
                      }
                    >
                      {m.common__pages__admin_booking__invoices__create()}
                    </Button>
                  </Group>
                </Stack>
              </Paper>
            </Stack>
          </Tabs.Panel>

          <Tabs.Panel value="audit" pt="md">
            <AuditTab bookingId={bookingId} />
          </Tabs.Panel>
        </Tabs>
      </Stack>
    </Container>
  )
}

function BooleanSelect({
  label,
  value,
  onChange,
  'data-testid': testId,
}: {
  label: I18nNode
  value: boolean
  onChange: (v: boolean) => void
  'data-testid'?: string
}) {
  useLocale()
  return (
    <Select
      label={label}
      data-testid={testId}
      value={value ? 'true' : 'false'}
      onChange={(v) => onChange(v === 'true')}
      data={[
        { value: 'true', label: m.common__yes() },
        { value: 'false', label: m.common__no() },
      ]}
      allowDeselect={false}
    />
  )
}

/** Audit rows store the email method name (camelCase); the enum labels are keyed by the snake_case enum value. */
function emailKindLabel(value: string): string {
  const key = value.replace(/([A-Z])/g, '_$1').toLowerCase()
  return key in emailKind ? emailKind[key as keyof typeof emailKind]() : value
}

function AuditTab({ bookingId }: { bookingId: string }) {
  useLocale()
  const { data, isLoading } = usePikkuQuery('getBookingAuditLog', { bookingId })

  if (isLoading) return <Text size="sm" c="dimmed">{m.common__states__loading()}</Text>

  const entries = data?.entries ?? []

  if (entries.length === 0) {
    return (
      <Paper withBorder radius="md" p="xl">
        <Text size="sm" c="dimmed" ta="center">{m.common__pages__admin_booking__audit__empty()}</Text>
      </Paper>
    )
  }

  const fmt = new Intl.DateTimeFormat(getLocale().startsWith('de') ? 'de-DE' : 'en-GB', {
    day: 'numeric', month: 'short', year: 'numeric',
    hour: '2-digit', minute: '2-digit',
  })

  return (
    <Paper withBorder radius="md" p={0}>
      <Table>
        <Table.Thead>
          <Table.Tr>
            <Table.Th>{m.common__pages__admin_booking__audit__cols__when()}</Table.Th>
            <Table.Th>{m.common__pages__admin_booking__audit__cols__who()}</Table.Th>
            <Table.Th>{m.common__pages__admin_booking__audit__cols__change()}</Table.Th>
          </Table.Tr>
        </Table.Thead>
        <Table.Tbody>
          {entries.map((e) => (
            <Table.Tr key={e.auditId}>
              <Table.Td>
                <Text size="sm" style={{ whiteSpace: 'nowrap' }}>{asI18n(`${fmt.format(e.at)}`)}</Text>
              </Table.Td>
              <Table.Td>
                <Text size="sm">{asI18n(`${e.userName ?? m.common__pages__admin_booking__audit__system()}`)}</Text>
                {e.userEmail && <Text size="xs" c="dimmed">{asI18n(`${e.userEmail}`)}</Text>}
              </Table.Td>
              <Table.Td>
                {e.field ? (
                  <Text size="sm">
                    <Text span fw={500}>{asI18n(`${e.field}`)}</Text>
                    {e.beforeValue != null && (
                      <> {e.beforeValue} → </>
                    )}
                    {e.afterValue != null && (
                      <Text span fw={500}>{asI18n(`${e.afterValue}`)}</Text>
                    )}
                  </Text>
                ) : e.action === 'email_sent' ? (
                  <Text size="sm">
                    <Text span fw={500}>{asI18n(`${m.common__pages__admin_booking__audit__email_sent()}`)}</Text>
                    {e.afterValue ? asI18n(` — ${emailKindLabel(e.afterValue)}`) : null}
                  </Text>
                ) : (
                  <Text size="sm" c="dimmed">{asI18n(`${e.action}`)}</Text>
                )}
              </Table.Td>
            </Table.Tr>
          ))}
        </Table.Tbody>
      </Table>
    </Paper>
  )
}

export const Route = createFileRoute('/admin/bookings/$bookingId')({
  component: AdminBookingDetailInner,
})
