import React, { useEffect, useId, useMemo, useRef, useState } from 'react';
import {
  fetchLocations,
  fetchCategories,
  fetchServices,
  fetchExtras,
  fetchAgents,
  fetchFormFields,
  createBooking,
  startWooCheckout,
  startStripeCheckout,
  startPaypalCheckout,
} from './api';
import useBookingFormDesign from '../hooks/useBookingFormDesign';
import StepLocation from './steps/StepLocation';
import StepCategory from './steps/StepCategory';
import StepService from './steps/StepService';
import StepExtras from './steps/StepExtras';
import StepAgent from './steps/StepAgent';
import StepDateTime from './steps/StepDateTime';
import StepCustomer from './steps/StepCustomer';
import StepPayment from './steps/StepPayment';
import StepReview from './steps/StepReview';
import StepConfirmation from './steps/StepConfirmation';
import useBpFrontSettings from '../hooks/useBpFrontSettings';
import { formatMoney } from './money';
import { trapFocusWithin } from '../../shared/focusTrap';

const REQUIRED_STEP_ORDER = [
  'location',
  'category',
  'service',
  'extras',
  'agent',
  'datetime',
  'customer',
  'review',
  'payment',
  'confirmation',
];

const DEFAULT_STEPS = [
  { key: 'location', title: 'Location Selection', icon: 'locations.svg' },
  { key: 'category', title: 'Choose Category', icon: 'categories.svg' },
  { key: 'service', title: 'Choose Service', icon: 'services.svg' },
  { key: 'extras', title: 'Service Extras', icon: 'service-extras.svg' },
  { key: 'agent', title: 'Choose Agent', icon: 'agents.svg' },
  { key: 'datetime', title: 'Choose Date & Time', icon: 'calendar.svg' },
  { key: 'customer', title: 'Customer Information', icon: 'customers.svg' },
  { key: 'review', title: 'Review Order', icon: 'bookings.svg' },
  { key: 'payment', title: 'Payment', icon: 'payment.svg' },
  { key: 'confirmation', title: 'Confirm', icon: 'logo.png' },
];

const CANONICAL_ORDER = REQUIRED_STEP_ORDER.slice();
const REQUIRED_KEYS = new Set(['service', 'agent', 'datetime', 'customer', 'review', 'confirmation']);
const SHORTCODE_SKIPPABLE_KEYS = new Set(['location', 'category', 'service', 'extras', 'agent']);

function normalizeKey(k) {
  if (!k) return '';
  if (k === 'agents') return 'agent';
  if (k === 'agent') return 'agent';
  if (k === 'done' || k === 'confirm') return 'confirmation';
  if (k === 'confirmation') return 'confirmation';
  return k;
}

function toBoolEnabled(v) {
  return v !== false && v !== 0 && v !== '0';
}

function parseOptionalBool(value) {
  if (value === true || value === false) return value;
  if (value == null) return null;
  const normalized = String(value).trim().toLowerCase();
  if (!normalized) return null;
  if (['1', 'true', 'yes', 'on'].includes(normalized)) return true;
  if (['0', 'false', 'no', 'off'].includes(normalized)) return false;
  return null;
}

function toPositiveInt(value) {
  const num = Number(value);
  if (!Number.isFinite(num)) return 0;
  const whole = Math.trunc(num);
  return whole > 0 ? whole : 0;
}

function sanitizeColor(value) {
  if (typeof value !== 'string') return '';
  const color = value.trim();
  if (!color) return '';
  if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color)) return color;
  if (/^rgba?\(\s*[\d.\s,%]+\s*\)$/i.test(color)) return color;
  if (/^hsla?\(\s*[\d.\s,%]+\s*\)$/i.test(color)) return color;
  return '';
}

function normalizeIdList(value) {
  if (!Array.isArray(value)) return [];
  const unique = new Set();
  value.forEach((item) => {
    const id = toPositiveInt(item);
    if (id > 0) unique.add(id);
  });
  return Array.from(unique);
}

function normalizeSkipKey(key) {
  const normalized = normalizeKey(String(key || '').trim().toLowerCase());
  return SHORTCODE_SKIPPABLE_KEYS.has(normalized) ? normalized : '';
}

function canShortcodeSkip(stepKey, preselect) {
  switch (stepKey) {
    case 'location':
      return preselect.locationId > 0;
    case 'category':
      return preselect.categoryIds.length > 0;
    case 'service':
      return preselect.serviceId > 0;
    case 'agent':
      return preselect.agentId > 0;
    case 'extras':
      return true;
    default:
      return false;
  }
}

function normalizeWidgetOptions(rawOptions) {
  const options = rawOptions && typeof rawOptions === 'object' ? rawOptions : {};
  const preselectRaw = options.preselect && typeof options.preselect === 'object' ? options.preselect : {};
  const widgetRaw = options.widget && typeof options.widget === 'object' ? options.widget : {};

  const preselect = {
    locationId: toPositiveInt(preselectRaw.locationId),
    categoryIds: normalizeIdList(preselectRaw.categoryIds),
    serviceId: toPositiveInt(preselectRaw.serviceId),
    agentId: toPositiveInt(preselectRaw.agentId),
  };

  const requestedSkip = Array.isArray(options.skipSteps) ? options.skipSteps : [];
  const skipUnique = new Set();
  requestedSkip.forEach((item) => {
    const key = normalizeSkipKey(item);
    if (!key) return;
    if (!canShortcodeSkip(key, preselect)) return;
    skipUnique.add(key);
  });

  const widget = {};
  const primaryColor = sanitizeColor(widgetRaw.primaryColor);
  if (primaryColor) widget.primaryColor = primaryColor;

  const overlayColor = sanitizeColor(widgetRaw.overlayColor);
  if (overlayColor) widget.overlayColor = overlayColor;

  const backgroundColor = sanitizeColor(widgetRaw.backgroundColor);
  if (backgroundColor) widget.backgroundColor = backgroundColor;

  const sideBackgroundColor = sanitizeColor(widgetRaw.sideBackgroundColor);
  if (sideBackgroundColor) widget.sideBackgroundColor = sideBackgroundColor;

  const mainBackgroundColor = sanitizeColor(widgetRaw.mainBackgroundColor);
  if (mainBackgroundColor) widget.mainBackgroundColor = mainBackgroundColor;

  const borderRadius = toPositiveInt(widgetRaw.borderRadius);
  if (borderRadius > 0) widget.borderRadius = Math.min(80, borderRadius);

  const maxWidth = toPositiveInt(widgetRaw.maxWidth);
  if (maxWidth >= 420) widget.maxWidth = Math.min(1800, maxWidth);

  const showLeftPanel = parseOptionalBool(widgetRaw.showLeftPanel);
  if (showLeftPanel !== null) widget.showLeftPanel = showLeftPanel;

  const showSummary = parseOptionalBool(widgetRaw.showSummary);
  if (showSummary !== null) widget.showSummary = showSummary;

  return {
    preselect,
    skipSteps: Array.from(skipUnique),
    widget,
  };
}

