// Booking drawer — slides in from right when a slot is picked.
//
// Pre-order: design-system catalog + menu drill-in pattern. Catalog shows
// Packages (vertical list with inline quick-add steppers) and Menus
// (2-column tinted card grid that drills into per-item selection). The
// footer becomes the canonical cart entry point — click the bag chip to
// expand a popover of every line item, grouped and subtotaled.

function BookingDrawer({ slot, onClose, onConfirm }) {
  const [stage, setStage] = React.useState('form'); // form | confirming | done
  const [form, setForm] = React.useState({
    firstName: '', lastName: '', email: '', phone: '', notes: '',
    sendConfirm: true,
  });
  const [preOrder, setPreOrder] = React.useState({ items: {}, packages: {}, menuId: null });
  const [openMenuId, setOpenMenuId] = React.useState(null); // null = catalog; id = menu detail
  const [cartOpen, setCartOpen] = React.useState(false);
  const [step, setStep] = React.useState(1); // 1 = pre-order, 2 = guest details
  React.useEffect(() => {
    setPreOrder({ items: {}, packages: {}, menuId: null });
    setOpenMenuId(null);
    setCartOpen(false);
    setStep(1);
  }, [slot && slot.venueId, slot && slot.productId]);

  const venue = slot && venueById(slot.venueId);
  const product = slot && productById(slot.productId);
  // Prefer matching the raw product by composite — multiple Bookable products
  // collapse into one portal bucket and only the specific one carries the
  // correct pre-order allow-list / required-type for the slot just clicked.
  const rawProducts = (venue && venue.rawProducts) || [];
  const rawProduct = rawProducts.find(rp => slot && slot.compositeId && rp.compositeId === slot.compositeId)
                  || rawProducts.find(rp => rp.portalProduct === (slot && slot.productId));
  // Pre-order catalogue is published once at venue level; each product
  // declares which menus/packages it allows via `preorders.menuIds` etc.
  // Filter the venue catalogue down to the IDs scoped to this product.
  const allMenus = (venue && venue.preorders && venue.preorders.menus) || [];
  const allPackages = (venue && venue.preorders && venue.preorders.packages) || [];
  const allowedMenuIds = (rawProduct && rawProduct.preorderMenuIds) || [];
  const allowedPackageIds = (rawProduct && rawProduct.preorderPackageIds) || [];
  // The availability response scopes pre-order packages/menus per (date × slot),
  // which is narrower than the venue catalogue allow-list — a package the
  // catalogue lists may not be bookable on the chosen date. When the clicked
  // slot carries this scoping (`preOrderItems`), it wins. Fall back to the
  // product's static catalogue allow-list only when it's absent (e.g. a
  // map-view quick-book that never resolved a specific slot).
  const slotPre = slot && slot.preOrderItems;
  const scopeCatalogue = (catalogue, mode, ids, fallbackIds) => {
    if (mode === 'none') return [];
    if (mode === 'all') return catalogue;
    if (mode === 'set') {
      const set = (ids || []).map(String);
      return catalogue.filter(x => set.includes(String(x.id)) || (x.slug && set.includes(x.slug)));
    }
    // No slot scoping — fall back to the catalogue allow-list for this product.
    return fallbackIds.length
      ? catalogue.filter(x => fallbackIds.includes(String(x.id)) || (x.slug && fallbackIds.includes(x.slug)))
      : [];
  };
  const packagesList = scopeCatalogue(allPackages, slotPre && slotPre.allowedPackages, slotPre && slotPre.packageIds, allowedPackageIds);
  const menus = scopeCatalogue(allMenus, slotPre && slotPre.allowedMenus, slotPre && slotPre.menuIds, allowedMenuIds);
  const preOrderRequiredType = rawProduct && rawProduct.preOrderRequired ? (rawProduct.preOrderRequiredType || 'menu') : 'none';
  const hasCatalogue = menus.length > 0 || packagesList.length > 0;
  const preOrderType = preOrderRequiredType !== 'none' ? preOrderRequiredType
                     : hasCatalogue ? (packagesList.length > 0 && menus.length > 0 ? 'packageAndMenu'
                                     : packagesList.length > 0 ? 'package'
                                     : 'menu')
                     : 'none';
  const preOrderOptional = preOrderRequiredType === 'none' && hasCatalogue;
  const wideDrawer = preOrderType !== 'none' && hasCatalogue;

  const packagesQtyTotal = Object.values(preOrder.packages).reduce((n, q) => n + (q || 0), 0);
  const menuItemsQtyTotal = Object.values(preOrder.items).reduce((n, q) => n + (q || 0), 0);
  const selectedMenu = menus.find(m => m.id === preOrder.menuId) || null;
  const openMenu = openMenuId ? menus.find(m => m.id === openMenuId) : null;
  const packagesSubtotal = packagesList.reduce((n, p) => n + (preOrder.packages[p.id] || 0) * (typeof p.price === 'number' ? p.price : 0), 0);
  const itemsSubtotal = (selectedMenu?.items || []).reduce((n, it) => n + (preOrder.items[it.id] || 0) * (typeof it.price === 'number' ? it.price : 0), 0);
  const preOrderTotal = packagesSubtotal + itemsSubtotal;
  const cartLineCount = packagesQtyTotal + menuItemsQtyTotal;

  // Either path satisfies packageAndMenu — agent picks a package OR builds
  // a menu, not both.
  const preOrderSatisfied =
    (preOrderRequiredType === 'none') ||
    (preOrderRequiredType === 'package' && packagesQtyTotal > 0) ||
    (preOrderRequiredType === 'menu' && menuItemsQtyTotal > 0) ||
    (preOrderRequiredType === 'packageAndMenu' && (packagesQtyTotal > 0 || menuItemsQtyTotal > 0));

  React.useEffect(() => {
    if (!slot) return;
    const onKey = (e) => { if (e.key === 'Escape') { if (cartOpen) setCartOpen(false); else onClose(); } };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [slot, onClose, cartOpen]);

  // Lock the page behind the drawer so wheel events on the scrim don't
  // scroll the underlying results list.
  React.useEffect(() => {
    if (!slot) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => { document.body.style.overflow = prev; };
  }, [slot]);

  if (!slot) return null;

  const G = PRODUCT_GLYPHS[slot.productId] || PRODUCT_GLYPHS.dinner;
  const customerValid = form.firstName && form.lastName && form.email && form.phone;
  const valid = customerValid && preOrderSatisfied;

  const submit = () => {
    setCartOpen(false);
    setStage('confirming');
    setTimeout(() => {
      setStage('done');
      const preOrderPayload = preOrderType !== 'none' ? {
        type: preOrderType,
        packages: Object.entries(preOrder.packages)
          .filter(([, qty]) => qty > 0)
          .map(([packageId, qty]) => {
            const p = packagesList.find(x => x.id === packageId) || {};
            return { id: packageId, slug: p.slug, name: p.name, price: p.price, qty };
          }),
        menu: selectedMenu ? { id: selectedMenu.id, slug: selectedMenu.slug, name: selectedMenu.name } : null,
        items: Object.entries(preOrder.items)
          .filter(([, qty]) => qty > 0)
          .map(([itemId, qty]) => {
            const it = (selectedMenu?.items || []).find(x => x.id === itemId) || {};
            return { id: itemId, name: it.name, price: it.price, qty };
          }),
        subtotal: preOrderTotal,
      } : null;
      onConfirm({
        ...slot,
        customer: form.firstName + ' ' + form.lastName,
        email: form.email,
        phone: form.phone,
        notes: form.notes,
        preOrder: preOrderPayload,
      });
    }, 700);
  };

  const setItemQty = (itemId, qty) => {
    setPreOrder(s => {
      const items = { ...s.items };
      if (qty <= 0) delete items[itemId];
      else items[itemId] = qty;
      return { ...s, items };
    });
  };
  const setPackageQty = (packageId, qty) => {
    setPreOrder(s => {
      const pkgs = { ...s.packages };
      if (qty <= 0) delete pkgs[packageId];
      else pkgs[packageId] = qty;
      return { ...s, packages: pkgs };
    });
  };
  const openMenuDetail = (menuId) => {
    setPreOrder(s => s.menuId === menuId ? s : { ...s, menuId });
    setOpenMenuId(menuId);
  };
  const closeMenuDetail = () => setOpenMenuId(null);

  const confirmingLabel = slot.type === 'request' ? 'Sending enquiry…' : 'Confirming…';
  const confirmLabel    = slot.type === 'request' ? 'Send enquiry'     : 'Confirm booking';

  // For the no-pre-order classic flow the footer keeps its single-step
  // Confirm button. For the two-step flow the footer button label and
  // disabled state are driven by the current step.
  const twoStep = preOrderType !== 'none';
  const step1Done = preOrderSatisfied; // step 1 satisfied to advance
  let footerAction;
  if (!twoStep) {
    footerAction = {
      label: stage === 'confirming' ? confirmingLabel : confirmLabel,
      disabled: !customerValid || stage === 'confirming',
      onClick: submit,
    };
  } else if (step === 1) {
    if (!preOrderOptional && cartLineCount === 0) {
      footerAction = { label: 'Add items to continue', disabled: true, onClick: () => {} };
    } else if (cartLineCount === 0) {
      footerAction = { label: 'Skip pre-order →', disabled: false, onClick: () => { setCartOpen(false); setStep(2); } };
    } else {
      footerAction = { label: 'Continue →', disabled: false, onClick: () => { setCartOpen(false); setStep(2); } };
    }
  } else {
    footerAction = {
      label: stage === 'confirming' ? confirmingLabel : confirmLabel,
      disabled: !customerValid || stage === 'confirming',
      onClick: submit,
    };
  }

  return (
    <React.Fragment>
      <div className="pp-drawer-scrim" onClick={() => stage !== 'confirming' && !cartOpen && onClose()}/>
      <aside className={"pp-drawer" + (wideDrawer ? " pp-drawer--xl" : "")} role="dialog" aria-label="New booking">
        <header className="pp-drawer-head">
          <div className="pp-drawer-head-title">
            <span className="pp-drawer-eyebrow">New booking</span>
            <h2>{venue.name}</h2>
            {preOrderType !== 'none' && (
              <div className="pp-drawer-head-context">
                <span>{fmtDate(slot.date)} · {slot.time}</span>
                <span className="pp-drawer-head-dot"/>
                <span>{slot.guests} guests</span>
                <span className="pp-drawer-head-dot"/>
                <span className="pp-operator-pill"><OperatorTag id={venue.operator} size="sm"/></span>
              </div>
            )}
          </div>
          <button className="pp-icon-btn" onClick={onClose} aria-label="Close">
            <IconClose size={16}/>
          </button>
        </header>

        <div className="pp-drawer-body">
          {stage === 'done' ? (
            <ConfirmedView slot={slot} customer={form.firstName + ' ' + form.lastName} email={form.email}/>
          ) : (
            <React.Fragment>
              {preOrderType === 'none' && (
                <section className="pp-summary">
                  <div className="pp-summary-row">
                    <span className="pp-summary-icon"><IconPin size={14}/></span>
                    <span className="pp-summary-k">Location</span>
                    <span className="pp-summary-v">{venue.city}</span>
                  </div>
                  <div className="pp-summary-row">
                    <span className="pp-summary-icon"><IconTag size={14}/></span>
                    <span className="pp-summary-k">Operator</span>
                    <OperatorTag id={venue.operator} size="sm"/>
                  </div>
                  <div className="pp-summary-row">
                    <span className="pp-summary-icon"><G size={14}/></span>
                    <span className="pp-summary-k">Product</span>
                    <span className="pp-summary-v">{product.name}</span>
                  </div>
                  <div className="pp-summary-row">
                    <span className="pp-summary-icon"><IconCal size={14}/></span>
                    <span className="pp-summary-k">When</span>
                    <span className="pp-summary-v">{fmtDate(slot.date)} · {slot.time}</span>
                  </div>
                  <div className="pp-summary-row">
                    <span className="pp-summary-icon"><IconUsers size={14}/></span>
                    <span className="pp-summary-k">Guests</span>
                    <span className="pp-summary-v">{slot.guests}</span>
                  </div>
                  <div className="pp-summary-row">
                    <span className="pp-summary-icon"><IconCheck size={14}/></span>
                    <span className="pp-summary-k">Confirmation</span>
                    <span className={"pp-slot-legend pp-slot-legend--" + (slot.type === 'request' ? 'request' : 'book')}>
                      <span className="pp-slot-legend-sw"/>
                      <span>{slot.type === 'request' ? 'Operator approval required' : 'Instant — auto-confirmed'}</span>
                    </span>
                  </div>
                </section>
              )}

              {!twoStep && (
                <React.Fragment>
                  <section className="pp-form-section">
                    <h3 className="pp-form-section-title">Customer details</h3>
                    <div className="pp-form-grid pp-form-grid--2">
                      <Field label="First name">
                        <input className="pp-input" value={form.firstName}
                               onChange={(e) => setForm({ ...form, firstName: e.target.value })}/>
                      </Field>
                      <Field label="Last name">
                        <input className="pp-input" value={form.lastName}
                               onChange={(e) => setForm({ ...form, lastName: e.target.value })}/>
                      </Field>
                      <Field label="Email">
                        <input className="pp-input" type="email" value={form.email}
                               onChange={(e) => setForm({ ...form, email: e.target.value })}/>
                      </Field>
                      <Field label="Phone">
                        <input className="pp-input" type="tel" value={form.phone}
                               onChange={(e) => setForm({ ...form, phone: e.target.value })}/>
                      </Field>
                    </div>
                    <Field label="Special requests (optional)">
                      <textarea className="pp-input pp-input--area" rows={3}
                                placeholder="Allergies, accessibility, table preference…"
                                value={form.notes}
                                onChange={(e) => setForm({ ...form, notes: e.target.value })}/>
                    </Field>
                  </section>
                  <section className="pp-form-section">
                    <label className="pp-checkbox">
                      <input type="checkbox" checked={form.sendConfirm}
                             onChange={(e) => setForm({ ...form, sendConfirm: e.target.checked })}/>
                      <span className="pp-checkbox-box"><IconCheck size={11}/></span>
                      <span>Email confirmation to the customer</span>
                    </label>
                  </section>
                </React.Fragment>
              )}

              {twoStep && (
                <StepCard
                  step={step}
                  setStep={setStep}
                  preOrderOptional={preOrderOptional}
                  step1Done={step1Done}
                  openMenu={openMenu}
                  closeMenuDetail={closeMenuDetail}
                >
                  {step === 1 && (
                    openMenu
                      ? <MenuDetailView menu={openMenu} preOrder={preOrder} setItemQty={setItemQty}/>
                      : <CatalogView
                          packages={packagesList}
                          menus={menus}
                          preOrder={preOrder}
                          setPackageQty={setPackageQty}
                          openMenuDetail={openMenuDetail}
                        />
                  )}
                  {step === 2 && (
                    <DetailsForm form={form} setForm={setForm}/>
                  )}
                </StepCard>
              )}
            </React.Fragment>
          )}
        </div>

        {stage !== 'done' && (
          <div className={"pp-cart-footer" + (cartOpen ? " is-open" : "")}>
            {cartOpen && (
              <div className="pp-cart-backdrop" onClick={() => setCartOpen(false)}/>
            )}
            {cartOpen && (
              <CartPopover
                preOrder={preOrder}
                packages={packagesList}
                menu={selectedMenu}
                packagesQtyTotal={packagesQtyTotal}
                menuItemsQtyTotal={menuItemsQtyTotal}
                packagesSubtotal={packagesSubtotal}
                itemsSubtotal={itemsSubtotal}
                preOrderTotal={preOrderTotal}
                setItemQty={setItemQty}
                setPackageQty={setPackageQty}
                onClose={() => setCartOpen(false)}
              />
            )}

            <footer className={"pp-drawer-foot pp-drawer-foot--cart" + (!twoStep ? ' pp-drawer-foot--end' : '')}>
              {twoStep && (
                <button type="button" className="pp-footer-summary"
                        onClick={() => setCartOpen(o => !o)}
                        aria-expanded={cartOpen}>
                  <span className="pp-footer-bag">
                    <PreorderIcon name="bag" size={22}/>
                    {cartLineCount > 0 && (
                      <span className="pp-footer-bag-count">{cartLineCount}</span>
                    )}
                  </span>
                  <span className="pp-footer-text">
                    <span className="pp-footer-label">
                      {cartLineCount === 0 ? 'Pre-order' : 'View order'}
                      <PreorderIcon name="chev" size={12}
                        className={"pp-footer-chev" + (cartOpen ? " is-open" : "")}/>
                    </span>
                    <span className="pp-footer-note">
                      {cartLineCount === 0
                        ? (preOrderOptional ? 'Optional — pick now or skip' : 'Nothing selected yet')
                        : cartLineCount + ' item' + (cartLineCount !== 1 ? 's' : '') + ' in this booking'}
                    </span>
                  </span>
                  <span className="pp-footer-amt">£{preOrderTotal.toFixed(2)}</span>
                </button>
              )}
              <button className="pp-btn pp-btn--primary pp-footer-confirm"
                      disabled={footerAction.disabled}
                      onClick={footerAction.onClick}>
                {footerAction.label}
              </button>
            </footer>
          </div>
        )}
      </aside>
    </React.Fragment>
  );
}

// StepCard — warm panel that wraps both step 1 (pre-order catalog / menu
// drill-in) and step 2 (guest details). The header switches depending on
// what's being shown:
//   - Step 1, catalog view:        "PRE-ORDER" eyebrow
//   - Step 1, drilled into a menu: back arrow + menu name (+ price/cover)
//   - Step 2:                      back arrow + "GUEST DETAILS"
function StepCard({ step, setStep, preOrderOptional, step1Done, openMenu, closeMenuDetail, children }) {
  const showBack = !!openMenu || step === 2;
  const onBack = () => {
    if (step === 2) setStep(1);
    else if (openMenu) closeMenuDetail();
  };
  const title = step === 2 ? 'GUEST DETAILS' : (openMenu ? openMenu.name : null);
  return (
    <section className="pp-preorder-card">
      <header className="pp-preorder-head">
        {showBack ? (
          <div className="pp-preorder-head-left">
            <button type="button" className="pp-preorder-back" onClick={onBack}
                    aria-label={step === 2 ? 'Back to pre-order' : 'Back to catalog'}>
              <PreorderIcon name="back" size={16}/>
            </button>
            <div className={step === 2 ? "pp-preorder-eyebrow" : "pp-preorder-name"}>{title}</div>
          </div>
        ) : (
          <div className="pp-preorder-eyebrow">PRE-ORDER{preOrderOptional ? ' (OPTIONAL)' : ''}</div>
        )}
        {step === 1 && openMenu && typeof openMenu.pricePerCover === 'number' && (
          <div className="pp-preorder-ppc">£{openMenu.pricePerCover.toFixed(2)} <em>/ cover</em></div>
        )}
      </header>

      <div className="pp-preorder-body">
        <StepIndicator step={step} setStep={setStep}
                       preOrderOptional={preOrderOptional}
                       step1Done={step1Done}/>
        {children}
      </div>
    </section>
  );
}

// StepIndicator — two-dot tracker. Click step 1 anytime; step 2 only
// when pre-order is satisfied (or it's optional and the agent is
// skipping). A green check replaces the "1" once the agent has advanced
// (or in the optional path with an empty cart, just for visual feedback).
function StepIndicator({ step, setStep, preOrderOptional, step1Done }) {
  const step2Locked = !preOrderOptional && !step1Done;
  const step1IsDone = step > 1 && step1Done;
  return (
    <div className="pp-preorder-steps">
      <button type="button"
              className={"pp-preorder-step" + (step === 1 ? " is-active" : "") + (step1IsDone ? " is-done" : "")}
              onClick={() => setStep(1)}>
        <span className="pp-preorder-step-num">
          {step1IsDone ? <PreorderIcon name="check" size={12}/> : '1'}
        </span>
        <span className="pp-preorder-step-label">
          Pre-order
          {!preOrderOptional && <span className="pp-preorder-step-tag">Required</span>}
        </span>
      </button>
      <span className="pp-preorder-step-line"/>
      <button type="button"
              className={"pp-preorder-step" + (step === 2 ? " is-active" : "")}
              onClick={() => !step2Locked && setStep(2)}
              disabled={step2Locked}>
        <span className="pp-preorder-step-num">2</span>
        <span className="pp-preorder-step-label">Guest details</span>
      </button>
    </div>
  );
}

// DetailsForm — step 2 content: first / last / email / phone (required)
// + special-requests textarea + email confirmation checkbox.
function DetailsForm({ form, setForm }) {
  return (
    <div className="pp-preorder-form">
      <div className="pp-preorder-form-grid">
        <DetailsField label="First name" required>
          <input className="pp-preorder-input" value={form.firstName}
                 onChange={(e) => setForm({ ...form, firstName: e.target.value })}
                 placeholder="Jamie"/>
        </DetailsField>
        <DetailsField label="Last name" required>
          <input className="pp-preorder-input" value={form.lastName}
                 onChange={(e) => setForm({ ...form, lastName: e.target.value })}
                 placeholder="Carter"/>
        </DetailsField>
        <DetailsField label="Email" required>
          <input className="pp-preorder-input" type="email" value={form.email}
                 onChange={(e) => setForm({ ...form, email: e.target.value })}
                 placeholder="jamie.carter@example.com"/>
        </DetailsField>
        <DetailsField label="Phone" required>
          <input className="pp-preorder-input" type="tel" value={form.phone}
                 onChange={(e) => setForm({ ...form, phone: e.target.value })}
                 placeholder="07700 900000"/>
        </DetailsField>
      </div>
      <DetailsField label="Special requests" sublabel="optional" multiline>
        <textarea className="pp-preorder-input pp-preorder-input--area" rows={3}
                  value={form.notes}
                  onChange={(e) => setForm({ ...form, notes: e.target.value })}
                  placeholder="Allergies, accessibility, table preference…"/>
      </DetailsField>
      <label className="pp-preorder-check">
        <input type="checkbox" checked={form.sendConfirm}
               onChange={(e) => setForm({ ...form, sendConfirm: e.target.checked })}/>
        <span>Email confirmation to the customer</span>
      </label>
    </div>
  );
}

function DetailsField({ label, sublabel, required, multiline, children }) {
  return (
    <div className={"pp-preorder-field" + (multiline ? " is-multiline" : "")}>
      <label className="pp-preorder-field-label">
        {label}
        {required && <em className="pp-preorder-field-req">*</em>}
        {sublabel && !required && <em className="pp-preorder-field-sub">{sublabel}</em>}
      </label>
      {children}
    </div>
  );
}

function CatalogView({ packages, menus, preOrder, setPackageQty, openMenuDetail }) {
  const showPackages = packages && packages.length > 0;
  const showMenus    = menus && menus.length > 0;
  const itemsSelectedFor = (menuId) => menuId === preOrder.menuId
    ? Object.values(preOrder.items).reduce((a, b) => a + b, 0) : 0;

  if (!showPackages && !showMenus) {
    return (
      <div className="pp-preorder-empty">
        Pre-order is required for this product, but the venue has not published any menus or packages yet. Add internal notes describing the guest's selection or contact the venue directly.
      </div>
    );
  }

  return (
    <React.Fragment>
      {showPackages && (
        <div className="pp-preorder-section">
          <div className="pp-preorder-section-head">
            <h4>Packages</h4>
            <span>{packages.length} {packages.length === 1 ? 'option' : 'options'} · add directly</span>
          </div>
          <div className="pp-preorder-rows">
            {packages.map(p => {
              const qty = preOrder.packages[p.id] || 0;
              const { icon, tint } = packageGlyph(p);
              const desc = stripHtml(p.description);
              return (
                <div key={p.id} className={"pp-preorder-row" + (qty > 0 ? " is-selected" : "")}>
                  <div className={"pp-preorder-row-glyph pp-tint-" + tint} aria-hidden="true">
                    <PreorderIcon name={icon} size={18}/>
                  </div>
                  <div className="pp-preorder-row-body">
                    <div className="pp-preorder-row-line1">
                      <span className="pp-preorder-row-title">{cleanLabel(p.name)}</span>
                      <SlimPrice value={p.price}/>
                    </div>
                    {desc && <div className="pp-preorder-row-desc">{desc}</div>}
                  </div>
                  <div className="pp-preorder-row-side">
                    <Stepper value={qty} onChange={(n) => setPackageQty(p.id, n)}/>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {showMenus && (
        <div className="pp-preorder-section">
          <div className="pp-preorder-section-head">
            <h4>Menus</h4>
            <span>{menus.length} published · click to pick items</span>
          </div>
          <div className="pp-preorder-menu-cards">
            {menus.map(m => {
              const { icon, tint } = menuGlyph(m);
              const selectedCount = itemsSelectedFor(m.id);
              const courses = deriveCourses(m);
              const ppc = typeof m.pricePerCover === 'number' ? m.pricePerCover : null;
              return (
                <button key={m.id} type="button"
                        className={"pp-preorder-menu-card" + (selectedCount > 0 ? " is-selected" : "")}
                        onClick={() => openMenuDetail(m.id)}>
                  <div className="pp-preorder-menu-card-top">
                    <div className={"pp-preorder-menu-card-glyph pp-tint-" + tint} aria-hidden="true">
                      <PreorderIcon name={icon} size={22}/>
                    </div>
                    {selectedCount > 0 && (
                      <span className="pp-preorder-menu-card-count">{selectedCount}</span>
                    )}
                  </div>
                  <div className="pp-preorder-menu-card-tag">{menuTag(m)}</div>
                  <div className="pp-preorder-menu-card-title">{cleanLabel(m.name)}</div>
                  <div className="pp-preorder-menu-card-foot">
                    <span className="pp-preorder-menu-card-courses">
                      {courses.length === 0 ? <span>Menu</span> : courses.map((c, i) => (
                        <React.Fragment key={c + i}>
                          {i > 0 && <span className="pp-preorder-menu-card-sep"/>}
                          <span>{c}</span>
                        </React.Fragment>
                      ))}
                    </span>
                    {ppc != null
                      ? <span className="pp-preorder-menu-card-ppc">£{ppc.toFixed(2)} <em>/ cover</em></span>
                      : <span className="pp-preorder-menu-card-ppc-muted">à la carte</span>}
                  </div>
                </button>
              );
            })}
          </div>
        </div>
      )}
    </React.Fragment>
  );
}

function MenuDetailView({ menu, preOrder, setItemQty }) {
  const groups = groupMenuItems(menu.items);
  if (groups.length === 0) {
    return <div className="pp-preorder-empty">This menu has no items published yet.</div>;
  }
  return (
    <div className="pp-preorder-detail">
      {groups.map(group => (
        <div key={group.label} className="pp-preorder-subtype">
          <div className="pp-preorder-subtype-head">
            <span>{group.label}</span>
            <span>{group.items.length} {group.items.length === 1 ? 'option' : 'options'}</span>
          </div>
          <div className="pp-preorder-rows">
            {group.items.map(it => {
              const qty = preOrder.items[it.id] || 0;
              const desc = stripHtml(it.description);
              return (
                <div key={it.id} className={"pp-preorder-row pp-preorder-row--item" + (qty > 0 ? " is-selected" : "")}>
                  <div className="pp-preorder-row-body">
                    <div className="pp-preorder-row-line1">
                      <span className="pp-preorder-row-title">{it.name}</span>
                      {typeof it.price === 'number' && it.price > 0
                        ? <SlimPrice value={it.price}/>
                        : <span className="pp-preorder-row-included">Included</span>}
                    </div>
                    {desc && <div className="pp-preorder-row-desc">{desc}</div>}
                    {(it.diet_types?.length > 0 || it.allergens?.length > 0) && (
                      <div className="pp-preorder-row-meta">
                        {(it.diet_types || []).slice(0, 4).map(d => (
                          <DietBadge key={d} label={d}/>
                        ))}
                        <AllergenLine list={it.allergens}/>
                      </div>
                    )}
                  </div>
                  <div className="pp-preorder-row-side">
                    <Stepper value={qty} onChange={(n) => setItemQty(it.id, n)}/>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      ))}
    </div>
  );
}

function CartPopover({ preOrder, packages, menu, packagesQtyTotal, menuItemsQtyTotal,
                       packagesSubtotal, itemsSubtotal, preOrderTotal,
                       setItemQty, setPackageQty, onClose }) {
  const isEmpty = packagesQtyTotal === 0 && menuItemsQtyTotal === 0;
  const pkgLines = packages.filter(p => (preOrder.packages[p.id] || 0) > 0).map(p => ({
    id: p.id, name: cleanLabel(p.name), qty: preOrder.packages[p.id],
    unit: typeof p.price === 'number' ? p.price : 0,
    line: (typeof p.price === 'number' ? p.price : 0) * preOrder.packages[p.id],
  }));
  const itemsBySub = {};
  if (menu) {
    for (const it of (menu.items || [])) {
      const qty = preOrder.items[it.id];
      if (!qty) continue;
      const sub = it.subType || it.type || 'Items';
      (itemsBySub[sub] = itemsBySub[sub] || []).push({
        id: it.id, name: it.name, qty,
        unit: typeof it.price === 'number' ? it.price : 0,
        line: (typeof it.price === 'number' ? it.price : 0) * qty,
      });
    }
  }
  return (
    <div className="pp-cart-popover" role="dialog" aria-label="Your order">
      <div className="pp-cart-popover-head">
        <div>
          <div className="pp-cart-popover-title">Your order</div>
          <div className="pp-cart-popover-meta">
            {isEmpty
              ? 'Nothing selected yet'
              : packagesQtyTotal + ' package' + (packagesQtyTotal !== 1 ? 's' : '')
                + ' · ' + menuItemsQtyTotal + ' menu item' + (menuItemsQtyTotal !== 1 ? 's' : '')}
          </div>
        </div>
        <button className="pp-cart-close" onClick={onClose} aria-label="Close">
          <PreorderIcon name="close" size={16}/>
        </button>
      </div>
      <div className="pp-cart-popover-body">
        {isEmpty && (
          <div className="pp-cart-empty">
            <div className="pp-cart-empty-glyph" aria-hidden="true">
              <PreorderIcon name="bag" size={32}/>
            </div>
            <div className="pp-cart-empty-head">Nothing in the order yet</div>
            <div className="pp-cart-empty-sub">Use the catalog above to add packages or menu items.</div>
          </div>
        )}
        {pkgLines.length > 0 && (
          <CartGroup title="Packages" total={packagesSubtotal}>
            {pkgLines.map(l => (
              <CartLine key={l.id} name={l.name} qty={l.qty} unit={l.unit} line={l.line}
                        meta={l.unit > 0 ? '£' + l.unit.toFixed(2) + ' each' : null}
                        onDec={() => setPackageQty(l.id, l.qty - 1)}
                        onInc={() => setPackageQty(l.id, l.qty + 1)}
                        onRemove={() => setPackageQty(l.id, 0)}/>
            ))}
          </CartGroup>
        )}
        {Object.entries(itemsBySub).map(([sub, lines]) => {
          const subtotal = lines.reduce((a, l) => a + l.line, 0);
          return (
            <CartGroup key={sub} title={sub} total={subtotal}>
              {lines.map(l => (
                <CartLine key={l.id} name={l.name} qty={l.qty} unit={l.unit} line={l.line}
                          meta={l.unit > 0 ? '£' + l.unit.toFixed(2) + ' each' : null}
                          onDec={() => setItemQty(l.id, l.qty - 1)}
                          onInc={() => setItemQty(l.id, l.qty + 1)}
                          onRemove={() => setItemQty(l.id, 0)}/>
              ))}
            </CartGroup>
          );
        })}
      </div>
      {!isEmpty && (
        <div className="pp-cart-popover-foot">
          {pkgLines.length > 0 && <div><span>Packages</span><span>£{packagesSubtotal.toFixed(2)}</span></div>}
          {menuItemsQtyTotal > 0 && <div><span>Menu items</span><span>£{itemsSubtotal.toFixed(2)}</span></div>}
          <div className="pp-cart-popover-grand">
            <span>Total</span><span>£{preOrderTotal.toFixed(2)}</span>
          </div>
        </div>
      )}
    </div>
  );
}

function CartGroup({ title, total, children }) {
  return (
    <div className="pp-cart-group">
      <div className="pp-cart-group-head">
        <span className="pp-cart-group-title">{title}</span>
        {total != null && <span className="pp-cart-group-total">£{total.toFixed(2)}</span>}
      </div>
      <div className="pp-cart-group-body">{children}</div>
    </div>
  );
}

function CartLine({ name, qty, meta, line, onDec, onInc, onRemove }) {
  return (
    <div className="pp-cart-line">
      <div className="pp-cart-line-name">
        <div className="pp-cart-line-title">{name}</div>
        {meta && <div className="pp-cart-line-meta">{meta}</div>}
      </div>
      <div className="pp-cart-line-stepper">
        <button type="button" onClick={onDec} aria-label="Decrease">−</button>
        <span>{qty}</span>
        <button type="button" onClick={onInc} aria-label="Increase">+</button>
      </div>
      <div className="pp-cart-line-line">£{line.toFixed(2)}</div>
      <button type="button" className="pp-cart-line-rem" onClick={onRemove} aria-label="Remove">×</button>
    </div>
  );
}

function Stepper({ value, onChange, min = 0, max = 99 }) {
  const dec = () => onChange(Math.max(min, value - 1));
  const inc = () => onChange(Math.min(max, value + 1));
  return (
    <div className={"pp-stepper" + (value > 0 ? " is-active" : "")}>
      <button type="button" onClick={dec} disabled={value <= min} aria-label="Decrease">−</button>
      <span className="pp-stepper-value">{value}</span>
      <button type="button" onClick={inc} disabled={value >= max} aria-label="Increase">+</button>
    </div>
  );
}

function SlimPrice({ value, suffix }) {
  if (value == null || value === 0) return null;
  return (
    <span className="pp-preorder-price">
      £{Number(value).toFixed(2)}{suffix ? <em>{suffix}</em> : null}
    </span>
  );
}

function DietBadge({ label }) {
  const colors = {
    Vegan:        { bg: '#e3f1de', fg: '#2f5d22' },
    Vegetarian:   { bg: '#eaf2dc', fg: '#3f5c1c' },
    Pescatarian:  { bg: '#dfecf6', fg: '#1f4c70' },
    GF:           { bg: '#f3ead4', fg: '#6a5118' },
    'Gluten Free':{ bg: '#f3ead4', fg: '#6a5118' },
  }[label] || { bg: '#ece8df', fg: '#5a5240' };
  return <span className="pp-preorder-diet" style={{ background: colors.bg, color: colors.fg }}>{label}</span>;
}

function AllergenLine({ list }) {
  if (!list || !list.length) return null;
  const head = list.slice(0, 2).join(', ');
  const extra = list.length > 2 ? ' +' + (list.length - 2) : '';
  return (
    <span className="pp-preorder-allergen" title={list.join(', ')}>
      <PreorderIcon name="alert" size={12}/>
      Contains {head}{extra}
    </span>
  );
}

// ─────────── icon set ───────────
const PRE_ORDER_ICON_PATHS = {
  bag:      '<path d="M6 7h12l-1 13a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2L6 7Z"/><path d="M9 7V5a3 3 0 0 1 6 0v2"/>',
  alert:    '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><circle cx="12" cy="17" r="0.6" fill="currentColor"/>',
  cocktail: '<path d="M8 22h8"/><path d="M12 11v11"/><path d="m19 3-7 8-7-8Z"/>',
  wine:     '<path d="M8 22h8"/><path d="M7 10h10"/><path d="M12 15v7"/><path d="M12 15a5 5 0 0 0 5-5c0-2-.5-4-2-8H9c-1.5 4-2 6-2 8a5 5 0 0 0 5 5Z"/>',
  bottle:   '<path d="M10 2h4"/><path d="M11 2v3.5a1.5 1.5 0 0 1-.4 1L9 9a3 3 0 0 0-1 2.2V20a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-8.8a3 3 0 0 0-1-2.2l-1.6-2.5a1.5 1.5 0 0 1-.4-1V2"/>',
  ticket:   '<path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 11v2"/><path d="M13 17v2"/>',
  beef:     '<circle cx="12.5" cy="8.5" r="2.5"/><path d="M12.5 2a6.5 6.5 0 0 0-6.22 4.6c-1.1 3.13-.78 3.9-3.18 6.08A3 3 0 0 0 5 18c4 0 8.4-1.8 11.4-4.8A6.5 6.5 0 0 0 12.5 2Z"/><path d="m18.5 6 2.19 4.5a6.48 6.48 0 0 1 .31 2 6.49 6.49 0 0 1-2.6 5.2C15.4 20.2 11 22 7 22a3 3 0 0 1-2.68-1.66L2.4 16.5"/>',
  sun:      '<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>',
  moon:     '<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79Z"/>',
  leaf:     '<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19.2 2.96a1 1 0 0 1 1.5.55c.16.5.2 1.04.13 1.55C20 16 17 21.16 11 21.16"/><path d="M2 21c0-3 1.85-5.36 5.08-6"/>',
  utensils: '<path d="M3 2v7c0 1.1.9 2 2 2h2V2"/><path d="M7 2v20"/><path d="M19 2c-1.7 0-3 1.5-3 4v6a2 2 0 0 0 2 2h1v8"/>',
  sparkles: '<path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3Z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/>',
  salad:    '<path d="M7 21h10"/><path d="M12 21a9 9 0 0 0 9-9H3a9 9 0 0 0 9 9Z"/><path d="M11.5 12a2.5 2.5 0 1 1-3-2.4 2.5 2.5 0 0 1 3.4-2.9 2.5 2.5 0 0 1 4-1.1 2.5 2.5 0 0 1 4 3.4 2.5 2.5 0 0 1-1.6 4"/>',
  back:     '<path d="m15 18-6-6 6-6"/>',
  chev:     '<path d="m6 9 6 6 6-6"/>',
  close:    '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
  check:    '<path d="M20 6 9 17l-5-5"/>',
};

function PreorderIcon({ name, size = 18, strokeWidth = 1.8, className }) {
  const d = PRE_ORDER_ICON_PATHS[name];
  if (!d) return null;
  return (
    <svg className={"pp-preorder-icon " + (className || '')} width={size} height={size}
         viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={strokeWidth}
         strokeLinecap="round" strokeLinejoin="round"
         dangerouslySetInnerHTML={{ __html: d }}/>
  );
}

// ─────────── glyph + tint helpers ───────────
const TINT_ROTATION = ['cream', 'peach', 'pink', 'mint', 'lavender', 'sky'];

function menuGlyph(menu) {
  const n = (menu.name || '').toLowerCase();
  if (/(steak|beef)/.test(n))      return { icon: 'beef',     tint: 'peach' };
  if (/(morning|breakfast)/.test(n))return { icon: 'sun',      tint: 'cream' };
  if (/(evening|night)/.test(n))   return { icon: 'moon',     tint: 'lavender' };
  if (/mocktail/.test(n))          return { icon: 'leaf',     tint: 'sky' };
  if (/cocktail/.test(n))          return { icon: 'sparkles', tint: 'pink' };
  if (/(brunch)/.test(n))          return { icon: 'sun',      tint: 'cream' };
  if (/(2 course|salad|light)/.test(n)) return { icon: 'salad', tint: 'mint' };
  if (/(set|dinner|course|menu)/.test(n)) return { icon: 'utensils', tint: 'mint' };
  // deterministic fallback so a venue's menus don't all look identical
  const h = hashString(menu.id || menu.name || '');
  return { icon: 'utensils', tint: TINT_ROTATION[h % TINT_ROTATION.length] };
}

function packageGlyph(pkg) {
  const n = (pkg.name || '').toLowerCase();
  if (/mocktail/.test(n))                  return { icon: 'cocktail', tint: 'peach' };
  if (/cocktail/.test(n))                  return { icon: 'cocktail', tint: 'cream' };
  if (/(prosecco|champagne|sparkling)/.test(n)) return { icon: 'bottle', tint: 'pink' };
  if (/(ros[eé]|whispering)/.test(n))      return { icon: 'wine',     tint: 'pink' };
  if (/(wine)/.test(n))                    return { icon: 'wine',     tint: 'lavender' };
  if (/(lager|beer|ipa)/.test(n))          return { icon: 'bottle',   tint: 'cream' };
  if (/spirit/.test(n))                    return { icon: 'bottle',   tint: 'peach' };
  if (/(voucher|guestlist|entry|hen|party)/.test(n)) return { icon: 'ticket', tint: 'mint' };
  if (/(martini|tree)/.test(n))            return { icon: 'cocktail', tint: 'pink' };
  const h = hashString(pkg.id || pkg.name || '');
  return { icon: 'ticket', tint: TINT_ROTATION[h % TINT_ROTATION.length] };
}

function hashString(s) {
  let h = 0;
  for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
  return h;
}

// Operators routinely embed cosmetic prefixes — "3P 2026 -", "🍷 2026 Third
// Party -", emoji etc. Strip them so titles read cleanly.
function cleanLabel(s) {
  if (!s) return s;
  let out = String(s).replace(/[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}]/gu, '').trim();
  out = out.replace(/^\s*3p\s*[-–·:]\s*/i, '');
  out = out.replace(/^\s*\d{4}\s*[-–·:]?\s*/i, '');
  out = out.replace(/^\s*third\s*party\s*[-–·:]?\s*/i, '');
  out = out.replace(/^\s*3p\s+\d{4}\s*[-–·:]?\s*/i, '');
  out = out.replace(/^\s*[-–·:]\s*/, '');
  out = out.replace(/\s+/g, ' ').trim();
  return out || s;
}

function menuTag(menu) {
  const n = (menu.name || '').toLowerCase();
  if (/morning/.test(n))     return '2026 · Brunch';
  if (/evening/.test(n))     return '2026 · Brunch';
  if (/(cocktail|mocktail|masterclass)/.test(n)) return '2026 · Experience';
  if (/(2 course|3 course|set menu)/.test(n))    return '2026 · Set menu';
  if (/steak/.test(n))       return '2026 · Signature';
  return '2026 · Menu';
}

function deriveCourses(menu) {
  // Prefer the actual subTypes present on the menu's items.
  const subs = new Set();
  for (const it of (menu.items || [])) {
    const sub = it.subType || it.type;
    if (sub) subs.add(sub);
  }
  const list = [...subs];
  if (list.length > 0) return list.slice(0, 4);
  // Fall back to a couple of generic words derived from the menu name.
  const n = (menu.name || '').toLowerCase();
  if (/3\s*course/.test(n)) return ['Starter', 'Main', 'Dessert'];
  if (/2\s*course/.test(n)) return ['2 courses'];
  if (/brunch/.test(n))     return ['Brunch', 'Drinks'];
  return ['Menu'];
}

function groupMenuItems(items) {
  const buckets = new Map();
  for (const it of (items || [])) {
    const label = it.subType || it.type || 'Items';
    if (!buckets.has(label)) buckets.set(label, []);
    buckets.get(label).push(it);
  }
  return [...buckets.entries()].map(([label, list]) => ({
    label,
    items: list.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0)),
  }));
}

function Field({ label, children }) {
  return (
    <label className="pp-field">
      <span className="pp-field-label">{label}</span>
      {children}
    </label>
  );
}

function SegmentedRadio({ value, onChange, options }) {
  return (
    <div className="pp-segmented" role="radiogroup">
      {options.map(o => (
        <button key={o.id} role="radio" aria-checked={value === o.id}
                className={"pp-segmented-btn" + (value === o.id ? " is-active" : "")}
                onClick={() => onChange(o.id)}>
          {o.label}
        </button>
      ))}
    </div>
  );
}

function ConfirmedView({ slot, customer, email }) {
  const venue = venueById(slot.venueId);
  const product = productById(slot.productId);
  const isRequest = slot.type === 'request';
  return (
    <div className="pp-confirmed">
      <div className="pp-confirmed-mark"><IconCheck size={28}/></div>
      <div className="pp-confirmed-title">{isRequest ? 'Enquiry sent' : 'Booking confirmed'}</div>
      <div className="pp-confirmed-sub">
        {isRequest
          ? <span>{customer}'s request for {venue.name} — {product.name}, {fmtDate(slot.date)} at {slot.time} is awaiting operator approval.</span>
          : <span>{customer} is booked at {venue.name} — {product.name}, {fmtDate(slot.date)} at {slot.time}.</span>}
      </div>
      <div className="pp-confirmed-meta">
        <span className="pp-muted">Reference</span>
        <span className="pp-mono">{(isRequest ? 'REQ-' : 'BKL-') + (Math.floor(Math.random()*900000)+100000)}</span>
      </div>
      <div className="pp-confirmed-actions">
        <button className="pp-btn pp-btn--ghost"><IconMail size={14}/> Resend to {email}</button>
      </div>
    </div>
  );
}

function fmtDate(iso) {
  const d = new Date(iso);
  return d.toLocaleDateString('en-GB', { weekday:'short', day:'numeric', month:'short', year:'numeric' });
}

// Strip HTML tags + common entities from Bookable's HTML-flavoured strings
// so they render as readable plain text inside the drawer.
function stripHtml(s) {
  if (!s) return '';
  return String(s)
    .replace(/<\s*br\s*\/?>/gi, ' ')
    .replace(/<\/(p|div|li|ul|ol|em|strong|span|a)>/gi, ' ')
    .replace(/<[^>]+>/g, '')
    .replace(/&nbsp;/g, ' ')
    .replace(/&amp;/g, '&')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/\s+/g, ' ')
    .trim();
}

Object.assign(window, {
  BookingDrawer, fmtDate, Field, SegmentedRadio,
  StepCard, StepIndicator, DetailsForm,
  stripHtml, cleanLabel, menuGlyph, packageGlyph,
});
