// Public Booking Page — customer-facing, embedded into the agency-built site.
// Same Apple-style design system as the owner UI (sans-serif, soft shadows,
// rounded cards). Data comes from the public RPCs.
const HIDDEN_FIELD_IDS_PUBLIC = new Set(["fl_name", "fl_email", "fl_phone"]);

function PublicBookingPage() {
  const { useParams } = ReactRouterDOM;
  const { slug } = useParams();

  const [workspace, setWorkspace] = useState(null);
  const [loadingWs, setLoadingWs] = useState(true);
  const [loadError, setLoadError] = useState(null);

  const [step, setStep] = useState(1);
  const [pickedForm, setPickedForm] = useState(null);
  const [pickedSlot, setPickedSlot] = useState(null);
  const [answers, setAnswers] = useState({});
  const [contact, setContact] = useState({ name: "", email: "", phone: "" });
  const [submitting, setSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState(null);
  const [confirmation, setConfirmation] = useState(null);

  useEffect(() => {
    let cancelled = false;
    (async () => {
      setLoadingWs(true);
      setLoadError(null);
      const res = await SB.rpc('workspace_public', { p_slug: slug });
      if (cancelled) return;
      setLoadingWs(false);
      if (res.error) { setLoadError(res.error.message); return; }
      if (!res.data) { setWorkspace(null); return; }
      setWorkspace(res.data);
      const firstForm = (res.data.forms || [])[0] || null;
      setPickedForm(firstForm);
    })();
    return () => { cancelled = true; };
  }, [slug]);

  const submit = async () => {
    if (!workspace || !pickedForm || !pickedSlot) return;
    setSubmitting(true);
    setSubmitError(null);
    const extraFields = (pickedForm.fields || []).filter(
      f => !HIDDEN_FIELD_IDS_PUBLIC.has(f.id) && f.type !== "email" && f.type !== "phone"
    );
    const payload = {
      p_workspace_slug: workspace.slug,
      p_form_id: pickedForm.id,
      p_name: contact.name.trim(),
      p_email: contact.email.trim().toLowerCase(),
      p_phone: contact.phone.trim() || null,
      p_starts_at: pickedSlot.toISOString(),
      p_answers: extraFields.map(f => ({ q: f.label, a: answers[f.id] || null }))
    };
    const res = await SB.rpc('submit_booking', payload);
    setSubmitting(false);
    if (res.error) {
      setSubmitError(prettifySubmitError(res.error.message));
      return;
    }
    setConfirmation({ id: res.data, formId: pickedForm.id, slot: pickedSlot, contact: Object.assign({}, contact) });
    setStep(5);
  };

  const restart = () => {
    setStep(1);
    setPickedSlot(null);
    setAnswers({});
    setContact({ name: "", email: "", phone: "" });
    setConfirmation(null);
    setSubmitError(null);
  };

  const next = () => setStep(Math.min(step + 1, 5));
  const back = () => setStep(Math.max(step - 1, 1));

  // ── render guards ──────────────────────────────────────────────────────
  if (loadingWs) {
    return <PublicScaffold><div className="py-20 text-center text-[12.5px] text-ink/45">Loading…</div></PublicScaffold>;
  }
  if (loadError) {
    return (
      <PublicScaffold>
        <div className="py-20 text-center max-w-md mx-auto">
          <div className="font-display text-[28px] tracking-tight">Couldn't load this page</div>
          <p className="text-[13.5px] text-ink/55 mt-2">{loadError}</p>
        </div>
      </PublicScaffold>
    );
  }
  if (!workspace) {
    return (
      <PublicScaffold>
        <div className="py-20 text-center max-w-md mx-auto">
          <div className="font-display text-[28px] tracking-tight">Page not found</div>
          <p className="text-[13.5px] text-ink/55 mt-2">There's no business at <span className="font-mono text-ink/75">/{slug}</span>. Check the URL or contact the business directly.</p>
        </div>
      </PublicScaffold>
    );
  }

  const publishedForms = workspace.forms || [];
  if (publishedForms.length === 0) {
    return (
      <PublicScaffold workspaceName={workspace.name}>
        <div className="py-20 text-center max-w-md mx-auto">
          <div className="font-display text-[28px] tracking-tight">Bookings aren't open</div>
          <p className="text-[13.5px] text-ink/55 mt-2">{workspace.name} isn't taking online bookings right now. Please reach out to them directly.</p>
        </div>
      </PublicScaffold>
    );
  }

  if (!pickedForm) {
    return <PublicScaffold workspaceName={workspace.name}><div className="py-20 text-center text-[12.5px] text-ink/45">Loading…</div></PublicScaffold>;
  }

  return (
    <PublicScaffold workspaceName={workspace.name}>
      <div className="grid lg:grid-cols-[1fr_320px] gap-8">
        <div>
          {step < 5 && <PublicStepper step={step} />}

          <div className="mt-6 rounded-2xl bg-white border border-ink/[0.06] shadow-soft overflow-hidden">
            {submitError && (
              <div className="px-6 pt-5">
                <div className="border border-danger/30 bg-danger/[0.05] text-[13px] text-ink p-3 rounded-lg flex items-start gap-2">
                  <Icon name="alert-triangle" size={14} className="text-danger mt-0.5" />
                  <div><div className="font-medium">We couldn't submit your booking.</div><div className="text-ink/65 mt-0.5">{submitError}</div></div>
                </div>
              </div>
            )}

            {step === 1 && <Step1 forms={publishedForms} picked={pickedForm} onPick={setPickedForm} onNext={next} />}
            {step === 2 && <Step2 form={pickedForm} pickedSlot={pickedSlot} onPick={setPickedSlot} onNext={next} onBack={back} workspaceSlug={workspace.slug} />}
            {step === 3 && <Step3 form={pickedForm} contact={contact} setContact={setContact} answers={answers} setAnswers={setAnswers} onNext={next} onBack={back} />}
            {step === 4 && <Step4 form={pickedForm} slot={pickedSlot} contact={contact} loading={submitting} onSubmit={submit} onBack={back} />}
            {step === 5 && confirmation && <Step5 form={pickedForm} confirmation={confirmation} workspaceName={workspace.name} onRestart={restart} />}
          </div>
        </div>

        {/* Summary card on the right (hidden on mobile) */}
        <aside className="hidden lg:block">
          <div className="rounded-2xl bg-white border border-ink/[0.06] shadow-card p-5 sticky top-6">
            <div className="text-[10.5px] uppercase tracking-[0.12em] text-ink/45">Summary</div>
            <div className="mt-3 font-display text-[20px] leading-tight tracking-tight">{pickedForm.public_title}</div>
            {pickedForm.description && <p className="mt-2 text-[12.5px] text-ink/55 leading-relaxed">{pickedForm.description}</p>}
            <div className="mt-4 flex flex-col divide-y divide-ink/[0.06] text-[12.5px]">
              <SumRow label="Duration">{pickedForm.duration_min} min</SumRow>
              <SumRow label="Confirmation">{pickedForm.confirmation_mode === "instant" ? "Immediate" : "Within 1 business day"}</SumRow>
              {pickedSlot && (
                <SumRow label="When">
                  <div className="text-right">
                    {fmt(pickedSlot, "EEE, MMM d")}<br />
                    <span className="text-ink/55">{fmt(pickedSlot, "h:mma").toLowerCase()}</span>
                  </div>
                </SumRow>
              )}
            </div>
            <div className="mt-5 pt-4 border-t border-ink/[0.06] flex items-start gap-2 text-[11px] text-ink/45">
              <Icon name="shield-check" size={11} className="mt-0.5" />
              <span>Encrypted and shared only with {workspace.name}.</span>
            </div>
          </div>
        </aside>
      </div>
    </PublicScaffold>
  );
}

function prettifySubmitError(msg) {
  if (!msg) return "Something went wrong on our end. Please try again.";
  if (/email_invalid/i.test(msg)) return "That doesn't look like a valid email — please double-check.";
  if (/name_required/i.test(msg)) return "Please enter your name.";
  if (/form_not_available/i.test(msg)) return "This form is no longer available. Please refresh the page.";
  if (/workspace_not_found/i.test(msg)) return "We couldn't find this business. Please check the URL.";
  return msg;
}

// ── Outer chrome ──────────────────────────────────────────────────────────
function PublicScaffold({ children, workspaceName }) {
  const name = workspaceName || "Booking";
  const initials = name.split(/\s+/).filter(Boolean).slice(0, 2).map(s => s[0]).join("").toUpperCase() || "?";
  return (
    <div className="min-h-screen bg-paper text-ink antialiased">
      <header className="border-b border-ink/[0.06] bg-paper/80 backdrop-blur-lg sticky top-0 z-10">
        <div className="max-w-5xl mx-auto px-6 lg:px-10 h-14 flex items-center gap-3">
          <div className="h-8 w-8 rounded-xl bg-ink text-white grid place-items-center text-[10.5px] font-semibold tracking-wider">
            {initials}
          </div>
          <div className="font-display text-[16px] leading-none tracking-tight">{name}</div>
          <span className="ml-2 text-[10.5px] uppercase tracking-[0.14em] text-ink/40 mt-1">Booking</span>
        </div>
      </header>

      <main className="max-w-5xl mx-auto px-6 lg:px-10 py-10 lg:py-14">
        {children}
      </main>

      <footer className="border-t border-ink/[0.06]">
        <div className="max-w-5xl mx-auto px-6 lg:px-10 py-5 flex flex-wrap items-center justify-between gap-3 text-[11.5px] text-ink/45">
          <div>© {new Date().getFullYear()} {name}</div>
          <div className="text-ink/35">Booking by Vale</div>
        </div>
      </footer>
    </div>
  );
}

function PublicStepper({ step }) {
  const steps = [
    { n: 1, label: "Service" },
    { n: 2, label: "Date & time" },
    { n: 3, label: "Your details" },
    { n: 4, label: "Review" }
  ];
  return (
    <ol className="flex items-center gap-1.5 flex-wrap">
      {steps.map((s, i) => {
        const active = step === s.n, done = step > s.n;
        return (
          <React.Fragment key={s.n}>
            <li className={`inline-flex items-center gap-2 ${active ? "text-ink" : done ? "text-ink/65" : "text-ink/35"}`}>
              <span className={`h-5 w-5 grid place-items-center rounded-full text-[10.5px] font-medium ${active ? "bg-ink text-white" : done ? "bg-ink/15 text-ink/80" : "border border-ink/15 bg-white"}`}>
                {done ? <Icon name="check" size={11} /> : s.n}
              </span>
              <span className="text-[12.5px] hidden md:inline">{s.label}</span>
            </li>
            {i < steps.length - 1 && <div className={`h-px w-6 md:w-10 ${done ? "bg-ink/25" : "bg-ink/10"}`} />}
          </React.Fragment>
        );
      })}
    </ol>
  );
}

function SumRow({ label, children }) {
  return (
    <div className="flex items-baseline justify-between gap-4 py-2.5">
      <span className="text-ink/50">{label}</span>
      <span className="text-ink">{children}</span>
    </div>
  );
}

// ── Step 1: Pick service ──────────────────────────────────────────────────
function Step1({ forms, picked, onPick, onNext }) {
  return (
    <div className="p-6 md:p-8">
      <div className="text-[10.5px] uppercase tracking-[0.12em] text-ink/45">Step 1 of 4</div>
      <h2 className="font-display text-[26px] md:text-[30px] leading-tight tracking-tight mt-1">Choose a service</h2>
      <p className="mt-1.5 text-[13px] text-ink/55">Select the type of appointment you'd like to book.</p>
      <div className="mt-6 flex flex-col gap-2">
        {forms.map((f) => {
          const on = f.id === picked.id;
          return (
            <button
              key={f.id}
              onClick={() => onPick(f)}
              className={`text-left rounded-xl border p-4 transition ${on ? "border-ink bg-ink/[0.02] shadow-card" : "border-ink/[0.10] bg-white hover:border-ink/30"}`}
            >
              <div className="flex items-start justify-between gap-4">
                <div className="min-w-0">
                  <div className="font-medium text-[15px] leading-tight">{f.public_title}</div>
                  {f.description && <div className="text-[12.5px] text-ink/55 mt-1">{f.description}</div>}
                </div>
                <div className="text-right flex-shrink-0">
                  <div className="text-[12.5px] text-ink/70">{f.duration_min} min</div>
                  <div className="text-[11px] text-ink/45">{f.confirmation_mode === "instant" ? "Instant" : "Approval"}</div>
                </div>
              </div>
            </button>
          );
        })}
      </div>
      <div className="mt-7 flex items-center justify-end">
        <Button iconRight="arrow-right" onClick={onNext}>Continue</Button>
      </div>
    </div>
  );
}

// ── Step 2: Date & Time ──────────────────────────────────────────────────
function Step2({ form, pickedSlot, onPick, onNext, onBack, workspaceSlug }) {
  const [anchor, setAnchor] = useState(new Date());
  const start = startOfWeekMondayish(anchor);
  const days = Array.from({length: 7}, (_, i) => { const d = new Date(start); d.setDate(start.getDate() + i); return d; });
  const todayStart = new Date(); todayStart.setHours(0,0,0,0);
  const initialDay = pickedSlot ? new Date(pickedSlot.getFullYear(), pickedSlot.getMonth(), pickedSlot.getDate()) : (days.find(d => d >= todayStart) || days[0]);
  const [selectedDay, setSelectedDay] = useState(initialDay);

  // Fetch busy intervals for the visible week so we can:
  //   1. Hide booked slots on the selected day
  //   2. Mark days with zero free slots as "Full" in the week strip
  const [busy, setBusy] = useState([]);
  const [loadingBusy, setLoadingBusy] = useState(false);
  useEffect(() => {
    if (!workspaceSlug) return;
    let cancelled = false;
    setLoadingBusy(true);
    const from = new Date(days[0]); from.setHours(0, 0, 0, 0);
    const to   = new Date(days[6]); to.setHours(23, 59, 59, 999);
    (async () => {
      const res = await SB.rpc('busy_intervals', {
        p_workspace_slug: workspaceSlug,
        p_from: from.toISOString(),
        p_to:   to.toISOString()
      });
      if (cancelled) return;
      setLoadingBusy(false);
      setBusy((res.data || []).map(b => ({ start: new Date(b.start), end: new Date(b.end) })));
    })();
    return () => { cancelled = true; };
  }, [workspaceSlug, anchor.getTime()]);

  // Filter candidate slots against busy intervals (+ form buffer).
  const buffer = form.buffer_min || 0;
  const filterFree = (candidate) => candidate.filter(s => {
    const slotEnd = new Date(s.getTime() + form.duration_min * 60000);
    return !busy.some(b => {
      const blockedEnd = new Date(b.end.getTime() + buffer * 60000);
      return s < blockedEnd && slotEnd > b.start;
    });
  });
  const slots = filterFree(generateSlots(selectedDay, form));
  const fullyBooked = (day) => {
    const candidate = generateSlots(day, form);
    if (candidate.length === 0) return false;     // closed day, not "full"
    return filterFree(candidate).length === 0;
  };

  return (
    <div className="p-6 md:p-8">
      <div className="text-[10.5px] uppercase tracking-[0.12em] text-ink/45">Step 2 of 4</div>
      <h2 className="font-display text-[26px] md:text-[30px] leading-tight tracking-tight mt-1">Pick a date and time</h2>
      <p className="mt-1.5 text-[13px] text-ink/55">Sessions last about {form.duration_min} minutes.</p>

      <div className="mt-6 flex items-center justify-between">
        <div className="font-display text-[15px] tracking-tight">{fmt(days[0], "MMM d")} – {fmt(days[6], "MMM d, yyyy")}</div>
        <div className="flex items-center gap-1">
          <button onClick={() => { const d = new Date(anchor); d.setDate(d.getDate() - 7); setAnchor(d); }} className="h-8 w-8 grid place-items-center rounded-md border border-ink/[0.10] hover:bg-ink/[0.03]"><Icon name="chevron-left" size={13} /></button>
          <button onClick={() => { const d = new Date(anchor); d.setDate(d.getDate() + 7); setAnchor(d); }} className="h-8 w-8 grid place-items-center rounded-md border border-ink/[0.10] hover:bg-ink/[0.03]"><Icon name="chevron-right" size={13} /></button>
        </div>
      </div>

      <div className="mt-3 grid grid-cols-7 gap-1.5">
        {days.map((d) => {
          const on = sameDay(d, selectedDay);
          const past = d < todayStart;
          const isFull = !past && fullyBooked(d);
          return (
            <button
              key={d.toISOString()}
              disabled={past}
              onClick={() => setSelectedDay(d)}
              className={`relative p-2 text-center rounded-lg transition ${past ? "opacity-30 cursor-not-allowed" : ""} ${on ? "bg-ink text-white shadow-card" : "border border-ink/[0.10] bg-white hover:border-ink/30"}`}
            >
              <div className={`text-[10px] uppercase tracking-[0.08em] ${on ? "text-white/70" : "text-ink/50"}`}>{fmt(d, "EEE")}</div>
              <div className="font-display text-[18px] leading-none mt-1">{d.getDate()}</div>
              {isFull && (
                <div className={`text-[9px] uppercase tracking-[0.08em] mt-1 ${on ? "text-white/65" : "text-ink/45"}`}>Full</div>
              )}
            </button>
          );
        })}
      </div>

      <div className="mt-6">
        <div className="text-[11px] uppercase tracking-[0.1em] text-ink/55 mb-2 flex items-center gap-2">
          <span>{fmt(selectedDay, "EEEE, MMM d")}</span>
          {loadingBusy && <span className="text-ink/40 normal-case tracking-normal">· checking availability…</span>}
        </div>
        {slots.length === 0 ? (
          <div className="rounded-xl border border-dashed border-ink/[0.12] py-10 text-center">
            <div className="text-[13.5px]">{generateSlots(selectedDay, form).length === 0 ? "Closed on this day." : "Fully booked."}</div>
            <div className="text-[12px] text-ink/55 mt-1">Try another date.</div>
          </div>
        ) : (
          <div className="grid grid-cols-3 md:grid-cols-4 gap-2">
            {slots.map((s) => {
              const on = pickedSlot && +pickedSlot === +s;
              return (
                <button
                  key={+s}
                  onClick={() => onPick(s)}
                  className={`h-10 rounded-md text-[13px] transition ${on ? "bg-ink text-white" : "border border-ink/[0.10] bg-white hover:border-ink/30"}`}
                >
                  {fmt(s, "h:mma").toLowerCase()}
                </button>
              );
            })}
          </div>
        )}
      </div>

      <div className="mt-8 flex items-center justify-between">
        <Button variant="ghost" icon="arrow-left" onClick={onBack}>Back</Button>
        <Button iconRight="arrow-right" onClick={onNext} disabled={!pickedSlot}>Continue</Button>
      </div>
    </div>
  );
}

// ── Step 3: Details + custom questions ────────────────────────────────────
function Step3({ form, contact, setContact, answers, setAnswers, onNext, onBack }) {
  const [errors, setErrors] = useState({});
  const extraFields = (form.fields || []).filter(
    f => !HIDDEN_FIELD_IDS_PUBLIC.has(f.id) && f.type !== "email" && f.type !== "phone"
  );

  const handleAnswer = (id, val) => setAnswers(Object.assign({}, answers, { [id]: val }));

  const submit = () => {
    const errs = {};
    if (!contact.name.trim())  errs.name  = "Required";
    if (!/^.+@.+\..+/.test(contact.email.trim())) errs.email = "Enter a valid email";
    extraFields.forEach(f => {
      if (f.required && !answers[f.id]) errs[f.id] = "Required";
    });
    if (Object.keys(errs).length > 0) { setErrors(errs); return; }
    onNext();
  };

  return (
    <div className="p-6 md:p-8">
      <div className="text-[10.5px] uppercase tracking-[0.12em] text-ink/45">Step 3 of 4</div>
      <h2 className="font-display text-[26px] md:text-[30px] leading-tight tracking-tight mt-1">Your details</h2>
      <p className="mt-1.5 text-[13px] text-ink/55">We'll use these to confirm your booking and send reminders.</p>

      <div className="mt-6 grid sm:grid-cols-2 gap-4">
        <Field label="Full name" required error={errors.name}>
          <Input value={contact.name} onChange={(e) => setContact(Object.assign({}, contact, { name: e.target.value }))} placeholder="First Last" autoFocus />
        </Field>
        <Field label="Phone" hint="Optional" error={errors.phone}>
          <Input value={contact.phone} onChange={(e) => setContact(Object.assign({}, contact, { phone: e.target.value }))} placeholder="+32 …" />
        </Field>
        <Field label="Email" required className="sm:col-span-2" error={errors.email}>
          <Input type="email" value={contact.email} onChange={(e) => setContact(Object.assign({}, contact, { email: e.target.value }))} placeholder="you@example.com" />
        </Field>
      </div>

      {extraFields.length > 0 && (
        <>
          <div className="mt-7 mb-3 text-[11px] uppercase tracking-[0.1em] text-ink/45">Tell us more</div>
          <div className="grid sm:grid-cols-2 gap-4">
            {extraFields.map((f) => (
              <div key={f.id} className={f.type === "long" || f.type === "address" ? "sm:col-span-2" : ""}>
                <PublicAnswerField field={f} value={answers[f.id]} onChange={(v) => handleAnswer(f.id, v)} error={errors[f.id]} />
              </div>
            ))}
          </div>
        </>
      )}

      <div className="mt-8 flex items-center justify-between">
        <Button variant="ghost" icon="arrow-left" onClick={onBack}>Back</Button>
        <Button iconRight="arrow-right" onClick={submit}>Continue</Button>
      </div>
    </div>
  );
}

// Custom-question input wired with value/onChange so the customer's input is
// captured (the form-builder preview is a separate component).
function PublicAnswerField({ field, value, onChange, error }) {
  if (field.type === "checkbox") {
    return (
      <Field label={field.label} required={field.required} error={error}>
        <label className="inline-flex items-center gap-2 text-[13px] cursor-pointer">
          <input type="checkbox" checked={!!value} onChange={(e) => onChange(e.target.checked)} className="accent-ink h-4 w-4" />
          Yes
        </label>
      </Field>
    );
  }

  let control;
  switch (field.type) {
    case "long":
      control = <Textarea rows="3" value={value || ""} onChange={(e) => onChange(e.target.value)} placeholder={field.placeholder || ""} />;
      break;
    case "dropdown":
    case "vehicle":
    case "service":
      control = (
        <Select value={value || ""} onChange={(e) => onChange(e.target.value)}>
          <option value="">Choose…</option>
          {(field.options || []).map(o => <option key={o} value={o}>{o}</option>)}
        </Select>
      );
      break;
    case "multi": {
      const arr = Array.isArray(value) ? value : [];
      const toggle = (o) => onChange(arr.includes(o) ? arr.filter(x => x !== o) : arr.concat(o));
      control = (
        <div className="flex flex-wrap gap-1.5">
          {(field.options || []).map(o => (
            <button
              type="button"
              key={o}
              onClick={() => toggle(o)}
              className={`px-3 h-9 rounded-md text-[12.5px] border transition ${arr.includes(o) ? "bg-ink text-white border-ink" : "border-ink/[0.10] bg-white hover:border-ink/30"}`}
            >
              {o}
            </button>
          ))}
        </div>
      );
      break;
    }
    case "radio":
      control = (
        <div className="flex flex-col gap-1.5">
          {(field.options || []).map(o => (
            <label key={o} className="inline-flex items-center gap-2 text-[13px] cursor-pointer">
              <input type="radio" name={field.id} checked={value === o} onChange={() => onChange(o)} className="accent-ink" />
              {o}
            </label>
          ))}
        </div>
      );
      break;
    case "party":
      control = (
        <div className="flex items-center gap-1.5">
          {[1,2,3,4,5,"6+"].map(n => (
            <button
              type="button"
              key={n}
              onClick={() => onChange(String(n))}
              className={`h-10 w-11 rounded-md text-[13px] border transition ${String(value) === String(n) ? "bg-ink text-white border-ink" : "border-ink/[0.10] bg-white hover:border-ink/30"}`}
            >
              {n}
            </button>
          ))}
        </div>
      );
      break;
    case "number":
      control = <Input type="number" value={value || ""} onChange={(e) => onChange(e.target.value)} placeholder={field.placeholder || ""} />;
      break;
    case "date":
      control = <Input type="date" value={value || ""} onChange={(e) => onChange(e.target.value)} />;
      break;
    case "time":
      control = <Input type="time" value={value || ""} onChange={(e) => onChange(e.target.value)} />;
      break;
    case "address":
      control = <Input value={value || ""} onChange={(e) => onChange(e.target.value)} placeholder="Street address" />;
      break;
    default:
      control = <Input value={value || ""} onChange={(e) => onChange(e.target.value)} placeholder={field.placeholder || ""} />;
  }
  return <Field label={field.label} required={field.required} error={error}>{control}</Field>;
}

// ── Step 4: Review ────────────────────────────────────────────────────────
function Step4({ form, slot, contact, loading, onSubmit, onBack }) {
  return (
    <div className="p-6 md:p-8">
      <div className="text-[10.5px] uppercase tracking-[0.12em] text-ink/45">Step 4 of 4</div>
      <h2 className="font-display text-[26px] md:text-[30px] leading-tight tracking-tight mt-1">Review and confirm</h2>
      <p className="mt-1.5 text-[13px] text-ink/55">Double-check the details below before submitting.</p>

      <div className="mt-6 rounded-xl border border-ink/[0.08] bg-white divide-y divide-ink/[0.06] overflow-hidden">
        <ReviewRow label="Service">{form.public_title}</ReviewRow>
        <ReviewRow label="Date & time">{slot ? `${fmt(slot, "EEEE, MMM d")} · ${fmt(slot, "h:mma").toLowerCase()}` : "—"}</ReviewRow>
        <ReviewRow label="Duration">{form.duration_min} minutes</ReviewRow>
        <ReviewRow label="Name">{contact.name || "—"}</ReviewRow>
        <ReviewRow label="Email">{contact.email || "—"}</ReviewRow>
        <ReviewRow label="Phone">{contact.phone || "—"}</ReviewRow>
      </div>

      <div className="mt-5 text-[12.5px] text-ink/55">
        {form.confirmation_mode === "instant"
          ? "You'll receive a confirmation email immediately."
          : "Your booking will be reviewed by our team and confirmed within one business day."}
      </div>

      <div className="mt-7 flex items-center justify-between">
        <Button variant="ghost" icon="arrow-left" onClick={onBack} disabled={loading}>Back</Button>
        <Button icon={loading ? null : "check"} onClick={onSubmit} disabled={loading}>
          {loading ? <><span className="h-1.5 w-1.5 rounded-full bg-white pulse-dot mr-1.5" />Submitting…</> : "Confirm booking"}
        </Button>
      </div>
    </div>
  );
}

function ReviewRow({ label, children }) {
  return (
    <div className="flex items-baseline justify-between gap-4 px-4 py-3">
      <span className="text-[11px] uppercase tracking-[0.08em] text-ink/50">{label}</span>
      <span className="text-[13.5px] text-right">{children}</span>
    </div>
  );
}

// ── Step 5: Success ──────────────────────────────────────────────────────
function Step5({ form, confirmation, workspaceName, onRestart }) {
  const shortRef = (confirmation.id || "").split("-")[0]?.toUpperCase() || "—";
  return (
    <div className="p-8 md:p-12 text-center">
      <div className="h-14 w-14 rounded-full bg-ok/15 mx-auto grid place-items-center">
        <Icon name="check" size={22} className="text-ok" />
      </div>
      <h2 className="font-display text-[30px] md:text-[36px] leading-tight tracking-tight mt-5">You're booked.</h2>
      <p className="mt-2 text-[13.5px] text-ink/60 max-w-md mx-auto">
        We've recorded your request from <span className="text-ink">{confirmation.contact.email}</span>.
        {form.confirmation_mode === "manual" && ` ${workspaceName || "The team"} will follow up shortly to confirm.`}
      </p>

      <div className="mt-7 inline-block text-left rounded-2xl border border-ink/[0.08] bg-white shadow-card overflow-hidden">
        <div className="px-6 py-4 border-b border-ink/[0.06] bg-ink/[0.015]">
          <div className="text-[10.5px] uppercase tracking-[0.1em] text-ink/45">Confirmation #</div>
          <div className="font-mono text-[14px] mt-1">VB-{shortRef}</div>
        </div>
        <div className="px-6 py-4">
          <div className="text-[10.5px] uppercase tracking-[0.1em] text-ink/45">Appointment</div>
          <div className="font-display text-[20px] tracking-tight mt-1 leading-tight">{form.public_title}</div>
          <div className="text-[12.5px] text-ink/65 mt-1">{confirmation.slot && `${fmt(confirmation.slot, "EEEE, MMM d")} · ${fmt(confirmation.slot, "h:mma").toLowerCase()}`}</div>
        </div>
      </div>

      <div className="mt-8 flex items-center justify-center gap-3">
        <Button variant="ghost" icon="plus" onClick={onRestart}>Book another</Button>
      </div>
    </div>
  );
}

// ── helpers ───────────────────────────────────────────────────────────────
function startOfWeekMondayish(d) {
  const r = new Date(d); r.setHours(0,0,0,0); r.setDate(d.getDate() - d.getDay()); return r;
}

function generateSlots(day, form) {
  const now = new Date();
  const startOfDay = new Date(day); startOfDay.setHours(0,0,0,0);
  const today = new Date(now); today.setHours(0,0,0,0);
  if (startOfDay < today) return [];
  const weekday = day.getDay();
  if (weekday === 0) return []; // Sundays closed for v1
  const duration = form.duration_min || 30;
  const step = duration <= 30 ? 0.5 : 1; // hours
  const slots = [];
  for (let h = 9; h + (duration / 60) <= 17; h += step) {
    const hh = Math.floor(h);
    const mm = Math.round((h - hh) * 60);
    const d = new Date(day); d.setHours(hh, mm, 0, 0);
    if (sameDay(d, now) && d < now) continue;
    slots.push(d);
  }
  return slots;
}

Object.assign(window, { PublicBookingPage });