function resolveSummaryVisibility(rawLayoutValue) {
  const bool = parseOptionalBool(rawLayoutValue);
  if (bool !== null) return bool;
  return true;
}

function getRestBase() {
  const url = window.pointlybooking_FRONT?.restUrl || '/wp-json/pointly-booking/v1';
  return url.replace(/\/$/, '');
}

function extractManageKey(maybeUrl) {
  if (!maybeUrl) return '';
  try {
    const parsed = new URL(maybeUrl, window.location.origin);
    return parsed.searchParams.get('key') || '';
  } catch (e) {
    return '';
  }
}

async function paypalCapture(orderId, bookingId, bookingKey) {
  const r = await fetch(`${getRestBase()}/front/payments/paypal/capture`, {
    method: 'POST',
    credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ order_id: orderId, booking_id: bookingId, key: bookingKey }),
  });
  const j = await r.json().catch(() => ({}));
  if (!r.ok) throw new Error(j?.message || 'PayPal capture failed');
  return j;
}

async function fetchBookingStatus(id, bookingKey) {
  const params = new URLSearchParams({ _t: String(Date.now()) });
  if (bookingKey) params.set('key', bookingKey);

  const r = await fetch(`${getRestBase()}/front/bookings/${id}/status?${params.toString()}`, {
    credentials: 'same-origin',
    headers: { 'X-WP-Nonce': window.pointlybooking_FRONT?.nonce || '' },
  });
  const j = await r.json().catch(() => ({}));
  if (!r.ok || !j?.success) throw new Error(j?.message || 'Could not load booking status');
  return j.booking;
}

async function waitForConfirmed(bookingId, bookingKey) {
  let last = null;
  for (let i = 0; i < 10; i++) {
    const b = await fetchBookingStatus(bookingId, bookingKey);
    last = b;
    if (b?.status === 'confirmed' && b?.payment_status === 'paid') return b;
    await new Promise((res) => setTimeout(res, 1000));
  }
  return last;
}

function clearPaymentQuery() {
  const url = new URL(window.location.href);
  url.searchParams.delete('pointlybooking_payment');
  url.searchParams.delete('booking_id');
  url.searchParams.delete('token');
  url.searchParams.delete('key');
  window.history.replaceState({}, '', url.toString());
}

function buildSteps(designConfig) {
  const baseMap = new Map(DEFAULT_STEPS.map((s) => [s.key, s]));
  const raw = Array.isArray(designConfig?.steps) ? designConfig.steps : [];

  const ordered = [];
  const seen = new Set();

  for (const s of raw) {
    const key = normalizeKey(s?.key);
    if (!key) continue;
    if (seen.has(key)) continue;
    seen.add(key);
    ordered.push({
      key,
      enabled: toBoolEnabled(s?.enabled),
      title: s?.title,
      subtitle: s?.subtitle,
      image: s?.image,
      buttonBackLabel: s?.buttonBackLabel,
      buttonNextLabel: s?.buttonNextLabel,
      accentOverride: s?.accentOverride,
      showLeftPanel: s?.showLeftPanel,
      showHelpBox: s?.showHelpBox,
    });
  }

  for (const d of DEFAULT_STEPS) {
    if (!seen.has(d.key)) {
      ordered.push({ key: d.key, enabled: true });
    }
  }

  const byKey = new Map(ordered.map((s) => [s.key, s]));

  const list = CANONICAL_ORDER
    .filter((k) => byKey.has(k))
    .map((key) => {
      const cfg = byKey.get(key) || {};
      const base = baseMap.get(key) || { key, title: key, icon: 'services.svg' };
      return {
        ...base,
        key,
        enabled: REQUIRED_KEYS.has(key) ? true : toBoolEnabled(cfg.enabled),
        title: (cfg.title != null && String(cfg.title).trim() !== '') ? String(cfg.title) : base.title,
        subtitle: (cfg.subtitle != null) ? String(cfg.subtitle) : '',
        icon: cfg.image || base.icon,
        image: cfg.image || base.icon,
        buttonBackLabel: cfg.buttonBackLabel,
        buttonNextLabel: cfg.buttonNextLabel,
        accentOverride: cfg.accentOverride,
        showLeftPanel: cfg.showLeftPanel,
        showHelpBox: cfg.showHelpBox,
      };
    })
    .filter((s) => !!s && (s.enabled !== false));

  return list;
}

