'use client'
import { BASE } from '@/lib/basepath'

import { useState, useEffect, useCallback, useMemo } from 'react'
import { useCart } from '@/context/CartContext'
import type { DeliveryQuote } from '@/lib/delivery'
import { fmt } from '@/lib/format'
import DeliveryDatePicker from './DeliveryDatePicker'
import type { DeliverySelection } from './DeliveryDatePicker'
import { getPickupSlots } from '@/lib/slots'
import PaymentForm from './PaymentForm'
import type { CheckoutPayload } from './PaymentForm'
import ColorPicker from './ColorPicker'
import CalendarPicker from './CalendarPicker'
import type { CartEntry } from '@/context/CartContext'

/** Syncs a string state value to localStorage. Hydrates after mount. */
function useStoredString(key: string, initial: string): [string, (v: string) => void] {
  const [value, setValue] = useState(initial)
  useEffect(() => {
    try {
      const saved = localStorage.getItem(key)
      if (saved !== null) setValue(saved)
    } catch {}
  }, [key])
  const set = useCallback((v: string) => {
    setValue(v)
    try { localStorage.setItem(key, v) } catch {}
  }, [key])
  return [value, set]
}

function maxColorsFor(name: string): number | null {
  const n = name.toLowerCase()
  if (/arch|column/.test(n))                             return 4
  if (/\b11["''″]|\b11[- ]?inch/.test(n))               return 1
  if (/number.{0,10}sculpt|sculpt.{0,10}number/.test(n)) return 4
  if (/ultimate/.test(n))                                return 4
  return null
}

type Step = 'cart' | 'delivery' | 'info' | 'payment'

const STEP_TITLES: Record<Step, string> = {
  cart:     'Your Order',
  delivery: 'Delivery',
  info:     'Your Info',
  payment:  'Secure Checkout',
}

const STEP_ORDER: Step[] = ['cart', 'delivery', 'info', 'payment']

export default function CartDrawer() {
  const { entries, drawerOpen, closeDrawer, removeEntry, updateQuantity, clearCart, totalItems } = useCart()

  useEffect(() => {
    if (!drawerOpen) return
    const prev = document.body.style.overflow
    document.body.style.overflow = 'hidden'
    return () => { document.body.style.overflow = prev }
  }, [drawerOpen])

  const [editingEntry, setEditingEntry] = useState<CartEntry | null>(null)

  const [step,            setStep]           = useState<Step>('cart')
  const [orderId,         setOrderId]        = useState<string | null>(null)
  const [shortRef,        setShortRef]       = useState<string | null>(null)
  const [fulfillmentType, setFulfillmentType] = useState<'delivery' | 'pickup'>('pickup')

  // If any item requires delivery, force delivery mode and suppress pickup option
  const cartRequiresDelivery = useMemo(
    () => entries.some((e) => e.product.requiresDelivery),
    [entries]
  )
  // Effective fulfillment type — pickup blocked when any item requires delivery
  const effectiveFulfillment = cartRequiresDelivery ? 'delivery' : fulfillmentType

  // Merged delivery rate override: highest base + highest perMile across requires-delivery items
  const deliveryRateOverride = useMemo(() => {
    const overrideItems = entries.filter(
      (e) => e.product.requiresDelivery &&
             (e.product.deliveryBaseOverride != null || e.product.deliveryPerMileOverride != null)
    )
    if (!overrideItems.length) return undefined
    const base    = Math.max(...overrideItems.map((e) => e.product.deliveryBaseOverride ?? 0))
    const perMile = Math.max(...overrideItems.map((e) => e.product.deliveryPerMileOverride ?? 0))
    return { base, perMile }
  }, [entries])

  // Delivery step — persisted
  const [street,  setStreet] = useStoredString('bpb_street', '')
  const [city,    setCity]   = useStoredString('bpb_city',   '')
  const [state,   setState]  = useStoredString('bpb_state',  'CT')
  const [zip,     setZip]    = useStoredString('bpb_zip',    '')

  // Delivery step — ephemeral (quote resets on address change anyway)
  const [quote,             setQuote]             = useState<DeliveryQuote | null>(null)
  const [quoteErr,          setQuoteErr]          = useState('')
  const [quoting,           setQuoting]           = useState(false)
  const [deliverySlot,      setDeliverySlot]      = useState<DeliverySelection | null>(null)
  const [deliveryInstructions, setDeliveryInstructions] = useState('')

  // Pickup step — ephemeral
  const [pickupDate, setPickupDate] = useState('')
  const [pickupSlot, setPickupSlot] = useState<{ date: string; slotISO: string; label: string } | null>(null)

  // Info step — persisted
  const [custFirst, setCustFirst] = useStoredString('bpb_first', '')
  const [custLast,  setCustLast]  = useStoredString('bpb_last',  '')
  const [custEmail, setCustEmail] = useStoredString('bpb_email', '')
  const [custPhone, setCustPhone] = useStoredString('bpb_phone', '')

  // Idempotency key — persisted across component remounts and page refreshes so
  // that a retry after a network error always uses the same key, preventing
  // double charges even if the user closes and reopens the cart drawer.
  // Cleared only on confirmed payment success.
  const [checkoutKey, setCheckoutKey] = useStoredString('bpb_checkout_key', '')

  const [infoErrors, setInfoErrors] = useState<{ firstName?: string; lastName?: string; email?: string; phone?: string; balloon?: string }>({})
  const [balloonAgreement, setBalloonAgreement] = useState(false)

  const isValidEmail = (v: string) =>
    /^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$/.test(v.trim())
  const isValidPhone = (v: string) => v.replace(/\D/g, '').length === 10

  const validateAndContinue = () => {
    const errors: typeof infoErrors = {}
    if (!custFirst.trim())         errors.firstName = 'First name is required'
    if (!custLast.trim())          errors.lastName  = 'Last name is required'
    if (!isValidEmail(custEmail))  errors.email     = 'Enter a valid email address'
    if (!isValidPhone(custPhone))  errors.phone     = 'Enter a valid phone number'
    setInfoErrors(errors)
    if (!balloonAgreement) errors.balloon = 'Please confirm the balloon use agreement to continue'
    if (Object.keys(errors).length === 0) {
      // Generate a fresh idempotency key for this checkout attempt if we don't
      // already have one. An existing key means we're retrying after a failure —
      // keep it so the server can detect idempotency replay and avoid double charge.
      if (!checkoutKey) setCheckoutKey(crypto.randomUUID())
      setStep('payment')
    }
  }

  const fullAddress  = [street, city, state, zip].filter(Boolean).join(', ')
  const canQuote     = street.trim() && city.trim()
  const pickupSlots  = useMemo(() => getPickupSlots(pickupDate), [pickupDate])
  const todayStr     = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString().slice(0, 10)
  const maxDateStr   = new Date(Date.now() + 90 * 86400_000).toISOString().slice(0, 10)

  // Unit price — uses selected variation price if set, otherwise product default
  const entryUnitPrice = useCallback((entry: (typeof entries)[number]) => {
    const base = entry.selectedVariationId
      ? (entry.product.variations.find((v) => v.id === entry.selectedVariationId)?.priceCents ?? (entry.product.price ?? 0))
      : (entry.product.price ?? 0)
    const modDelta = Object.entries(entry.modifierChoices).reduce((sum, [listId, optIds]) => {
      const ml = entry.product.modifiers?.find((m) => m.id === listId)
      if (!ml) return sum
      return sum + optIds.reduce((s, optId) => {
        const opt = ml.options.find((o) => o.id === optId)
        return s + (opt?.priceDelta ?? 0)
      }, 0)
    }, 0)
    return base + modDelta
  }, [])

  // Pre-compute pickup disabled dates (closed days) for the next 90 days
  const pickupDisabledDates = useMemo(() => {
    const disabled = new Set<string>()
    const base = Date.now() + 24 * 60 * 60 * 1000  // start from tomorrow
    for (let i = 0; i < 90; i++) {
      const d = new Date(base + i * 86400_000).toISOString().slice(0, 10)
      if (getPickupSlots(d).length === 0) disabled.add(d)
    }
    return disabled
  }, [])

  const CT_TAX_RATE   = 0.0635
  const subtotal      = entries.reduce((sum, e) => sum + entryUnitPrice(e) * e.quantity, 0)
  const deliveryTotal = effectiveFulfillment === 'delivery' ? (quote?.totalCents ?? 0) : 0
  const taxCents      = Math.round(subtotal * CT_TAX_RATE)
  const grandTotal    = subtotal + deliveryTotal + taxCents

  // Build the payload sent to /api/checkout (recomputed only when dependencies change)
  const checkoutPayload = useMemo<CheckoutPayload>(() => ({
    lineItems: entries.map((e) => ({
      name:          e.product.name,
      quantity:      e.quantity,
      priceCents:    entryUnitPrice(e),
      catalogItemId: e.selectedVariationId ?? e.product.id,
      colors:        e.selectedColors.length ? e.selectedColors : undefined,
      note:          e.notes || undefined,
      modifiers:     Object.entries(e.modifierChoices).flatMap(([listId, optIds]) => {
        const ml = e.product.modifiers.find((m) => m.id === listId)
        if (!ml) return []
        return optIds.map((optId) => ({
          catalogObjectId: optId,
          name: ml.options.find((o) => o.id === optId)?.name ?? optId,
        }))
      }),
    })),
    selectedColors:  entries.flatMap((e) => e.selectedColors),
    deliverySlotISO: effectiveFulfillment === 'delivery' ? deliverySlot?.slotISO : undefined,
    driveMinutes:    effectiveFulfillment === 'delivery' ? deliverySlot?.driveMinutes : undefined,
    deliveryAddress: effectiveFulfillment === 'delivery' ? (fullAddress || undefined) : undefined,
    deliveryTier:    effectiveFulfillment === 'delivery' ? quote?.tier : undefined,
    deliveryNotes:   effectiveFulfillment === 'delivery' && deliveryInstructions ? deliveryInstructions : undefined,
    deliveryCents:   effectiveFulfillment === 'delivery' ? (quote?.totalCents ?? 0) : undefined,
    pickupSlotISO:   effectiveFulfillment === 'pickup' ? pickupSlot?.slotISO : undefined,
    customerFirstName: custFirst,
    customerLastName:  custLast,
    customerEmail:     custEmail,
    customerPhone:     custPhone,
    grandTotal,
    idempotencyKey:    checkoutKey || undefined,
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }), [entries, effectiveFulfillment, deliverySlot, pickupSlot, fullAddress, quote, deliveryInstructions, custFirst, custLast, custEmail, custPhone, grandTotal, entryUnitPrice])

  const handleSuccess = (id: string, ref: string) => {
    setOrderId(id)
    setShortRef(ref)
    clearCart()
    setCheckoutKey('')   // clear so the next order gets a fresh idempotency key
    setStep('cart')
    setQuote(null)
    setDeliverySlot(null)
  }

  const resetQuote = () => { setQuote(null); setQuoteErr(''); setDeliverySlot(null) }

  const goBack = () => {
    const i = STEP_ORDER.indexOf(step)
    if (i > 0) setStep(STEP_ORDER[i - 1])
  }

  const handleClose = () => {
    setBalloonAgreement(false)
    closeDrawer()
  }

  const getQuote = async () => {
    setQuoting(true)
    setQuoteErr('')
    setQuote(null)
    try {
      const res  = await fetch(BASE + '/api/delivery-quote', {
        method:  'POST',
        headers: { 'Content-Type': 'application/json' },
        body:    JSON.stringify({
          address:      fullAddress,
          itemNames:    entries.map((e) => e.product.name),
          rateOverride: deliveryRateOverride,
        }),
      })
      const data = await res.json()
      if (!res.ok) { setQuoteErr(data.error ?? 'Could not calculate delivery.'); return }
      setQuote(data)
    } catch {
      setQuoteErr('Network error — please try again.')
    } finally {
      setQuoting(false)
    }
  }

  // ── Step content ───────────────────────────────────────────────────────────

  const cartBody = (
    <>
      {entries.length === 0 ? (
        <p className="has-text-grey has-text-centered" style={{ marginTop: '3rem' }}>
          Your order is empty.<br />Pick something from the shop!
        </p>
      ) : (
        entries.map((entry) => (
          <div key={entry.cartId} style={{ borderBottom: '1px solid #e6dfc8', paddingBottom: '0.75rem', marginBottom: '0.75rem' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
              <strong style={{ fontSize: '0.95rem' }}>{entry.product.name}</strong>
              <div style={{ display: 'flex', gap: '6px', alignItems: 'center' }}>
                <button
                  onClick={() => setEditingEntry(entry)}
                  aria-label="Edit"
                  style={{ background: 'none', border: 'none', color: '#11b3be', cursor: 'pointer', fontSize: '0.75rem', lineHeight: 1, padding: '2px 4px' }}
                >Edit</button>
                <button
                  onClick={() => removeEntry(entry.cartId)}
                  aria-label="Remove"
                  style={{ background: 'none', border: 'none', color: '#aaa', cursor: 'pointer', fontSize: '1.1rem', lineHeight: 1 }}
                >×</button>
              </div>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: '6px', marginTop: '0.35rem' }}>
              <button
                onClick={() => updateQuantity(entry.cartId, entry.quantity - 1)}
                style={{ width: '24px', height: '24px', borderRadius: '50%', border: '1px solid #ccc', background: '#f5f5f5', cursor: 'pointer', fontSize: '1rem', lineHeight: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
              >−</button>
              <span style={{ fontSize: '0.85rem', minWidth: '18px', textAlign: 'center' }}>{entry.quantity}</span>
              <button
                onClick={() => updateQuantity(entry.cartId, entry.quantity + 1)}
                style={{ width: '24px', height: '24px', borderRadius: '50%', border: '1px solid #ccc', background: '#f5f5f5', cursor: 'pointer', fontSize: '1rem', lineHeight: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
              >+</button>
              {entry.product.price && (
                <span style={{ fontSize: '0.82rem', color: '#666', marginLeft: '4px' }}>{fmt(entryUnitPrice(entry) * entry.quantity)}</span>
              )}
            </div>
            {entry.selectedColors.length > 0 && (
              <div style={{ fontSize: '0.8rem', color: '#555', marginTop: '0.2rem' }}>
                Colors: {entry.selectedColors.join(', ')}
              </div>
            )}
            {Object.entries(entry.modifierChoices).map(([listId, optIds]) => {
              if (!optIds.length) return null
              const ml = entry.product.modifiers?.find((m) => m.id === listId)
              if (!ml) return null
              const names = optIds.map((id) => ml.options.find((o) => o.id === id)?.name ?? id)
              return (
                <div key={listId} style={{ fontSize: '0.8rem', color: '#555', marginTop: '0.2rem' }}>
                  {ml.name}: {names.join(', ')}
                </div>
              )
            })}
            {entry.notes && (
              <div style={{ fontSize: '0.8rem', color: '#888', marginTop: '0.2rem', fontStyle: 'italic' }}>
                &ldquo;{entry.notes}&rdquo;
              </div>
            )}
          </div>
        ))
      )}
    </>
  )

  const cartFooter = entries.length > 0 && (
    <>
      {subtotal > 0 && (
        <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.85rem', marginBottom: '0.75rem' }}>
          <span>Items subtotal</span>
          <span>{fmt(subtotal)}</span>
        </div>
      )}

      {/* Fulfillment toggle */}
      {cartRequiresDelivery ? (
        <p style={{ fontSize: '0.8rem', color: '#555', marginBottom: '0.75rem', background: '#f5f5f5', padding: '7px 10px', borderRadius: 6 }}>
          🚗 One or more items require delivery &amp; setup — pickup is not available for this order.
        </p>
      ) : (
        <div style={{ display: 'flex', gap: '6px', marginBottom: '0.75rem' }}>
          {(['delivery', 'pickup'] as const).map((type) => (
            <button
              key={type}
              type="button"
              onClick={() => { setFulfillmentType(type); setPickupSlot(null); setPickupDate('') }}
              style={{
                flex: 1, padding: '7px 4px', fontSize: '0.82rem',
                borderRadius: '6px', cursor: 'pointer', fontFamily: 'inherit',
                border:      `1px solid ${effectiveFulfillment === type ? '#11b3be' : '#d0d0d0'}`,
                background:  effectiveFulfillment === type ? '#11b3be' : '#fff',
                color:       effectiveFulfillment === type ? '#fff' : '#555',
                fontWeight:  effectiveFulfillment === type ? 'bold' : 'normal',
              }}
            >
              {type === 'delivery' ? '🚗 Delivery' : '🏪 Pick Up'}
            </button>
          ))}
        </div>
      )}

      <button
        className="button is-info is-fullwidth"
        onClick={() => setStep('delivery')}
      >
        {effectiveFulfillment === 'pickup' ? 'Choose Pickup Time →' : 'Continue to Delivery →'}
      </button>
    </>
  )

  const pickupBody = (
    <>
      <p style={{ fontWeight: 'bold', fontSize: '0.85rem', marginBottom: '0.25rem' }}>
        Pickup date &amp; time
      </p>
      <p style={{ fontSize: '0.78rem', color: '#666', marginBottom: '0.5rem', lineHeight: 1.4 }}>
        Pick up at 554 Boston Post Rd, Milford CT
      </p>
      <div style={{ border: '1px solid #e0e0e0', borderRadius: '8px', padding: '0.6rem', marginBottom: '0.6rem' }}>
        <CalendarPicker
          selected={pickupDate}
          onSelect={(d) => { setPickupDate(d); setPickupSlot(null) }}
          minDate={todayStr}
          maxDate={maxDateStr}
          disabledDates={pickupDisabledDates}
        />
      </div>
      {pickupDate && pickupSlots.length > 0 && (
        <>
          <p className="is-size-7 has-text-grey" style={{ marginBottom: '0.35rem' }}>
            Select a time
          </p>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '5px' }}>
            {pickupSlots.map((slot) => {
              const chosen = pickupSlot?.slotISO === slot.startISO
              return (
                <button
                  key={slot.startISO}
                  type="button"
                  onClick={() => setPickupSlot({ date: pickupDate, slotISO: slot.startISO, label: slot.label })}
                  style={{
                    padding: '5px 4px', fontSize: '0.78rem', borderRadius: '6px',
                    cursor: 'pointer', fontFamily: 'inherit',
                    border:      `1px solid ${chosen ? '#11b3be' : '#d0d0d0'}`,
                    background:  chosen ? '#11b3be' : '#fff',
                    color:       chosen ? '#fff' : '#333',
                    fontWeight:  chosen ? 'bold' : 'normal',
                  }}
                >
                  {slot.label}
                </button>
              )
            })}
          </div>
        </>
      )}
    </>
  )

  const deliveryBody = (
    <>
      <p style={{ fontWeight: 'bold', fontSize: '0.85rem', marginBottom: '0.5rem' }}>
        Delivery address
      </p>
      <div className="field">
        <input
          className="input is-small"
          placeholder="Street address"
          autoComplete="street-address"
          value={street}
          onChange={(e) => { setStreet(e.target.value); resetQuote() }}
        />
      </div>
      <div style={{ display: 'flex', gap: '6px', marginBottom: '0.4rem' }}>
        <input
          className="input is-small"
          placeholder="City"
          autoComplete="address-level2"
          value={city}
          style={{ flex: 2 }}
          onChange={(e) => { setCity(e.target.value); resetQuote() }}
        />
        <input
          className="input is-small"
          placeholder="State"
          autoComplete="address-level1"
          value={state}
          style={{ flex: 1, maxWidth: '58px' }}
          onChange={(e) => { setState(e.target.value); resetQuote() }}
        />
        <input
          className="input is-small"
          placeholder="ZIP"
          autoComplete="postal-code"
          value={zip}
          style={{ flex: 1, maxWidth: '72px' }}
          onChange={(e) => { setZip(e.target.value); resetQuote() }}
          onKeyDown={(e) => e.key === 'Enter' && canQuote && getQuote()}
        />
      </div>

      <button
        className={`button is-small is-info is-fullwidth mb-3${quoting ? ' is-loading' : ''}`}
        disabled={!canQuote || quoting}
        onClick={getQuote}
      >
        Check availability &amp; pricing
      </button>

      {quoteErr && (
        <div style={{ fontSize: '0.78rem', color: '#cc3333', marginBottom: '0.75rem', lineHeight: 1.5 }}>
          {quoteErr}
          {!quoteErr.includes('contact') && (
            <> <a href="https://beachpartyballoons.com/contact/" style={{ color: '#cc3333', textDecoration: 'underline' }}>Contact us</a> and we&rsquo;ll sort it out.</>
          )}
        </div>
      )}

      {quote && (
        <>
          <div style={{
            background: '#f0f9fa', border: '1px solid #b2e0e4',
            borderRadius: '8px', padding: '0.6rem 0.85rem',
            fontSize: '0.8rem', marginBottom: '1rem',
          }}>
            <div style={{ fontWeight: 'bold', marginBottom: '3px', color: '#0d6e75' }}>{quote.label}</div>
            <div style={{ color: '#444' }}>
              {quote.miles} driving miles &nbsp;·&nbsp;
              {fmt(quote.baseCents)} base + {fmt(quote.mileCents)} mileage
            </div>
            <div style={{ fontWeight: 'bold', marginTop: '4px' }}>
              Est. delivery: {fmt(quote.totalCents)}
            </div>
          </div>

          <DeliveryDatePicker
            address={fullAddress}
            tier={quote.tier}
            value={deliverySlot}
            onChange={setDeliverySlot}
          />

          <div className="field" style={{ marginTop: '0.75rem' }}>
            <label className="label is-small">Delivery instructions <span style={{ fontWeight: 'normal', color: '#999' }}>(optional)</span></label>
            <textarea
              className="textarea is-small"
              rows={2}
              placeholder="Gate code, parking notes, where to set up…"
              value={deliveryInstructions}
              onChange={(e) => setDeliveryInstructions(e.target.value)}
            />
          </div>
        </>
      )}
    </>
  )

  const deliveryFooter = (
    <>
      {effectiveFulfillment === 'delivery' && (
        <p style={{ fontSize: '0.72rem', color: '#999', marginBottom: '0.5rem' }}>
          Delivery fee is based on driving distance from our shop.
        </p>
      )}
      <button
        className="button is-info is-fullwidth"
        disabled={effectiveFulfillment === 'delivery' ? (!quote || !deliverySlot) : !pickupSlot}
        onClick={() => setStep('info')}
      >
        Continue to Your Info →
      </button>
    </>
  )

  const infoBody = (
    <>
      <p style={{ color: '#555', fontSize: '0.85rem', marginBottom: '1rem' }}>
        We&rsquo;ll use this to send your booking confirmation and create your account.
      </p>

      <div style={{ display: 'flex', gap: '8px' }}>
        <div className="field" style={{ flex: 1 }}>
          <label className="label is-small">First name</label>
          <input
            className={`input is-small${infoErrors.firstName ? ' is-danger' : ''}`}
            placeholder="Jane"
            autoComplete="given-name"
            maxLength={300}
            value={custFirst}
            onChange={(e) => { setCustFirst(e.target.value); setInfoErrors((p) => ({ ...p, firstName: undefined })) }}
          />
          {infoErrors.firstName && <p className="help is-danger">{infoErrors.firstName}</p>}
        </div>
        <div className="field" style={{ flex: 1 }}>
          <label className="label is-small">Last name</label>
          <input
            className={`input is-small${infoErrors.lastName ? ' is-danger' : ''}`}
            placeholder="Smith"
            autoComplete="family-name"
            maxLength={300}
            value={custLast}
            onChange={(e) => { setCustLast(e.target.value); setInfoErrors((p) => ({ ...p, lastName: undefined })) }}
          />
          {infoErrors.lastName && <p className="help is-danger">{infoErrors.lastName}</p>}
        </div>
      </div>
      <div className="field">
        <label className="label is-small">Email address</label>
        <input
          className={`input is-small${infoErrors.email ? ' is-danger' : ''}`}
          type="email"
          placeholder="jane@example.com"
          autoComplete="email"
          maxLength={255}
          value={custEmail}
          onChange={(e) => { setCustEmail(e.target.value); setInfoErrors((p) => ({ ...p, email: undefined })) }}
        />
        {infoErrors.email && <p className="help is-danger">{infoErrors.email}</p>}
      </div>
      <div className="field">
        <label className="label is-small">Phone number</label>
        <input
          className={`input is-small${infoErrors.phone ? ' is-danger' : ''}`}
          type="tel"
          placeholder="(203) 555-0100"
          autoComplete="tel"
          maxLength={17}
          value={custPhone}
          onChange={(e) => {
            const digits = e.target.value.replace(/\D/g, '').slice(0, 10)
            let formatted = digits
            if (digits.length > 6) formatted = `(${digits.slice(0,3)}) ${digits.slice(3,6)}-${digits.slice(6)}`
            else if (digits.length > 3) formatted = `(${digits.slice(0,3)}) ${digits.slice(3)}`
            else if (digits.length > 0) formatted = `(${digits}`
            setCustPhone(formatted)
            setInfoErrors((p) => ({ ...p, phone: undefined }))
          }}
        />
        {infoErrors.phone && <p className="help is-danger">{infoErrors.phone}</p>}
      </div>

      {/* Order summary recap */}
      <div style={{
        background: '#f8f8f8', borderRadius: '8px',
        padding: '0.75rem', fontSize: '0.82rem',
        marginTop: '1rem',
      }}>
        <p style={{ fontWeight: 'bold', marginBottom: '4px' }}>Order summary</p>
        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '2px' }}>
          <span>Items</span><span>{fmt(subtotal)}</span>
        </div>
        {effectiveFulfillment === 'delivery' && quote && (
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '2px' }}>
            <span>Delivery</span><span>{fmt(quote.totalCents)}</span>
          </div>
        )}
        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '2px', color: '#666' }}>
          <span>Tax (6.35%)</span><span>{fmt(taxCents)}</span>
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', fontWeight: 'bold', borderTop: '1px solid #ddd', paddingTop: '4px', marginTop: '4px' }}>
          <span>Estimated total</span><span>{fmt(grandTotal)}</span>
        </div>
        {effectiveFulfillment === 'delivery' && deliverySlot && (
          <p style={{ color: '#555', marginTop: '6px' }}>
            Delivery: {deliverySlot.date} at {deliverySlot.label}
          </p>
        )}
        {effectiveFulfillment === 'pickup' && pickupSlot && (
          <p style={{ color: '#555', marginTop: '6px' }}>
            Pickup: {pickupSlot.date} at {pickupSlot.label}
          </p>
        )}
      </div>

      {/* Balloon release agreement */}
      <div style={{
        marginTop:    '1rem',
        background:   '#fff8e1',
        border:       `1.5px solid ${infoErrors.balloon ? '#e53e3e' : '#f6c000'}`,
        borderRadius: '8px',
        padding:      '0.85rem 1rem',
      }}>
        <label style={{ display: 'flex', gap: '10px', alignItems: 'flex-start', cursor: 'pointer' }}>
          <input
            type="checkbox"
            checked={balloonAgreement}
            onChange={(e) => {
              setBalloonAgreement(e.target.checked)
              if (e.target.checked) setInfoErrors((p) => ({ ...p, balloon: undefined }))
            }}
            style={{ marginTop: '3px', flexShrink: 0, width: '16px', height: '16px', accentColor: '#11b3be' }}
          />
          <span style={{ fontSize: '0.82rem', color: '#5a4000', lineHeight: 1.5 }}>
            <strong>I agree not to release these balloons outdoors.</strong> I understand that balloon releases are harmful to wildlife and the environment, and I will keep all balloons weighted, anchored, or indoors at all times.
          </span>
        </label>
        {infoErrors.balloon && (
          <p style={{ color: '#e53e3e', fontSize: '0.78rem', marginTop: '0.4rem', marginLeft: '26px' }}>
            {infoErrors.balloon}
          </p>
        )}
      </div>
    </>
  )

  const infoFooter = (
    <button
      className="button is-info is-fullwidth"
      onClick={validateAndContinue}
    >
      Continue to Payment →
    </button>
  )

  const paymentSummary = (
    <div style={{
      background: '#f8f8f8', borderRadius: '8px',
      padding: '0.75rem', fontSize: '0.82rem',
      marginBottom: '1.25rem',
    }}>
      <p style={{ fontWeight: 'bold', marginBottom: '4px' }}>Order summary</p>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '2px' }}>
        <span>Items</span><span>{fmt(subtotal)}</span>
      </div>
      {effectiveFulfillment === 'delivery' && quote && (
        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '2px' }}>
          <span>Delivery</span><span>{fmt(quote.totalCents)}</span>
        </div>
      )}
      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '2px', color: '#666' }}>
        <span>Tax (6.35%)</span><span>{fmt(taxCents)}</span>
      </div>
      <div style={{ display: 'flex', justifyContent: 'space-between', fontWeight: 'bold', borderTop: '1px solid #ddd', paddingTop: '4px', marginTop: '4px' }}>
        <span>Total</span><span>{fmt(grandTotal)}</span>
      </div>
      {effectiveFulfillment === 'delivery' && deliverySlot && (
        <p style={{ color: '#555', marginTop: '6px' }}>
          Delivery: {deliverySlot.date} at {deliverySlot.label}
        </p>
      )}
      {effectiveFulfillment === 'pickup' && pickupSlot && (
        <p style={{ color: '#555', marginTop: '6px' }}>
          Pickup: {pickupSlot.date} at {pickupSlot.label}
        </p>
      )}
    </div>
  )

  const paymentFooter = null   // PaymentForm renders its own button

  const bodyContent: Record<Step, React.ReactNode> = {
    cart:     cartBody,
    delivery: effectiveFulfillment === 'pickup' ? pickupBody : deliveryBody,
    info:     infoBody,
    payment:  paymentSummary,   // PaymentForm rendered separately below, always mounted
  }

  const footerContent: Record<Step, React.ReactNode> = {
    cart:     cartFooter,
    delivery: deliveryFooter,
    info:     infoFooter,
    payment:  paymentFooter,
  }

  const stepIndex  = STEP_ORDER.indexOf(step)
  const isFirstStep = stepIndex === 0

  return (
    <>
      {/* Backdrop */}
      {drawerOpen && !editingEntry && (
        <div
          onClick={handleClose}
          style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.45)', zIndex: 1000 }}
        />
      )}

      {/* Drawer panel */}
      <div style={{
        position:      'fixed',
        top:           0,
        right:         0,
        bottom:        0,
        width:         'min(420px, 95vw)',
        background:    '#fff',
        boxShadow:     '-4px 0 24px rgba(0,0,0,0.15)',
        zIndex:        1001,
        display:       'flex',
        flexDirection: 'column',
        transform:     drawerOpen && !editingEntry ? 'translateX(0)' : 'translateX(100%)',
        transition:    'transform 400ms ease',
      }}>

        {/* Header */}
        <div style={{
          background:     '#11b3be',
          color:          '#fff',
          padding:        '1rem 1.25rem',
          display:        'flex',
          alignItems:     'center',
          justifyContent: 'space-between',
          gap:            '0.5rem',
        }}>
          {/* Back button (hidden on first step) */}
          <button
            onClick={goBack}
            aria-label="Go back"
            style={{
              background: 'none', border: 'none', color: '#fff',
              fontSize: '1.3rem', cursor: 'pointer', lineHeight: 1,
              visibility: isFirstStep ? 'hidden' : 'visible',
              padding: '0 0.25rem',
            }}
          >
            ‹
          </button>

          <div style={{ flex: 1, textAlign: 'center' }}>
            <strong style={{ fontSize: '1.05rem', display: 'inline-flex', alignItems: 'center', gap: '5px' }}>
              {step === 'payment' && (
                <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
                  <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
                </svg>
              )}
              {step === 'delivery' && effectiveFulfillment === 'pickup' ? 'Pickup Time' : STEP_TITLES[step]}
              {step === 'cart' && totalItems > 0 && ` (${totalItems})`}
            </strong>
            {/* Step indicator dots */}
            <div style={{ display: 'flex', justifyContent: 'center', gap: '5px', marginTop: '4px' }}>
              {STEP_ORDER.map((s, i) => (
                <div
                  key={s}
                  style={{
                    width: '6px', height: '6px', borderRadius: '50%',
                    background: i <= stepIndex ? '#fff' : 'rgba(255,255,255,0.4)',
                  }}
                />
              ))}
            </div>
          </div>

          <button
            onClick={handleClose}
            aria-label="Close cart"
            style={{ background: 'none', border: 'none', color: '#fff', fontSize: '1.5rem', cursor: 'pointer', lineHeight: 1 }}
          >
            ×
          </button>
        </div>

        {/* Body */}
        <div style={{ flex: 1, overflowY: 'auto', padding: '1rem 1.25rem' }}>
          {orderId ? (
            /* ── Success screen ── */
            <div style={{ textAlign: 'center', padding: '2rem 0.5rem' }}>
              <div style={{ fontSize: '3rem', marginBottom: '0.75rem' }}>🎈</div>
              <p style={{ fontWeight: 'bold', fontSize: '1.1rem', marginBottom: '0.5rem' }}>
                You&rsquo;re booked!
              </p>
              <p style={{ color: '#555', fontSize: '0.88rem', marginBottom: '1rem', lineHeight: 1.5 }}>
                Order <strong>#{shortRef}</strong> confirmed.{' '}
                {effectiveFulfillment === 'pickup'
                  ? <>Your pickup is all set — see you at the shop! A confirmation will be sent to <strong>{custEmail}</strong>.</>
                  : <>We&rsquo;ll reach out to <strong>{custEmail}</strong> to confirm final delivery details.</>
                }
              </p>
              {effectiveFulfillment === 'delivery' && deliverySlot && (
                <p style={{ color: '#0d6e75', fontSize: '0.85rem', marginBottom: '1.5rem' }}>
                  Delivery: {deliverySlot.date} at {deliverySlot.label}
                </p>
              )}
              {effectiveFulfillment === 'pickup' && pickupSlot && (
                <p style={{ color: '#0d6e75', fontSize: '0.85rem', marginBottom: '1.5rem' }}>
                  Pickup: {pickupSlot.date} at {pickupSlot.label}
                </p>
              )}
              <button
                className="button is-info is-fullwidth"
                onClick={() => { setOrderId(null); handleClose() }}
              >
                Close
              </button>
            </div>
          ) : (
            <>
              {bodyContent[step]}
              {/* PaymentForm stays mounted to prevent Square DOM errors on step changes */}
              <div style={{ display: step === 'payment' && !orderId ? undefined : 'none' }}>
                <PaymentForm payload={checkoutPayload} onSuccess={handleSuccess} active={step === 'payment' && !orderId} />
              </div>
            </>
          )}
        </div>

        {/* Footer — hidden on success screen */}
        {!orderId && footerContent[step] && (
          <div style={{ padding: '0.75rem 1.25rem', borderTop: '1px solid #e6dfc8' }}>
            {footerContent[step]}
          </div>
        )}
      </div>
      {/* Edit item modal — rendered outside the drawer so it overlays everything */}
      {editingEntry && (
        <ColorPicker
          product={editingEntry.product}
          maxColors={editingEntry.product.showColors
            ? (editingEntry.product.colorMax !== null && editingEntry.product.colorMax !== undefined
                ? editingEntry.product.colorMax
                : maxColorsFor(editingEntry.product.name))
            : null}
          editingEntry={editingEntry}
          onClose={() => setEditingEntry(null)}
        />
      )}
    </>
  )
}