export default function WizardModal({ open, onClose, brand, widgetOptions }) {
  const dialogTitleId = useId();
  const mobileSummaryPanelId = useId();
  const closeButtonRef = useRef(null);
  const restoreFocusRef = useRef(null);
  const modalRef = useRef(null);
  const mainRef = useRef(null);
  const headingRef = useRef(null);
  const [stepIndex, setStepIndex] = useState(0);
  const [mobileSummaryOpen, setMobileSummaryOpen] = useState(false);
  const { config: designConfig, loading: designLoading, error: designError } = useBookingFormDesign(open);
  const shortcodeOptions = useMemo(() => normalizeWidgetOptions(widgetOptions), [widgetOptions]);
  const preselect = shortcodeOptions.preselect;
  const skipSteps = shortcodeOptions.skipSteps;
  const widgetStyleOptions = shortcodeOptions.widget;
  const skipStepsSig = skipSteps.join('|');
  const forcedSkipSet = useMemo(() => new Set(skipSteps), [skipStepsSig]);
  const baseSteps = useMemo(() => buildSteps(designConfig), [designConfig]);
  const { settings: bpSettings } = useBpFrontSettings(open);

  const [locationId, setLocationId] = useState(null);
  const [categoryIds, setCategoryIds] = useState([]);
  const [serviceId, setServiceId] = useState(null);
  const [extraIds, setExtraIds] = useState([]);
  const [agentId, setAgentId] = useState(null);
  const [date, setDate] = useState(null);
  const [slot, setSlot] = useState(null);
  const [paymentMethod, setPaymentMethod] = useState('');
  const [paymentBookingId, setPaymentBookingId] = useState(0);
  const [bookingId, setBookingId] = useState(null);
  const [bookingKey, setBookingKey] = useState('');
  const [isCreatingBooking, setIsCreatingBooking] = useState(false);
  const [createError, setCreateError] = useState(null);
  const [returnState, setReturnState] = useState({ mode: '', bookingId: 0, token: '', key: '', action: '' });
  const [returnLoading, setReturnLoading] = useState(false);
  const [returnError, setReturnError] = useState('');
  const [confirmData, setConfirmData] = useState(null);
  const [confirmInfo, setConfirmInfo] = useState(null);
  const [confirmLoading, setConfirmLoading] = useState(false);
  const [confirmError, setConfirmError] = useState('');

  const [formFields, setFormFields] = useState({ form: [], customer: [], booking: [] });
  const [answers, setAnswers] = useState({});

  const [locations, setLocations] = useState([]);
  const [categories, setCategories] = useState([]);
  const [services, setServices] = useState([]);
  const [extras, setExtras] = useState([]);
  const [agents, setAgents] = useState([]);

  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  const paymentsActive = bpSettings ? bpSettings.payments_enabled !== 0 : false;
  const steps = useMemo(() => {
    let nextSteps = baseSteps;

    if (!paymentsActive) {
      nextSteps = nextSteps.filter((s) => s.key !== 'payment');
    }
    if (skipSteps.length) {
      const skipSet = new Set(skipSteps);
      nextSteps = nextSteps.filter((s) => !skipSet.has(s.key));
    }

    return nextSteps;
  }, [baseSteps, paymentsActive, skipStepsSig]);
  const hasPaymentStep = useMemo(() => steps.some((s) => s.key === 'payment'), [steps]);
  const hasLocationStep = useMemo(() => steps.some((s) => s.key === 'location'), [steps]);
  const hasExtrasStep = useMemo(() => steps.some((s) => s.key === 'extras'), [steps]);
  const hasServiceStep = useMemo(() => steps.some((s) => s.key === 'service'), [steps]);
  const hasAgentStep = useMemo(() => steps.some((s) => s.key === 'agent'), [steps]);
  const shouldLoadLocations = hasLocationStep || forcedSkipSet.has('location') || preselect.locationId > 0;
  const showLocationSummary = hasLocationStep || !!locationId;
  const showExtrasSummary = hasExtrasStep || extraIds.length > 0;
  const step = steps[stepIndex] || steps[0] || DEFAULT_STEPS[0];
  const stepNumber = Math.min(stepIndex + 1, steps.length || 1);
  const progressPercent = steps.length ? Math.round((stepNumber / steps.length) * 100) : 0;
  const stepDescription = step?.subtitle || 'Please complete the steps to schedule your booking.';

  const themePrimary = widgetStyleOptions?.primaryColor || designConfig?.appearance?.primaryColor || '';
  const accent = (step?.accentOverride && String(step.accentOverride).trim() !== '')
    ? String(step.accentOverride).trim()
    : (themePrimary && String(themePrimary).trim() !== '' ? String(themePrimary).trim() : '');
  const overlayStyle = useMemo(() => {
    const style = {};
    if (widgetStyleOptions?.overlayColor) {
      style['--bp-overlay-bg'] = widgetStyleOptions.overlayColor;
    }
    return style;
  }, [widgetStyleOptions?.overlayColor]);
  const modalStyle = useMemo(() => {
    const style = {};
    if (accent) style['--bp-accent'] = accent;
    if (widgetStyleOptions?.backgroundColor) style['--bp-modal-bg'] = widgetStyleOptions.backgroundColor;
    if (widgetStyleOptions?.sideBackgroundColor) style['--bp-side-bg'] = widgetStyleOptions.sideBackgroundColor;
    if (widgetStyleOptions?.mainBackgroundColor) style['--bp-main-bg'] = widgetStyleOptions.mainBackgroundColor;
    if (widgetStyleOptions?.borderRadius) style['--bp-modal-radius'] = `${widgetStyleOptions.borderRadius}px`;
    if (widgetStyleOptions?.maxWidth) style['--bp-modal-max-width'] = `${widgetStyleOptions.maxWidth}px`;
    const borderStyle = String(designConfig?.appearance?.borderStyle || '').toLowerCase();
    if (borderStyle === 'square' && !widgetStyleOptions?.borderRadius) style['--bp-modal-radius'] = '0px';
    return style;
  }, [
    accent,
    designConfig?.appearance?.borderStyle,
    widgetStyleOptions?.backgroundColor,
    widgetStyleOptions?.sideBackgroundColor,
    widgetStyleOptions?.mainBackgroundColor,
    widgetStyleOptions?.borderRadius,
    widgetStyleOptions?.maxWidth,
  ]);

  const iconUrl = useMemo(() => {
    if (step?.imageUrl) return step.imageUrl;
    const file = String(step?.icon || step?.image || 'services.svg').trim();
    if (!file) return '';
    const isSvg = file.toLowerCase().endsWith('.svg');
    const base = isSvg ? (brand?.iconsBase || '') : (brand?.imagesBase || '');
    if (!base) return '';

    const url = base + file;
    const v = String(isSvg ? window.pointlybooking_FRONT?.iconsBuild : window.pointlybooking_FRONT?.imagesBuild || '').trim();
    if (!v) return url;
    const sep = url.includes('?') ? '&' : '?';
    return `${url}${sep}v=${encodeURIComponent(v)}`;
  }, [brand, step]);
  const helpTitle = designConfig?.texts?.helpTitle || 'Need help?';
  const helpPhone = designConfig?.texts?.helpPhone || designConfig?.layout?.helpPhone || brand?.helpPhone || '';
  const layoutShowSummary = resolveSummaryVisibility(designConfig?.layout?.showSummary);
  const showLeft = widgetStyleOptions?.showLeftPanel != null
    ? widgetStyleOptions.showLeftPanel
    : step?.showLeftPanel !== false;
  const showSummary = widgetStyleOptions?.showSummary != null
    ? widgetStyleOptions.showSummary
    : layoutShowSummary;
  const showHelp = step?.showHelpBox !== false;
  const modalGridClassName = showLeft && showSummary
    ? 'bp-modal-grid'
    : showLeft && !showSummary
      ? 'bp-modal-grid bp-modal-grid--left-main'
      : !showLeft && showSummary
        ? 'bp-modal-grid bp-modal-grid--main-summary'
        : 'bp-modal-grid bp-modal-grid--main-only';
  const globalNextLabel = designConfig?.texts?.nextLabel || 'Next ->';
  const globalBackLabel = designConfig?.texts?.backLabel || '<- Back';
  const labels = useMemo(() => ({
    next: (step?.buttonNextLabel != null && String(step.buttonNextLabel).trim() !== '') ? String(step.buttonNextLabel) : globalNextLabel,
    back: (step?.buttonBackLabel != null && String(step.buttonBackLabel).trim() !== '') ? String(step.buttonBackLabel) : globalBackLabel,
  }), [step?.buttonNextLabel, step?.buttonBackLabel, globalNextLabel, globalBackLabel]);
  const paymentLabelMap = {
    free: 'Free (no payment)',
    cash: 'Pay at location (Cash)',
    woocommerce: 'Pay with WooCommerce',
    stripe: 'Pay with Stripe',
    paypal: 'Pay with PayPal',
  };
  const totalAmount = useMemo(() => {
    const svc = services.find((x) => String(x.id) === String(serviceId));
    const svcPrice = svc?.price != null ? Number(svc.price) : 0;
    const selectedExtras = extras.filter((extra) => (
      extraIds.includes(extra.id) || extraIds.includes(String(extra.id))
    ));
    const extrasPrice = selectedExtras.reduce((sum, item) => (
      sum + (item?.price != null ? Number(item.price) : 0)
    ), 0);
    return svcPrice + extrasPrice;
  }, [services, serviceId, extras, extraIds]);
  const summaryData = useMemo(() => buildSummaryData({
    settings: bpSettings,
    locations,
    categories,
    services,
    extras,
    agents,
    locationId,
    categoryIds,
    serviceId,
    extraIds,
    agentId,
  }), [bpSettings, locations, categories, services, extras, agents, locationId, categoryIds, serviceId, extraIds, agentId]);
  const mobileSummaryPreview = useMemo(() => {
    const parts = [];
    if (summaryData.svc?.name) {
      parts.push(summaryData.svc.name);
    }
    if (date && slot?.start_time) {
      parts.push(`${date} ${slot.start_time}`);
    } else if (date) {
      parts.push(date);
    }
    if (summaryData.totalLabel !== '-') {
      parts.push(summaryData.totalLabel);
    }
    return parts.length ? parts.join(' · ') : 'Tap to review your booking details';
  }, [summaryData, date, slot]);
  const mobileSummaryPreviewLabel = useMemo(
    () => String(mobileSummaryPreview || '').replace(/\s*[\u00c2\u00b7]+\s*/g, ' | '),
    [mobileSummaryPreview]
  );

  useEffect(() => {
    if (!open) return;
    restoreFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
    document.body.classList.add('bp-modal-open');
    const frame = window.requestAnimationFrame(() => {
      closeButtonRef.current?.focus?.();
    });
    const handleKeyDown = (event) => {
      if (event.key === 'Escape') {
        event.preventDefault();
        onClose?.();
        return;
      }

      trapFocusWithin(event, modalRef.current, closeButtonRef.current);
    };
    document.addEventListener('keydown', handleKeyDown);
    return () => {
      document.body.classList.remove('bp-modal-open');
      document.removeEventListener('keydown', handleKeyDown);
      window.cancelAnimationFrame(frame);
      restoreFocusRef.current?.focus?.();
    };
  }, [open, onClose]);

  useEffect(() => {
    if (!open) return;
    setStepIndex(0);
    setMobileSummaryOpen(false);
    setError('');
    setLocationId(preselect.locationId || null);
    setCategoryIds(preselect.categoryIds || []);
    setServiceId(preselect.serviceId || null);
    setExtraIds([]);
    setAgentId(preselect.agentId || null);
    setDate(null);
    setSlot(null);
    setPaymentMethod('');
    setPaymentBookingId(0);
    setBookingId(null);
    setBookingKey('');
    setIsCreatingBooking(false);
    setCreateError(null);
    setReturnState({ mode: '', bookingId: 0, token: '', key: '', action: '' });
    setReturnLoading(false);
    setReturnError('');
    setConfirmData(null);
    setAnswers({});
  }, [open, preselect.locationId, preselect.serviceId, preselect.agentId, preselect.categoryIds]);

  useEffect(() => {
    if (!open || !showSummary) return;
    setMobileSummaryOpen(step.key === 'review' || step.key === 'payment' || step.key === 'confirmation');
  }, [open, showSummary, step.key]);

  useEffect(() => {
    if (!open) return;
    const frame = window.requestAnimationFrame(() => {
      if (mainRef.current) {
        if (typeof mainRef.current.scrollTo === 'function') {
          mainRef.current.scrollTo({ top: 0, left: 0, behavior: stepIndex > 0 ? 'smooth' : 'auto' });
        } else {
          mainRef.current.scrollTop = 0;
        }
      }
      headingRef.current?.focus?.({ preventScroll: true });
    });
    return () => window.cancelAnimationFrame(frame);
  }, [open, stepIndex]);

  useEffect(() => {
    if (!open) return;
    if (!hasServiceStep && !serviceId && services.length === 1) {
      setServiceId(services[0].id);
    }
  }, [open, hasServiceStep, serviceId, services]);

  useEffect(() => {
    if (!open) return;
    if (!hasAgentStep && !agentId && agents.length === 1) {
      setAgentId(agents[0].id);
    }
  }, [open, hasAgentStep, agentId, agents]);

  useEffect(() => {
    if (!open) return;
    if (!locations.length) return;

    const hasSelected = !!locationId && locations.some((item) => String(item.id) === String(locationId));
    if (hasSelected) return;

    if (forcedSkipSet.has('location')) {
      const preferredValid = preselect.locationId > 0
        ? locations.find((item) => String(item.id) === String(preselect.locationId))
        : null;
      const nextLocation = preferredValid?.id || locations[0]?.id || null;
      if (nextLocation && String(nextLocation) !== String(locationId || '')) {
        setLocationId(nextLocation);
      }
      return;
    }

    if (locationId) setLocationId(null);
  }, [open, locations, locationId, forcedSkipSet, preselect.locationId]);

  useEffect(() => {
    if (!open) return;
    if (!categories.length) return;

    const validSet = new Set(categories.map((item) => String(item.id)));
    const current = normalizeIdList(categoryIds);
    const validCurrent = current.filter((id) => validSet.has(String(id)));

    if (forcedSkipSet.has('category')) {
      if (validCurrent.length) {
        if (validCurrent.length !== current.length) {
          setCategoryIds(validCurrent);
        }
        return;
      }

      const validPreferred = (preselect.categoryIds || []).filter((id) => validSet.has(String(id)));
      const fallback = validPreferred.length ? validPreferred : (categories[0]?.id ? [categories[0].id] : []);
      if (fallback.length) {
        setCategoryIds(fallback);
      }
      return;
    }

    if (current.length && validCurrent.length !== current.length) {
      setCategoryIds(validCurrent);
    }
  }, [open, categories, categoryIds, forcedSkipSet, preselect.categoryIds]);

  useEffect(() => {
    if (!open) return;
    if (!services.length) return;

    const hasSelected = !!serviceId && services.some((item) => String(item.id) === String(serviceId));
    if (hasSelected) return;

    if (forcedSkipSet.has('service')) {
      const preferredValid = preselect.serviceId > 0
        ? services.find((item) => String(item.id) === String(preselect.serviceId))
        : null;
      const nextService = preferredValid?.id || services[0]?.id || null;
      if (nextService && String(nextService) !== String(serviceId || '')) {
        setServiceId(nextService);
      }
      return;
    }

    if (serviceId) setServiceId(null);
  }, [open, services, serviceId, forcedSkipSet, preselect.serviceId]);

  useEffect(() => {
    if (!open) return;
    if (!agents.length) return;

    const hasSelected = !!agentId && agents.some((item) => String(item.id) === String(agentId));
    if (hasSelected) return;

    if (forcedSkipSet.has('agent')) {
      const preferredValid = preselect.agentId > 0
        ? agents.find((item) => String(item.id) === String(preselect.agentId))
        : null;
      const nextAgent = preferredValid?.id || agents[0]?.id || null;
      if (nextAgent && String(nextAgent) !== String(agentId || '')) {
        setAgentId(nextAgent);
      }
      return;
    }

    if (agentId) setAgentId(null);
  }, [open, agents, agentId, forcedSkipSet, preselect.agentId]);

  useEffect(() => {
    if (!open) return;
    if (!paymentsActive) {
      setPaymentMethod('cash');
      return;
    }
    if (!paymentMethod && bpSettings?.payments_default_method) {
      setPaymentMethod(bpSettings.payments_default_method);
    }
  }, [open, bpSettings, paymentMethod, paymentsActive]);

  useEffect(() => {
    if (!open) return;

    const params = new URLSearchParams(window.location.search);
    const bpPayment = params.get('pointlybooking_payment');
    const token = params.get('token') || '';
    const bookingId = Number(params.get('booking_id') || 0);
    const key = params.get('key') || '';

    const stripeFlag = bpPayment === 'stripe_success' || bpPayment === 'stripe_cancel';
    const paypalFlag = bpPayment === 'paypal_return' || bpPayment === 'paypal_cancel';

    if (!bookingId) return;

    if (stripeFlag) {
      setReturnState({
        mode: 'stripe',
        bookingId,
        token: '',
        key,
        action: bpPayment === 'stripe_cancel' ? 'cancel' : 'success',
      });
    } else if (paypalFlag) {
      setReturnState({
        mode: 'paypal',
        bookingId,
        token,
        key,
        action: bpPayment === 'paypal_cancel' ? 'cancel' : 'success',
      });
    }
  }, [open]);

  useEffect(() => {
    if (!open) return;
    if (!returnState.bookingId || !returnState.mode) return;

    let alive = true;
    setReturnLoading(true);
    setReturnError('');

    (async () => {
      try {
        if (returnState.action === 'cancel') {
          await fetch(`${getRestBase()}/front/bookings/${returnState.bookingId}/payment-cancel`, {
            method: 'POST',
            credentials: 'same-origin',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ key: returnState.key }),
          });
          throw new Error('Payment cancelled.');
        }

        if (returnState.mode === 'paypal') {
          if (!returnState.token) {
            await fetch(`${getRestBase()}/front/bookings/${returnState.bookingId}/payment-cancel`, {
              method: 'POST',
              credentials: 'same-origin',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ key: returnState.key }),
            });
            throw new Error('Payment cancelled.');
          }
          await paypalCapture(returnState.token, returnState.bookingId, returnState.key);
        }

        const confirmed = await waitForConfirmed(returnState.bookingId, returnState.key);
        if (!alive) return;

        setBookingId(returnState.bookingId);
        setBookingKey(returnState.key);
        setPaymentBookingId(returnState.bookingId);
        setAnswers((a) => ({ ...a, __booking: { booking_id: returnState.bookingId } }));
        if (confirmed?.status === 'confirmed' && confirmed?.payment_status === 'paid') {
          setConfirmData({ booking: confirmed, paid: true, error: '' });
        } else {
          setConfirmData({
            booking: confirmed,
            paid: false,
            error: '',
            pending: true,
          });
        }
        goToStepKey('confirmation');
      } catch (e) {
        if (!alive) return;
        const message = e?.message || 'Payment failed';
        setReturnError(message);
        setBookingId(returnState.bookingId);
        setBookingKey(returnState.key);
        setPaymentBookingId(returnState.bookingId);
        setAnswers((a) => ({ ...a, __booking: { booking_id: returnState.bookingId } }));
        setConfirmData({ booking: { id: returnState.bookingId }, paid: false, error: message });
        goToStepKey('confirmation');
      } finally {
        if (!alive) return;
        setReturnLoading(false);
        clearPaymentQuery();
      }
    })();

    return () => {
      alive = false;
    };
  }, [open, returnState]);

  useEffect(() => {
    if (error === 'Select a payment method' && paymentMethod) {
      setError('');
    }
  }, [error, paymentMethod]);

  useEffect(() => {
    if (!open) return;
    (async () => {
      try {
        setLoading(true);
        const [locs, cats, fields] = await Promise.all([
          shouldLoadLocations ? fetchLocations() : Promise.resolve([]),
          fetchCategories(),
          fetchFormFields(),
        ]);
        setLocations(locs);
        setCategories(cats);
        setFormFields(fields);
      } catch (e) {
        setError(e?.message || 'Failed to load booking data.');
      } finally {
        setLoading(false);
      }
    })();
  }, [open, shouldLoadLocations]);

  useEffect(() => {
    if (!open) return;
    const keySig = steps.map((s) => s.key).join('|');
    if (!keySig) return;
    setStepIndex(0);
  }, [open, steps]);

  useEffect(() => {
    setStepIndex((i) => {
      if (i < 0) return 0;
      if (i >= steps.length) return Math.max(0, steps.length - 1);
      return i;
    });
  }, [steps.length]);

  const bookingData = answers?.__booking || null;
  const currentStepKey = steps[stepIndex]?.key;

  useEffect(() => {
    if (!open) return;
    if (currentStepKey !== 'confirmation') return;
    if (!bookingId) return;

    let alive = true;
    setConfirmLoading(true);
    setConfirmError('');

    fetchBookingStatus(bookingId, bookingKey)
      .then((b) => alive && setConfirmInfo(b))
      .catch((e) => alive && setConfirmError(e.message || 'Error'))
      .finally(() => alive && setConfirmLoading(false));

    return () => { alive = false; };
  }, [open, currentStepKey, bookingId, bookingKey]);

  useEffect(() => {
    if (!open) return;
    (async () => {
      try {
        if (step.key === 'service' || step.key === 'extras' || step.key === 'agent' || step.key === 'datetime' || step.key === 'review') {
          const svc = await fetchServices({ category_ids: categoryIds, location_id: locationId });
          setServices(svc);
        }
      } catch (e) {
        setServices([]);
      }
    })();
  }, [open, step.key, categoryIds, locationId]);

  useEffect(() => {
    if (!open) return;
    (async () => {
      try {
        if (!serviceId) return;
        const [ex, ag] = await Promise.all([
          hasExtrasStep ? fetchExtras({ service_id: serviceId }) : Promise.resolve([]),
          fetchAgents({ service_id: serviceId, location_id: locationId }),
        ]);
        setExtras(ex);
        setAgents(ag);
      } catch (e) {
        setExtras([]);
        setAgents([]);
      }
    })();
  }, [open, serviceId, locationId, hasExtrasStep]);

  function next() {
    setError('');
    if (step?.key === 'payment' && !paymentMethod) {
      setError('Select a payment method');
      return;
    }
    setStepIndex((i) => Math.min(i + 1, steps.length - 1));
  }

  function back() {
    setError('');
    setStepIndex((i) => Math.max(i - 1, 0));
  }

  function goToStepKey(key) {
    const idx = steps.findIndex((s) => s.key === key);
    if (idx >= 0) setStepIndex(idx);
  }

  const buildPayload = () => {
    const customer_fields = {};
    const booking_fields = {};
    Object.entries(answers || {}).forEach(([k, v]) => {
      if (k.startsWith('customer.')) {
        customer_fields[k.slice(9)] = v;
      } else if (k.startsWith('booking.')) {
        booking_fields[k.slice(8)] = v;
      }
    });
    const settingsCurrency = bpSettings?.currency || window.pointlybooking_FRONT?.currency || 'USD';

    return {
      location_id: locationId,
      category_ids: categoryIds,
      service_id: serviceId,
      extra_ids: hasExtrasStep ? extraIds : [],
      agent_id: agentId,
      date,
      start_time: slot?.start_time || slot?.start || '',
      end_time: slot?.end_time || slot?.end || '',
      field_values: answers,
      customer_fields,
      booking_fields,
      extras: hasExtrasStep ? extraIds : [],
      total_price: totalAmount,
      currency: settingsCurrency,
    };
  };

  const createBookingIfNeeded = async () => {
    if (bookingId) return bookingId;

    setIsCreatingBooking(true);
    setCreateError(null);

    try {
      const payload = buildPayload();
      payload.payment_method = paymentsActive ? (paymentMethod || 'cash') : 'free';

      const res = await fetch(`${getRestBase()}/front/booking/create`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-WP-Nonce': window.pointlybooking_FRONT?.nonce || '',
        },
        credentials: 'same-origin',
        body: JSON.stringify(payload),
      });
      const j = await res.json().catch(() => ({}));
        if (!res.ok || !j?.success) {
          throw new Error(j?.message || 'Could not create booking');
        }

        setBookingId(j.booking_id);
        const createdKey = j.manage_key || extractManageKey(j.manage_url || '');
        if (createdKey) setBookingKey(createdKey);
        return j.booking_id;
    } catch (e) {
      setCreateError(e.message || 'Create booking failed');
      throw e;
    } finally {
      setIsCreatingBooking(false);
    }
  };

  async function submitBooking() {
    try {
      setLoading(true);
      setError('');

      const payload = buildPayload();

      const method = !paymentsActive
        ? 'free'
        : (paymentMethod || bpSettings?.payments_default_method || 'cash');
      payload.payment_method = method;

      if (method === 'woocommerce') {
        const res = await startWooCheckout(payload);
        if (!res?.checkout_url) {
          throw new Error('Checkout URL missing');
        }
        if (res?.manage_key) setBookingKey(res.manage_key);
        window.location.href = res.checkout_url;
        return;
      }

      if (method === 'stripe') {
        const res = await startStripeCheckout(payload);
        if (!res?.checkout_url) {
          throw new Error('Stripe checkout URL missing');
        }
        if (res?.manage_key) setBookingKey(res.manage_key);
        window.location.href = res.checkout_url;
        return;
      }

      if (method === 'paypal') {
        const res = await startPaypalCheckout(payload);
        if (!res?.approve_url) {
          throw new Error('PayPal approve URL missing');
        }
        if (res?.manage_key) setBookingKey(res.manage_key);
        window.location.href = res.approve_url;
        return;
      }

      if (method !== 'cash' && method !== 'free') {
        throw new Error('Unsupported payment method');
      }

      const res = await createBooking(payload);
      setBookingId(res?.booking_id || null);
      const manageKey = res?.manage_key || extractManageKey(res?.manage_url || '');
      if (manageKey) setBookingKey(manageKey);
      setPaymentBookingId(res?.booking_id || 0);
      setConfirmData({
        booking: res,
        paid: method === 'free',
        error: '',
      });
      setAnswers((a) => ({ ...a, __booking: res }));
      const doneIdx = steps.findIndex((s) => s.key === 'confirmation');
      setStepIndex(doneIdx >= 0 ? doneIdx : steps.length - 1);
    } catch (e) {
      setError(e?.message || 'Could not submit booking.');
    } finally {
      setLoading(false);
    }
  }

  if (!open) return null;
  if (designLoading && !designConfig) {
    return <div className="bp-wizard-loading">Loading booking form...</div>;
  }
  if (designError && !designConfig) {
    return <div className="bp-wizard-loading">Wizard error: {designError}</div>;
  }
  if (!steps.length) {
    return <div className="bp-wizard-loading">Wizard config invalid (no steps)</div>;
  }

  return (
    <div
      className="bp-modal-overlay"
      role="dialog"
      aria-modal="true"
      aria-labelledby={dialogTitleId}
      style={overlayStyle}
      onMouseDown={(event) => {
        if (event.target === event.currentTarget) onClose?.();
      }}
    >
      <div ref={modalRef} className="bp-modal" style={modalStyle} onMouseDown={(event) => event.stopPropagation()}>
        <button ref={closeButtonRef} type="button" className="bp-modal-close" onClick={onClose} aria-label="Close">×</button>

        <div className={modalGridClassName}>
          {showLeft ? (
            <aside className="bp-side">
              <div className="bp-side-step">Step {stepNumber} of {steps.length}</div>
              <div className="bp-side-icon">
                {iconUrl ? <img src={iconUrl} alt="" /> : null}
              </div>
              <h3 className="bp-side-title">{step.title}</h3>
              <p className="bp-side-desc">
                {stepDescription}
              </p>
              <div className="bp-side-progress" aria-hidden="true">
                <span style={{ width: `${progressPercent}%` }} />
              </div>

              {showHelp ? (
                <div className="bp-side-help">
                  <div>{helpTitle}</div>
                  <div className="bp-help-phone">{helpPhone}</div>
                </div>
              ) : null}
            </aside>
          ) : null}

          <main ref={mainRef} className="bp-main">
            <div className="bp-main-head">
              <div className="bp-main-head__copy">
                <div className="bp-main-eyebrow">Step {stepNumber} of {steps.length}</div>
                <h2 ref={headingRef} id={dialogTitleId} tabIndex="-1">{step.title}</h2>
                <p className="bp-main-head__desc">{stepDescription}</p>
              </div>
              <div className="bp-progress-card" aria-hidden="true">
                <div className="bp-progress-card__meta">
                  <span>{progressPercent}% complete</span>
                  <span>{steps.length - stepNumber} step{steps.length - stepNumber === 1 ? '' : 's'} left</span>
                </div>
                <div className="bp-progress-bar">
                  <span style={{ width: `${progressPercent}%` }} />
                </div>
                <div className="bp-step-dots">
                  {steps.map((s, idx) => (
                    <span key={s.key} className={idx === stepIndex ? 'bp-dot active' : idx < stepIndex ? 'bp-dot done' : 'bp-dot'} />
                  ))}
                </div>
              </div>
            </div>

            <div className="bp-mobile-context">
              <div className={iconUrl ? 'bp-mobile-intro' : 'bp-mobile-intro bp-mobile-intro--textOnly'}>
                {iconUrl ? (
                  <div className="bp-mobile-intro__icon">
                    <img src={iconUrl} alt="" />
                  </div>
                ) : null}
                <div className="bp-mobile-intro__body">
                  <span className="bp-mobile-intro__eyebrow">Step {stepNumber} of {steps.length}</span>
                  <p className="bp-mobile-intro__desc">
                    {stepDescription}
                  </p>
                  {showHelp ? (
                    <div className="bp-mobile-intro__help">
                      <span>{helpTitle}</span>
                      <strong>{helpPhone || 'Support available during booking hours.'}</strong>
                    </div>
                  ) : null}
                </div>
              </div>

              {showSummary ? (
                <section className={mobileSummaryOpen ? 'bp-mobile-summary is-open' : 'bp-mobile-summary'}>
                  <button
                    type="button"
                    className="bp-mobile-summary__toggle"
                    onClick={() => setMobileSummaryOpen((isOpen) => !isOpen)}
                    aria-expanded={mobileSummaryOpen}
                    aria-controls={mobileSummaryPanelId}
                  >
                    <span className="bp-mobile-summary__copy">
                      <span className="bp-mobile-summary__eyebrow">Booking summary</span>
                      <span className="bp-mobile-summary__preview">{mobileSummaryPreviewLabel}</span>
                    </span>
                    <span className="bp-mobile-summary__state">{mobileSummaryOpen ? 'Hide' : 'Show'}</span>
                  </button>
                  <div id={mobileSummaryPanelId} className="bp-mobile-summary__panel" aria-hidden={!mobileSummaryOpen}>
                    <div className="bp-mobile-summary__panelInner">
                      <div className="bp-summary-box">
                        <SummaryBlock
                          settings={bpSettings}
                          locations={locations}
                          categories={categories}
                          services={services}
                          extras={extras}
                          agents={agents}
                          locationId={locationId}
                          categoryIds={categoryIds}
                          serviceId={serviceId}
                          extraIds={extraIds}
                          agentId={agentId}
                          date={date}
                          slot={slot}
                          showLocation={showLocationSummary}
                          showExtras={showExtrasSummary}
                        />
                      </div>
                    </div>
                  </div>
                </section>
              ) : null}
            </div>

            {error ? <div className="bp-error">{error}</div> : null}
            {loading ? <div className="bp-loading">Loading booking data...</div> : null}

            {step.key === 'location' && (
              <StepLocation
                locations={locations}
                value={locationId}
                onChange={setLocationId}
                onNext={next}
                nextLabel={labels.next}
              />
            )}

            {step.key === 'category' && (
              <StepCategory
                categories={categories}
                value={categoryIds}
                onChange={setCategoryIds}
                onBack={back}
                onNext={next}
                backLabel={labels.back}
                nextLabel={labels.next}
              />
            )}

            {step.key === 'service' && (
              <StepService
                services={services}
                value={serviceId}
                onChange={setServiceId}
                onBack={back}
                onNext={next}
                settings={bpSettings}
                backLabel={labels.back}
                nextLabel={labels.next}
              />
            )}

            {step.key === 'extras' && (
              <StepExtras
                extras={extras}
                value={extraIds}
                onChange={setExtraIds}
                onBack={back}
                onNext={next}
                settings={bpSettings}
                backLabel={labels.back}
                nextLabel={labels.next}
              />
            )}

            {step.key === 'agent' && (
              <StepAgent
                agents={agents}
                value={agentId}
                onChange={setAgentId}
                onBack={back}
                onNext={next}
                backLabel={labels.back}
                nextLabel={labels.next}
              />
            )}

            {step.key === 'datetime' && (
              <StepDateTime
                locationId={locationId}
                serviceId={serviceId}
                agentId={agentId}
                serviceDurationMin={services.find((s) => String(s.id) === String(serviceId))?.duration || 30}
                valueDate={date}
                valueSlot={slot}
                onChangeDate={(v) => {
                  setDate(v);
                  setSlot(null);
                }}
                onChangeSlot={setSlot}
                onBack={back}
                onNext={next}
                backLabel={labels.back}
                nextLabel={labels.next}
              />
            )}

            {step.key === 'customer' && (
              <StepCustomer
                formFields={formFields}
                answers={answers}
                onChange={setAnswers}
                onBack={back}
                onNext={next}
                onError={setError}
                layout={designConfig?.fieldsLayout}
                backLabel={labels.back}
                nextLabel={labels.next}
              />
            )}

            {step.key === 'payment' && (
              <StepPayment
                bookingId={bookingId}
                paymentMethod={paymentMethod}
                totalLabel={formatMoney(totalAmount, bpSettings)}
                onBack={() => goToStepKey('review')}
                onPaid={() => goToStepKey('confirmation')}
              />
            )}

            {step.key === 'review' && (
              <StepReview
                locationId={locationId}
                categoryIds={categoryIds}
                serviceId={serviceId}
                extraIds={extraIds}
                agentId={agentId}
                date={date}
                slot={slot}
                locations={locations}
                categories={categories}
                services={services}
                extras={extras}
                agents={agents}
                formFields={formFields}
                answers={answers}
                bookingId={bookingId}
                showLocation={showLocationSummary}
                showExtras={showExtrasSummary}
                showPayment={paymentsActive && hasPaymentStep}
                hasPayment={paymentsActive && hasPaymentStep}
                paymentMethodLabel={(paymentsActive && hasPaymentStep) ? (paymentLabelMap[paymentMethod] || paymentMethod || '-') : ''}
                totalLabel={formatMoney(totalAmount, bpSettings)}
                onBack={back}
                onNext={async () => {
                  if (paymentsActive && hasPaymentStep) {
                    await createBookingIfNeeded();
                    goToStepKey('payment');
                    return;
                  }
                  await submitBooking();
                }}
                isCreatingBooking={isCreatingBooking}
                createError={createError}
                loading={loading}
                backLabel={labels.back}
                nextLabel={labels.next}
              />
            )}

            {step.key === 'confirmation' && (
              <StepConfirmation
                bookingId={bookingId}
                confirmInfo={confirmInfo}
                loading={confirmLoading}
                error={confirmError}
                summary={null}
                onClose={onClose}
                onRetry={() => goToStepKey('payment')}
              />
            )}
          </main>

          {showSummary ? (
            <aside className="bp-summary">
              <div className="bp-summary-title">Summary</div>
              <div className="bp-summary-box">
                <SummaryBlock
                  settings={bpSettings}
                  locations={locations}
                  categories={categories}
                  services={services}
                  extras={extras}
                  agents={agents}
                  locationId={locationId}
                  categoryIds={categoryIds}
                  serviceId={serviceId}
                  extraIds={extraIds}
                  agentId={agentId}
                  date={date}
                  slot={slot}
                  showLocation={showLocationSummary}
                  showExtras={showExtrasSummary}
                />
              </div>
            </aside>
          ) : null}
        </div>
      </div>
    </div>
  );
}

function buildSummaryData(props) {
  const {
    settings,
    locations, categories, services, extras, agents,
    locationId, categoryIds, serviceId, extraIds, agentId,
  } = props;

  const loc = locations.find((x) => String(x.id) === String(locationId));
  const svc = services.find((x) => String(x.id) === String(serviceId));
  const ag = agents.find((x) => String(x.id) === String(agentId));
  const ex = extras.filter((x) => extraIds.includes(x.id) || extraIds.includes(String(x.id)));
  const cats = categories.filter((x) => categoryIds.includes(x.id) || categoryIds.includes(String(x.id)));
  const svcPrice = svc?.price != null ? Number(svc.price) : 0;
  const extrasPrice = ex.reduce((sum, item) => sum + (item?.price != null ? Number(item.price) : 0), 0);
  const totalPrice = svcPrice + extrasPrice;

  return {
    loc,
    svc,
    ag,
    ex,
    cats,
    svcPrice,
    extrasPrice,
    totalPrice,
    servicePriceLabel: svc?.price != null ? formatMoney(svcPrice, settings) : '-',
    extrasPriceLabel: ex.length ? formatMoney(extrasPrice, settings) : '-',
    totalLabel: svc?.price != null || ex.length ? formatMoney(totalPrice, settings) : '-',
  };
}

function SummaryBlock(props) {
  const {
    settings,
    locations, categories, services, extras, agents,
    locationId, categoryIds, serviceId, extraIds, agentId, date, slot,
    showLocation = true,
    showExtras = true,
  } = props;

  const {
    loc,
    svc,
    ag,
    ex,
    cats,
    servicePriceLabel,
    extrasPriceLabel,
    totalLabel,
  } = buildSummaryData({
    settings,
    locations,
    categories,
    services,
    extras,
    agents,
    locationId,
    categoryIds,
    serviceId,
    extraIds,
    agentId,
  });

  return (
    <div className="bp-summary-items">
      {showLocation ? (
        <div className="bp-summary-row">
          <div className="k">Location</div>
          <div className="v">{loc?.name || '-'}</div>
        </div>
      ) : null}
      <div className="bp-summary-row">
        <div className="k">Category</div>
        <div className="v">{cats.length ? cats.map((c) => c.name).join(', ') : '-'}</div>
      </div>
      <div className="bp-summary-row">
        <div className="k">Service</div>
        <div className="v">{svc?.name || '-'}</div>
      </div>
      <div className="bp-summary-row">
        <div className="k">Service Price</div>
        <div className="v">{servicePriceLabel}</div>
      </div>
      <div className="bp-summary-row">
        <div className="k">Agent</div>
        <div className="v">{ag?.name || '-'}</div>
      </div>
      <div className="bp-summary-row">
        <div className="k">Date</div>
        <div className="v">{date || '-'}</div>
      </div>
      <div className="bp-summary-row">
        <div className="k">Time</div>
        <div className="v">{slot?.start_time ? `${slot.start_time} - ${slot.end_time}` : '-'}</div>
      </div>
      {showExtras ? (
        <>
          <div className="bp-summary-row">
            <div className="k">Extras</div>
            <div className="v">{ex.length ? ex.map((e) => e.name).join(', ') : '-'}</div>
          </div>
          <div className="bp-summary-row">
            <div className="k">Extras Price</div>
            <div className="v">{extrasPriceLabel}</div>
          </div>
        </>
      ) : null}
      <div className="bp-summary-row bp-summary-row--total">
        <div className="k">Total</div>
        <div className="v">{svc?.price != null || (showExtras && ex.length) ? totalLabel : '-'}</div>
      </div>
    </div>
  );
}
