import { useEffect, useState, useRef, useCallback, useMemo } from "react";
import SignedImage from "@/components/SignedImage";
import BookingSuccessScreen from "@/components/client/BookingSuccessScreen";
import { useSearchParams, Link, useNavigate } from "react-router-dom";
import { supabase } from "@/integrations/supabase/client";
import { useAuth } from "@/contexts/AuthContext";
import DashboardLayout from "@/components/DashboardLayout";
import { clientNavItems } from "@/components/client/clientNav";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
import { Textarea } from "@/components/ui/textarea";
import { Input } from "@/components/ui/input";
import { Calendar } from "@/components/ui/calendar";
import { Checkbox } from "@/components/ui/checkbox";
import { useToast } from "@/hooks/use-toast";
import { useRateOverrides } from "@/hooks/use-rate-overrides";
import { cn } from "@/lib/utils";
import BookingProgress from "@/components/client/BookingProgress";
import BookingChat from "@/components/client/BookingChat";
import BookingModeChooser from "@/components/client/BookingModeChooser";
import BookingEstimate from "@/components/client/BookingEstimate";
import ActiveBookingsBanner from "@/components/client/ActiveBookingsBanner";
import {
  CalendarIcon, CheckCircle2, Home, MapPin, Sparkles,
  Star, ArrowRight, ArrowLeft, Send, MessageCircle, FileText,
  Loader2, AlertCircle, Mic, MicOff, ChevronDown, ChevronUp,
  Repeat, Gift, Check, Clock, Sunrise, Sun, Sunset, Moon,
  Droplets, Mail, Phone,
  Snowflake, Wrench, Leaf, ClipboardList, Search,
} from "lucide-react";
import StructuredPhoneInput, { stripPhone, toE164, parsePhone } from "@/components/client/StructuredPhoneInput";
import { format, differenceInDays, addDays, addWeeks, addMonths, formatDistanceToNow, differenceInHours, parseISO } from "date-fns";
import { sendAdminAlert, sendAdminInAppNotification, sendSlackNotification, sendTransactionalEmailNotification } from "@/lib/admin-alerts";
import { cacheBookingSnapshot } from "@/lib/booking-snapshot";
import {
  buildRateOverrideMatchers,
  getApplicableRateOverridesForRange,
  mergeRateOverridesWithCanadianHolidayFallbacks,
  resolveDateOverridePricing,
  resolveServicePricing,
  type PricingModel,
} from "@/lib/pricing";
import { getClientBookingPremiumSummary } from "@/lib/client-booking-premium";
import { formatPropertyAddress, formatPropertyDisplayName } from "@/lib/property-address";
import {
  getGuidedBookingReply,
  getLowConfidenceBookingReply,
  type GuidedBookingRoute,
  type GuidedBookingState,
} from "@/lib/booking-intake";

type ServiceOption = {
  id: string;
  service_type: string;
  base_price: number;
  modifiers: any;
};

type BookingStatus = "requested" | "confirmed";

const QUOTE_FIRST_SERVICE_TYPES = new Set(["handyman", "small_tasks", "property_inspection"]);

type ChatMessage = {
  role: "user" | "assistant";
  content: string;
  toolCall?: any;
};

const serviceIcons: Record<string, any> = {
  snow_removal: Snowflake,
  handyman: Wrench,
  yard_cleanup: Leaf,
  small_tasks: ClipboardList,
  gutter_cleaning: Droplets,
  property_inspection: Search,
};

const SERVICE_ALIAS: Record<string, string> = {
  snow: "snow_removal",
  handy: "handyman",
  yard: "yard_cleanup",
  leaf: "yard_cleanup",
  tasks: "small_tasks",
  gutter: "gutter_cleaning",
  inspection: "property_inspection",
  check: "property_inspection",
};

/** Return the recommended default service. Admin override > seasonal > first available. */
const getSeasonalDefault = (available: ServiceOption[], adminFeatured?: string): string => {
  // Admin-configured featured service takes top priority
  if (adminFeatured && available.some((s) => s.service_type === adminFeatured)) return adminFeatured;
  const month = new Date().getMonth(); // 0-indexed
  // Dec-Feb → snow_removal, Mar-May → yard_cleanup, Jun-Aug → handyman, Sep-Nov → gutter_cleaning/yard_cleanup
  let preferred: string[];
  if (month >= 11 || month <= 1) preferred = ["snow_removal", "property_inspection", "handyman"];
  else if (month >= 2 && month <= 4) preferred = ["yard_cleanup", "gutter_cleaning", "handyman"];
  else if (month >= 5 && month <= 7) preferred = ["handyman", "small_tasks", "property_inspection"];
  else preferred = ["yard_cleanup", "gutter_cleaning", "property_inspection"];
  for (const p of preferred) {
    if (available.some((s) => s.service_type === p)) return p;
  }
  return available[0]?.service_type || "";
};

/** Check if a service is seasonally recommended right now. */
const isSeasonallyRecommended = (serviceType: string): boolean => {
  const month = new Date().getMonth();
  if (month >= 11 || month <= 1) return serviceType === "snow_removal";
  if (month >= 2 && month <= 4) return serviceType === "yard_cleanup";
  if (month >= 5 && month <= 7) return serviceType === "handyman";
  return serviceType === "yard_cleanup" || serviceType === "gutter_cleaning";
};

const SINGLE_DAY_SERVICES = ["snow_removal", "handyman", "yard_cleanup", "small_tasks", "gutter_cleaning", "property_inspection"];
const isSingleDayService = (serviceType: string) => SINGLE_DAY_SERVICES.includes(serviceType);

type TimeSlot = "am" | "pm" | "anytime" | "tbd";
const TIME_SLOTS: { value: TimeSlot; label: string; range: string; icon: any }[] = [
  { value: "am", label: "Morning", range: "Before noon", icon: Sunrise },
  { value: "pm", label: "Afternoon", range: "After noon", icon: Sun },
  { value: "anytime", label: "Anytime", range: "Flexible", icon: Clock },
  { value: "tbd", label: "TBD", range: "We'll coordinate", icon: CalendarIcon },
];

const SPECIFIC_TIMES = Array.from({ length: 27 }, (_, i) => {
  const h = Math.floor(i / 2) + 7;
  const m = i % 2 === 0 ? "00" : "30";
  return `${h.toString().padStart(2, "0")}:${m}`;
}).filter((t) => t <= "20:00");

const formatTime12 = (time: string) => {
  const [h, m] = time.split(":").map(Number);
  const suffix = h >= 12 ? "PM" : "AM";
  const hour12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
  return `${hour12}:${m.toString().padStart(2, "0")} ${suffix}`;
};

const resolveServiceType = (raw: string | null, available: ServiceOption[]): string => {
  if (!raw) return "";
  if (available.some((s) => s.service_type === raw)) return raw;
  const alias = SERVICE_ALIAS[raw];
  if (alias) {
    const match = available.find((s) => s.service_type.includes(alias));
    if (match) return match.service_type;
  }
  const partial = available.find((s) => s.service_type.includes(raw) || raw.includes(s.service_type));
  if (partial) return partial.service_type;
  return raw;
};

const getInitialBookingStatus = (
  pricingModel: PricingModel | null | undefined,
  serviceType?: string | null,
): BookingStatus =>
  QUOTE_FIRST_SERVICE_TYPES.has(serviceType || "") || (pricingModel && pricingModel !== "flat")
    ? "requested"
    : "confirmed";

const isPricingReviewService = (serviceType?: string | null) =>
  serviceType !== "property_inspection";

const getRequestedSubmitLabel = (serviceType?: string | null) =>
  isPricingReviewService(serviceType) ? "Send Quote Request" : "Send Request";

const getRequestedToastDescription = (serviceType?: string | null) =>
  isPricingReviewService(serviceType)
    ? "Adam has the details and will confirm the quote, timing, and next step."
    : "Adam has the details and will confirm timing and the next step.";

const mergeGuidedNotes = (existing: string, prefill: string | null | undefined) => {
  if (!prefill) return existing;
  if (!existing.trim()) return prefill;
  if (existing.includes(prefill)) return existing;
  return `${existing.trim()}\n${prefill}`;
};

const CHAT_URL = `${import.meta.env.VITE_SUPABASE_URL}/functions/v1/booking-assistant`;
const BOOKING_STATE_KEY = "adam_booking_state";
const CRASH_RETENTION_KEY = "adam_booking_draft";
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
const PHONE_REGEX = /^[0-9()+\-\s]{10,20}$/;
const formatEmailDate = (value: string) =>
  new Date(`${value}T00:00:00`).toLocaleDateString("en-CA", {
    weekday: "long",
    day: "numeric",
    month: "long",
    year: "numeric",
  });

const formatMoney = (value: number) => {
  const normalized = Number.isFinite(value) ? value : 0;
  return Number.isInteger(normalized) ? normalized.toFixed(0) : normalized.toFixed(2);
};

const ClientBooking = () => {
  const { user, loading: authLoading } = useAuth();
  const { toast } = useToast();
  const [searchParams] = useSearchParams();

  const intentFromStorage = (() => {
    try {
      const raw = sessionStorage.getItem("adam_booking_intent");
      if (raw) { sessionStorage.removeItem("adam_booking_intent"); localStorage.removeItem("adam_booking_intent_backup"); return JSON.parse(raw) as Record<string, string>; }
    } catch {
      // Ignore malformed retained booking intent.
    }
    // Fall back to localStorage backup (survives new-tab email confirmation)
    try {
      const raw = localStorage.getItem("adam_booking_intent_backup");
      if (raw) {
        const parsed = JSON.parse(raw);
        localStorage.removeItem("adam_booking_intent_backup");
        if (parsed._saved_at && Date.now() - parsed._saved_at > 2 * 60 * 60 * 1000) return null;
        const { _saved_at, ...intent } = parsed;
        return intent as Record<string, string>;
      }
    } catch {
      // Ignore malformed retained booking backup.
    }
    return null;
  })();

  const savedBookingState = (() => {
    try {
      const raw = sessionStorage.getItem(BOOKING_STATE_KEY);
      if (raw) { sessionStorage.removeItem(BOOKING_STATE_KEY); return JSON.parse(raw) as Record<string, any>; }
    } catch {
      // Ignore malformed draft booking state.
    }
    return null;
  })();

  const crashDraft = (() => {
    try {
      const raw = localStorage.getItem(CRASH_RETENTION_KEY);
      if (raw) {
        const parsed = JSON.parse(raw) as Record<string, any>;
        const savedAt = parsed.saved_at ? new Date(parsed.saved_at) : null;
        if (savedAt) {
          const hoursOld = differenceInHours(new Date(), savedAt);
          if (hoursOld > 72) {
            localStorage.removeItem(CRASH_RETENTION_KEY);
            return null;
          }
        }
        return parsed;
      }
    } catch {
      // Ignore malformed crash-recovery draft.
    }
    return null;
  })();

  const crashDraftAge = useMemo(() => {
    if (!crashDraft?.saved_at) return null;
    const savedAt = new Date(crashDraft.saved_at);
    const hours = differenceInHours(new Date(), savedAt);
    return { hours, savedAt, label: formatDistanceToNow(savedAt, { addSuffix: true }) };
  }, []);

  const [showResumeBanner, setShowResumeBanner] = useState(!!crashDraft && !savedBookingState);

  const preselectedService = searchParams.get("service") || intentFromStorage?.service || savedBookingState?.service || crashDraft?.service || null;
  const preselectedStart = searchParams.get("start") || intentFromStorage?.start || savedBookingState?.start || crashDraft?.start || null;
  const preselectedEnd = searchParams.get("end") || intentFromStorage?.end || savedBookingState?.end || crashDraft?.end || null;
  const preselectedStartTime = searchParams.get("startTime") || intentFromStorage?.startTime || savedBookingState?.startTime || crashDraft?.startTime || null;
  const preselectedEndTime = searchParams.get("endTime") || intentFromStorage?.endTime || savedBookingState?.endTime || crashDraft?.endTime || null;
  const savedPropertyId: string | null = savedBookingState?.selectedPropertyId || crashDraft?.selectedPropertyId || null;
  const savedMode: "choose" | "chat" | "form" | null = savedBookingState?.mode || crashDraft?.mode || null;
  const savedNotes: string = savedBookingState?.notes || crashDraft?.notes || "";
  const savedAddonIds: string[] = crashDraft?.selectedAddonIds || [];
  const savedRecurrence: "none" | "weekly" | "biweekly" | "monthly" = crashDraft?.recurrence || "none";
  const hasPreselectedBookingIntent = Boolean(preselectedService || preselectedStart || preselectedEnd || preselectedStartTime);

  const navigate = useNavigate();
  const [mode, setMode] = useState<"choose" | "chat" | "form">(savedMode || (hasPreselectedBookingIntent ? "form" : "choose"));
  const [client, setClient] = useState<any>(null);
  const [properties, setProperties] = useState<any[]>([]);
  const [services, setServices] = useState<ServiceOption[]>([]);
  const [addons, setAddons] = useState<any[]>([]);
  const [clientPricing, setClientPricing] = useState<any[]>([]);
  const [cashDiscountEnabled, setCashDiscountEnabled] = useState(false);
  const [cashDiscountPct, setCashDiscountPct] = useState(3);
  const [featuredService, setFeaturedService] = useState<string>("");
  const [loading, setLoading] = useState(true);
  const rateOverrides = useRateOverrides();
  const [selectedPropertyId, setSelectedPropertyId] = useState<string | null>(null);
  const [chatMessages, setChatMessages] = useState<ChatMessage[]>([]);
  const [chatInput, setChatInput] = useState("");
  const [streaming, setStreaming] = useState(false);
  const [extractedBooking, setExtractedBooking] = useState<any>(null);
  const [guidedBookingState, setGuidedBookingState] = useState<GuidedBookingState | null>(null);
  const [guidedBookingRoute, setGuidedBookingRoute] = useState<GuidedBookingRoute | null>(null);
  const [bookingConfirmed, setBookingConfirmed] = useState(false);
  const [selectedService, setSelectedService] = useState<string>(preselectedService || "");
  const [startDate, setStartDate] = useState<Date | undefined>(preselectedStart ? new Date(preselectedStart + "T00:00:00") : undefined);
  const [endDate, setEndDate] = useState<Date | undefined>(preselectedEnd ? new Date(preselectedEnd + "T00:00:00") : undefined);
  const [dropOffTime, setDropOffTime] = useState<string>(preselectedStartTime || "");
  // pickUpTime removed — home services use single arrival time via time slots
  const [notes, setNotes] = useState(savedNotes);
  const [submitting, setSubmitting] = useState(false);
  const [successData, setSuccessData] = useState<any>(null);
  const [selectedAddonIds, setSelectedAddonIds] = useState<string[]>(savedAddonIds);
  const [showAddons, setShowAddons] = useState(false);
  const [showPropertySelector, setShowPropertySelector] = useState(false);
  const [recurrence, setRecurrence] = useState<"none" | "weekly" | "biweekly" | "monthly">(savedRecurrence);
  const [selectedTimeSlots, setSelectedTimeSlots] = useState<TimeSlot[]>([]);
  const [specificTime, setSpecificTime] = useState<string>("");
  const [showSpecificTime, setShowSpecificTime] = useState(false);
  const [isNotesListening, setIsNotesListening] = useState(false);
  const [calendarOpen, setCalendarOpen] = useState(true);
  const afterCalendarRef = useRef<HTMLDivElement>(null);

  // Inline contact editing state
  const existingPhone = parsePhone(client?.phone || "");
  const [inlineEmail, setInlineEmail] = useState("");
  const [inlinePhoneCountry, setInlinePhoneCountry] = useState(existingPhone.countryCode);
  const [inlinePhoneDigits, setInlinePhoneDigits] = useState(existingPhone.digits);
  const [contactSaving, setContactSaving] = useState(false);

  // Sync inline state when client loads
  useEffect(() => {
    if (client) {
      const parsed = parsePhone(client.phone || "");
      setInlinePhoneCountry(parsed.countryCode);
      setInlinePhoneDigits(parsed.digits);
      setInlineEmail(client.email || user?.email || "");
    }
  }, [client?.id]);
  const notesRecognitionRef = useRef<any>(null);
  const [availabilityBlocks, setAvailabilityBlocks] = useState<any[]>([]);
  const [datesUnavailable, setDatesUnavailable] = useState(false);
  const [activeBookings, setActiveBookings] = useState<any[]>([]);
  const [exceptionRequested, setExceptionRequested] = useState(false);
  const [requestingException, setRequestingException] = useState(false);
  const [isListening, setIsListening] = useState(false);
  const recognitionRef = useRef<any>(null);
  const [promoInput, setPromoInput] = useState(() => sessionStorage.getItem("adam_promo_code") || "");
  const [promoApplied, setPromoApplied] = useState<{ code: string; description: string; discount: string } | null>(null);
  const [promoError, setPromoError] = useState("");
  const [promoLoading, setPromoLoading] = useState(false);
  const [showPromo, setShowPromo] = useState(() => !!sessionStorage.getItem("adam_promo_code"));
  const [caregiver, setCaregiver] = useState<{ name: string; avatar_url?: string | null; verified?: boolean; specialities?: string[]; bio?: string | null; certification_date?: string | null; member_since?: string | null } | null>(null);

  // ── Crash retention ──
  useEffect(() => {
    if (mode === "chat") return;
    const draft: Record<string, any> = {};
    if (selectedService) draft.service = selectedService;
    if (startDate) draft.start = format(startDate, "yyyy-MM-dd");
    if (endDate) draft.end = format(endDate, "yyyy-MM-dd");
    if (dropOffTime) draft.startTime = dropOffTime;
    if (notes) draft.notes = notes;
    if (selectedPropertyId) draft.selectedPropertyId = selectedPropertyId;
    if (selectedAddonIds.length > 0) draft.selectedAddonIds = selectedAddonIds;
    if (recurrence !== "none") draft.recurrence = recurrence;
    if (mode !== "choose") draft.mode = mode;
    if (Object.keys(draft).length > 0) {
      draft.saved_at = crashDraft?.saved_at || new Date().toISOString();
      localStorage.setItem(CRASH_RETENTION_KEY, JSON.stringify(draft));
    } else {
      localStorage.removeItem(CRASH_RETENTION_KEY);
    }
  }, [selectedService, startDate, endDate, dropOffTime, notes, selectedPropertyId, selectedAddonIds, recurrence, mode]);

  const clearDraft = () => localStorage.removeItem(CRASH_RETENTION_KEY);

  const handleStartOver = () => {
    clearDraft();
    setSelectedService("");
    setStartDate(undefined);
    setEndDate(undefined);
    setDropOffTime("");
    setNotes("");
    setSelectedAddonIds([]);
    setRecurrence("none");
    setSelectedTimeSlots([]);
    setSpecificTime("");
    setShowSpecificTime(false);
    setMode("choose");
    setExtractedBooking(null);
    setGuidedBookingState(null);
    setGuidedBookingRoute(null);
    setBookingConfirmed(false);
    setChatMessages([]);
    setShowResumeBanner(false);
    setExceptionRequested(false);
    setDatesUnavailable(false);
    if (properties.length) setSelectedPropertyId(properties[0]?.id || null);
  };

  const handleCancelActiveBooking = async (bookingId: string) => {
    navigate(`/client/booking/${bookingId}?action=cancel`);
  };

  const signedInEmail = user?.email?.trim() || "";
  const contactEmail = inlineEmail || client?.email?.trim() || signedInEmail;
  const contactPhone = client?.phone?.trim() || "";
  const emailValid = EMAIL_REGEX.test(contactEmail);
  const phoneDigitsClean = stripPhone(inlinePhoneDigits);
  const hasInlinePhone = phoneDigitsClean.length > 0;
  const phoneValid = phoneDigitsClean.length === 10 && phoneDigitsClean.slice(3, 6) !== "555";
  const phoneInvalid = hasInlinePhone && !phoneValid;
  const missingRequiredContact = !emailValid;
  const missingPhone = !contactPhone && !phoneValid;

  const saveInlineContact = async () => {
    if (!client || !emailValid || phoneInvalid) return;
    setContactSaving(true);
    const nextPhone = phoneValid ? toE164(inlinePhoneCountry, inlinePhoneDigits) : null;
    const { error } = await supabase
      .from("clients")
      .update({ email: contactEmail, phone: nextPhone })
      .eq("id", client.id);
    if (error) {
      toast({ title: "Could not save contact details", description: error.message, variant: "destructive" });
    } else {
      toast({ title: "Contact details saved ✓" });
      // Refresh the client data
      setClient((prev: any) => prev ? { ...prev, email: contactEmail, phone: nextPhone } : prev);
    }
    setContactSaving(false);
  };

  // ── Voice helpers ──
  const createRecognition = (onResult: (f: string, i: string) => void, onEnd: () => void, initial: string) => {
    const SR = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition;
    if (!SR) { toast({ title: "Voice not supported", description: "Try Chrome or Safari.", variant: "destructive" }); return null; }
    const r = new SR();
    r.continuous = true; r.interimResults = true; r.lang = "en-US";
    let final = initial;
    r.onresult = (e: any) => { let interim = ""; for (let i = e.resultIndex; i < e.results.length; i++) { if (e.results[i].isFinal) final += e.results[i][0].transcript + " "; else interim += e.results[i][0].transcript; } onResult(final, interim); };
    r.onerror = () => onEnd(); r.onend = () => onEnd();
    return r;
  };

  const toggleVoice = () => {
    if (isListening && recognitionRef.current) { recognitionRef.current.stop(); setIsListening(false); return; }
    const r = createRecognition((f, i) => setChatInput(f + i), () => setIsListening(false), chatInput);
    if (!r) return; recognitionRef.current = r; r.start(); setIsListening(true);
  };

  const toggleNotesVoice = () => {
    if (isNotesListening && notesRecognitionRef.current) { notesRecognitionRef.current.stop(); setIsNotesListening(false); return; }
    const r = createRecognition((f, i) => setNotes(f + i), () => setIsNotesListening(false), notes);
    if (!r) return; notesRecognitionRef.current = r; r.start(); setIsNotesListening(true);
  };

  const handleAddProperty = () => {
    const state: Record<string, any> = { selectedPropertyId, service: selectedService || undefined, start: startDate ? format(startDate, "yyyy-MM-dd") : undefined, end: endDate ? format(endDate, "yyyy-MM-dd") : undefined, notes: notes || undefined, mode: mode !== "choose" ? mode : undefined };
    sessionStorage.setItem(BOOKING_STATE_KEY, JSON.stringify(state));
    navigate("/client/property/new?returnTo=/client/book");
  };

  const selectProperty = (propertyId: string) => setSelectedPropertyId(propertyId);

  const applyPromoCode = async () => {
    const trimmed = promoInput.trim().toUpperCase();
    if (!trimmed) return;
    setPromoLoading(true);
    setPromoError("");
    try {
      const { data: promo } = await supabase
        .from("promo_codes")
        .select("*")
        .eq("code", trimmed)
        .eq("active", true)
        .maybeSingle();
      if (!promo) { setPromoError("Code not recognised"); return; }
      if (promo.expires_at && new Date(promo.expires_at) < new Date()) { setPromoError("This code has expired"); return; }
      if (promo.max_uses && promo.current_uses >= promo.max_uses) { setPromoError("This code has reached its limit"); return; }

      // Check if already applied
      if (client?.id) {
        const { data: existing } = await supabase
          .from("client_promo_codes").select("id")
          .eq("client_id", client.id).eq("promo_code_id", promo.id).maybeSingle();
        if (!existing) {
          await supabase.from("client_promo_codes").insert({ client_id: client.id, promo_code_id: promo.id });
        }
      }

      sessionStorage.removeItem("adam_promo_code");
      const disc = promo.discount_type === "percentage" ? `${promo.discount_value}% off` : `$${promo.discount_value} rate`;
      setPromoApplied({ code: promo.code, description: promo.description || "", discount: disc });
      toast({ title: "Code applied! 🎉", description: promo.description || disc });
    } catch (err: any) {
      setPromoError(err.message || "Something went wrong");
    } finally {
      setPromoLoading(false);
    }
  };

  const fetchAvailability = useCallback(async () => {
    const { data } = await supabase.from("availability_blocks").select("*").eq("block_type", "unavailable");
    setAvailabilityBlocks(data || []);
  }, []);

  // ── Data fetch ──
  useEffect(() => {
    if (authLoading) return;
    if (!user) { setLoading(false); return; }
    const fetchData = async () => {
      const [servicesRes, clientRes, addonsRes, cashSettingsRes, caregiverRes] = await Promise.all([
        supabase.from("pricing_rules").select("*").order("service_type"),
        supabase.from("clients").select("*").eq("user_id", user.id),
        supabase.from("service_addons").select("*").eq("active", true),
        supabase.from("app_settings").select("key, value").in("key", ["cash_discount_enabled", "cash_discount_pct", "featured_service"]),
        supabase.from("caregiver_profiles").select("name, avatar_url, verified, specialities, bio, certification_date, member_since").limit(1),
      ]);
      if (servicesRes.error) {
        toast({ title: "Couldn't load services", description: "Please refresh the page and try again.", variant: "destructive" });
      }
      const loadedServices = servicesRes.data || [];
      setServices(loadedServices);
      if (preselectedService && loadedServices.length) {
        const resolved = resolveServiceType(preselectedService, loadedServices);
        if (resolved) setSelectedService(resolved);
      }
      setAddons(addonsRes.data || []);
      if (caregiverRes.data?.length) {
        setCaregiver(caregiverRes.data[0]);
      }
      const csMap: Record<string, string> = {};
      if (cashSettingsRes.data) {
        cashSettingsRes.data.forEach((r: any) => { csMap[r.key] = r.value || ""; });
        setCashDiscountEnabled(csMap.cash_discount_enabled === "true");
        setCashDiscountPct(Number(csMap.cash_discount_pct || "3"));
        if (csMap.featured_service) setFeaturedService(csMap.featured_service);
      }
      const adminFeatured = csMap.featured_service || "";
      await fetchAvailability();
      if (clientRes.data?.length) {
        const c = clientRes.data[0];
        setClient(c);
        const [propertyRes, activeBookingsRes, clientPricingRes] = await Promise.all([
          supabase.from("properties").select("*").eq("client_id", c.id).eq("status", "active"),
          supabase.from("bookings").select("*, booking_properties(property_id)").eq("client_id", c.id).in("status", ["requested", "approved", "deposit_paid", "confirmed", "active"]).order("created_at", { ascending: false }),
          supabase.from("client_pricing").select("*").eq("client_id", c.id),
        ]);
        const propertyList = propertyRes.data || [];
        setProperties(propertyList);
        setClientPricing(clientPricingRes.data || []);
        const propertyNameById = new Map(propertyList.map((p: any) => [p.id, formatPropertyDisplayName(p)]));
        setActiveBookings((activeBookingsRes.data || []).map((b: any) => ({
          ...b,
          propertyName: propertyNameById.get((b.booking_properties || [])[0]?.property_id) || null,
        })));
        if (propertyList.length) {
          if (savedPropertyId && propertyList.some((p: any) => p.id === savedPropertyId)) {
            setSelectedPropertyId(savedPropertyId);
          } else if (hasPreselectedBookingIntent && propertyList.length > 1) {
            setSelectedPropertyId(null);
          } else {
            setSelectedPropertyId(propertyList[0].id);
          }
        }
        if (!preselectedService) {
          setSelectedService(c.last_service_type || getSeasonalDefault(loadedServices, adminFeatured));
        }
        if (savedBookingState) {
          toast({ title: "Welcome back!", description: "Your selections are right where you left them." });
        } else if (crashDraft && !savedBookingState) {
          if (!showResumeBanner) {
            toast({ title: "Your saved booking has been restored.", description: crashDraftAge?.label ? `Started ${crashDraftAge.label}.` : "We saved your progress from last time." });
          }
        }
      } else if (!preselectedService && loadedServices.length) {
        // New user with no client record — pick a seasonal default
        setSelectedService(getSeasonalDefault(loadedServices, adminFeatured));
      }
      setLoading(false);
    };
    fetchData();
  }, [user, authLoading, fetchAvailability]);

  // Realtime availability
  useEffect(() => {
    const channel = supabase.channel("booking-availability").on("postgres_changes", { event: "*", schema: "public", table: "availability_blocks" }, () => fetchAvailability()).subscribe();
    return () => { supabase.removeChannel(channel); };
  }, [fetchAvailability]);

  // ── Helpers ──
  const saveBookingProperty = async (bookingId: string) => {
    if (!selectedPropertyId) return;
    const { error } = await supabase.from("booking_properties").insert({ booking_id: bookingId, property_id: selectedPropertyId });
    if (error) {
      // Rollback: delete the orphaned booking
      await supabase.from("bookings").delete().eq("id", bookingId);
      throw new Error("Failed to link property to booking. Please try again.");
    }
  };

  const saveBookingAddons = async (bookingId: string) => {
    if (selectedAddonIds.length === 0) return;
    const { error } = await supabase.from("booking_addons").insert(selectedAddonIds.map((addon_id) => {
      const addon = addons.find((a: any) => a.id === addon_id);
      return { booking_id: bookingId, addon_id, price: addon?.price || 0 };
    }));
    if (error) console.error("[ADAM] Failed to save booking addons:", error.message);
  };

  const isAddonAvailable = (addon: any) => {
    return !addon.requires_approval;
  };

  const isRelevantHomeServiceAddon = (addon: any) => {
    const category = String(addon?.category || "").toLowerCase();
    const searchable = `${addon?.name || ""} ${addon?.description || ""} ${category}`.toLowerCase();

    const homeServiceCategories = [
      "property",
      "home-care",
      "cleanup",
      "seasonal",
      "materials",
      "handyman",
      "yard",
      "landscaping",
      "snow",
      "waste",
      "hauling",
    ];

    const petKeywords = [
      "nail",
      "teeth",
      "brush",
      "bath",
      "walk",
      "playtime",
      "pet",
      "medication",
      "groom",
      "enrichment",
      "litter",
      "feeding",
      "dog",
      "cat",
    ];

    if (homeServiceCategories.includes(category)) return true;
    if (petKeywords.some((keyword) => searchable.includes(keyword))) return false;

    return false;
  };

  const toggleAddon = (addonId: string) => setSelectedAddonIds((prev) => prev.includes(addonId) ? prev.filter((id) => id !== addonId) : [...prev, addonId]);

  const toggleTimeSlot = (slot: TimeSlot) => {
    setSelectedTimeSlots((prev) => prev.includes(slot) ? prev.filter((s) => s !== slot) : [...prev, slot]);
    setSpecificTime("");
    setShowSpecificTime(false);
  };

  const handleSpecificTime = (time: string) => {
    setSpecificTime(time);
    setSelectedTimeSlots([]);
  };

  // Selected property details
  const selectedProperty = useMemo(() => properties.find((p) => p.id === selectedPropertyId) || null, [properties, selectedPropertyId]);
  const selectedPropertyName = selectedProperty ? formatPropertyDisplayName(selectedProperty) : null;
  const selectedPropertyAddress = selectedProperty ? formatPropertyAddress(selectedProperty) : null;

  const visibleAddons = useMemo(() => addons.filter((a: any) => a.active && isRelevantHomeServiceAddon(a)), [addons]);

  useEffect(() => {
    setSelectedAddonIds((prev) => prev.filter((id) => visibleAddons.some((addon: any) => addon.id === id)));
  }, [visibleAddons]);

  const otherAddons = useMemo(() => {
    return visibleAddons;
  }, [visibleAddons]);

  const buildTimeNote = () => {
    if (selectedTimeSlots.length > 0) {
      const labels = selectedTimeSlots.map((s) => TIME_SLOTS.find((t) => t.value === s)?.label).filter(Boolean);
      return `Preferred time: ${labels.join(", ")}`;
    }
    if (specificTime) return `Preferred time: ${formatTime12(specificTime)}`;
    return "";
  };

  const checkAvailability = useCallback((from: Date | undefined, to: Date | undefined) => {
    setDatesUnavailable(false); setExceptionRequested(false);
    if (!from || !to) return;
    const start = format(from, "yyyy-MM-dd"); const end = format(to, "yyyy-MM-dd");
    const hasConflict = availabilityBlocks.some((block: any) => start <= block.end_date && end >= block.start_date);
    setDatesUnavailable(hasConflict);
  }, [availabilityBlocks, client, selectedService]);

  const handleExceptionRequest = async () => {
    if (!client || !startDate || !endDate) return;
    setRequestingException(true);
    const message = `VIP client ${client.name} requested service for ${format(startDate, "MMM d")}–${format(endDate, "MMM d")} (${selectedService.replace(/_/g, " ")}) despite unavailability.`;
    void sendAdminInAppNotification({
      entityId: client.id,
      type: "vip_exception",
      message,
    });
    void sendSlackNotification("vip_exception", `VIP exception request from ${client.name}`, [
      { label: "Client", value: client.name || user?.email || "Client" },
      { label: "Dates", value: `${format(startDate, "MMM d")}–${format(endDate, "MMM d")}` },
      { label: "Service", value: selectedService.replace(/_/g, " ") },
    ]);
    setExceptionRequested(true); setRequestingException(false);
    toast({ title: "Request sent", description: "We'll review your request and get back to you shortly." });
  };

  const notifyBookingCreated = async (bookingId: string, serviceType: string, start: string, end: string, bookingStatus: BookingStatus, bookingNotes?: string | null) => {
    const clientName = client?.preferred_name || client?.first_name || client?.name || user?.email || "Client";
    const serviceLabel = serviceType.replace(/_/g, " ");
    const isReviewRequest = bookingStatus === "requested";

    void sendAdminAlert({
      templateName: "admin-new-booking",
      idempotencyKey: `admin-new-booking-${bookingId}`,
      templateData: {
        clientName,
        bookingStatus,
        serviceType: serviceLabel,
        startDate: formatEmailDate(start),
        endDate: formatEmailDate(end),
        notes: bookingNotes || undefined,
      },
      slackTitle: `${isReviewRequest ? "New request" : "New booking"}: ${clientName}`,
      slackEventType: "new_booking",
      slackFields: [
        { label: "Client", value: clientName },
        { label: "Status", value: isReviewRequest ? "Awaiting review" : "Confirmed" },
        { label: "Service", value: serviceLabel },
        { label: "Dates", value: `${start} — ${end}` },
        ...(selectedPropertyName ? [{ label: "Property", value: selectedPropertyName }] : []),
      ],
    });

    if (contactEmail) {
      const emailResult = await sendTransactionalEmailNotification({
        templateName: "booking-confirmation",
        recipientEmail: contactEmail,
        idempotencyKey: `booking-confirmation-${bookingId}-${contactEmail}`,
        templateData: {
          clientName,
          bookingStatus,
          serviceType: serviceLabel,
          startDate: formatEmailDate(start),
          endDate: formatEmailDate(end),
          startDateIso: start,
          endDateIso: end,
          notes: bookingNotes || undefined,
        },
      });

      if (!emailResult.ok) {
        void sendAdminAlert({
          templateName: "admin-new-message",
          idempotencyKey: `booking-confirmation-failed-${bookingId}`,
          templateData: {
            senderName: "Booking confirmation email failed",
            bookingInfo: `${serviceLabel} (${start} — ${end})`,
            messagePreview: `${isReviewRequest ? "Request" : "Booking"} ${bookingId} was created, but the ${isReviewRequest ? "request" : "confirmation"} email to ${contactEmail} failed. ${emailResult.error || "Unknown send error."}`,
          },
          slackTitle: `${isReviewRequest ? "Request" : "Booking confirmation"} email failed for ${clientName}`,
          slackEventType: "booking_email_failed",
          slackFields: [
            { label: "Client", value: clientName },
            { label: "Email", value: contactEmail },
            { label: "Booking", value: bookingId },
            { label: "Error", value: emailResult.error || "Unknown error" },
          ],
        });

        toast({
          title: isReviewRequest ? "Request sent" : "Booking confirmed",
          description: `Your ${isReviewRequest ? "request" : "job"} is ${isReviewRequest ? "in" : "booked"}, but the ${isReviewRequest ? "request" : "confirmation"} email to ${contactEmail} did not go out. Adam has your details and can help at AdamNarciso@gmail.com.`,
        });
      }
    }
  };

  // ── Chat ──
  const startChat = () => {
    setMode("chat");
    setExtractedBooking(null);
    setGuidedBookingState(null);
    setGuidedBookingRoute(null);
    const propertyName = selectedPropertyName;
    setChatMessages([{
      role: "assistant",
      content: propertyName
        ? `Hi there! 👋 I'm here to help you book a home service for ${propertyName}. Tell me what you need — snow removal, handyman work, yard cleanup, or anything else — and I'll find the best fit.`
        : `Hi there! 👋 I'd love to help you book a home service. Tell me what you need done, when you'd like it, and any special details — and I'll take care of the rest.`,
    }]);
  };

  const continueWithGuidedRoute = () => {
    if (!guidedBookingRoute) return;
    setSelectedService(guidedBookingRoute.serviceType);
    setNotes((prev) => mergeGuidedNotes(prev, guidedBookingRoute.prefillNotes));
    setMode("form");
  };

  const sendChatMessage = async (inputText: string) => {
    const trimmed = inputText.trim();
    if (!trimmed || !client || streaming) return;
    const userMsg: ChatMessage = { role: "user", content: trimmed };
    const allMessages = [...chatMessages, userMsg];
    setChatMessages(allMessages);
    setChatInput("");

    const guidedReply = getGuidedBookingReply({
      message: trimmed,
      state: guidedBookingState,
      services,
      propertyName: selectedPropertyName || null,
    });

    if (guidedReply.handled) {
      setGuidedBookingState(guidedReply.nextState || null);
      if (guidedReply.route) {
        setGuidedBookingRoute(guidedReply.route);
        setSelectedService(guidedReply.route.serviceType);
      } else {
        setGuidedBookingRoute(null);
      }
      if (guidedReply.assistantMessage) {
        setChatMessages([...allMessages, { role: "assistant", content: guidedReply.assistantMessage }]);
      }
      return;
    }

    setStreaming(true);
    let assistantContent = ""; let toolCallData: any = null;
    const abortCtrl = new AbortController();
    const chatTimeout = setTimeout(() => abortCtrl.abort(), 60_000); // 60s timeout
    try {
      const { data: { session } } = await supabase.auth.getSession();
      if (!session?.access_token) throw new Error("No active session");
      const resp = await fetch(CHAT_URL, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          apikey: import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY,
          Authorization: `Bearer ${session.access_token}`,
        },
        body: JSON.stringify({
          messages: allMessages.map((m) => ({ role: m.role, content: m.content })),
          client_id: client.id,
          selected_property_id: selectedPropertyId,
        }),
        signal: abortCtrl.signal,
      });
      if (!resp.ok) { const errData = await resp.json().catch(() => ({})); throw new Error(errData.error || `Error ${resp.status}`); }
      if (!resp.body) throw new Error("No response body");
      const reader = resp.body.getReader(); const decoder = new TextDecoder();
      let textBuffer = ""; let toolCallArgs = ""; let isToolCall = false;
      const updateAssistant = () => {
        setChatMessages((prev) => {
          const last = prev[prev.length - 1];
          if (last?.role === "assistant" && prev.length === allMessages.length + 1) return prev.map((m, i) => i === prev.length - 1 ? { ...m, content: assistantContent, toolCall: toolCallData } : m);
          return [...prev, { role: "assistant", content: assistantContent, toolCall: toolCallData }];
        });
      };
      while (true) {
        const { done, value } = await reader.read(); if (done) break;
        textBuffer += decoder.decode(value, { stream: true });
        let newlineIndex: number;
        while ((newlineIndex = textBuffer.indexOf("\n")) !== -1) {
          let line = textBuffer.slice(0, newlineIndex); textBuffer = textBuffer.slice(newlineIndex + 1);
          if (line.endsWith("\r")) line = line.slice(0, -1);
          if (!line.startsWith("data: ")) continue;
          const jsonStr = line.slice(6).trim(); if (jsonStr === "[DONE]") break;
          try {
            const parsed = JSON.parse(jsonStr); const delta = parsed.choices?.[0]?.delta;
            if (delta?.content) { assistantContent += delta.content; updateAssistant(); }
            if (delta?.tool_calls) { isToolCall = true; for (const tc of delta.tool_calls) { if (tc.function?.arguments) toolCallArgs += tc.function.arguments; } }
          } catch {
            // Ignore malformed stream chunks from the booking assistant.
          }
        }
      }
      if (isToolCall && toolCallArgs) {
        try {
          toolCallData = JSON.parse(toolCallArgs);
          setExtractedBooking(toolCallData);
          setGuidedBookingState(null);
          setGuidedBookingRoute(null);
          updateAssistant();
        } catch {
          console.error("Failed to parse tool call args:", toolCallArgs);
        }
      }
      clearTimeout(chatTimeout);
    } catch (err: any) {
      clearTimeout(chatTimeout);
      const msg = abortCtrl.signal.aborted
        ? "The live assistant took too long, so I'm switching to guided booking."
        : "The live assistant isn't available right now, so I'm switching to guided booking.";
      toast({ title: "Using guided booking", description: msg });
      setChatMessages((prev) => [...prev, { role: "assistant", content: getLowConfidenceBookingReply() }]);
    }
    setStreaming(false);
  };

  const confirmAIBooking = async () => {
    if (!extractedBooking || !client) return;
    if (missingRequiredContact) { toast({ title: "Email required", description: "We need a valid email address to send your confirmation and booking updates.", variant: "destructive" }); return; }
    if (!selectedPropertyId) { toast({ title: "Please select a property", variant: "destructive" }); return; }
    setSubmitting(true);
    const extractedService = services.find((service) => service.service_type === extractedBooking.service_type);
    const extractedPricing = extractedService ? resolveServicePricing(extractedService) : null;
    const bookingStatus = getInitialBookingStatus(extractedPricing?.model, extractedBooking.service_type);
    try {
      const { data: inserted, error } = await supabase.from("bookings").insert({
        client_id: client.id, service_type: extractedBooking.service_type, start_date: extractedBooking.start_date, end_date: extractedBooking.end_date, notes: extractedBooking.notes || null, payment_contact_email: contactEmail, payment_contact_phone: phoneValid ? toE164(inlinePhoneCountry, inlinePhoneDigits) : (contactPhone || null), status: bookingStatus, payment_state: "none", assigned_caregiver: null,
      }).select().single();
      if (error) { if (error.code === "23505") { toast({ title: "That slot was just taken", description: "Please choose different dates and try again." }); await fetchAvailability(); setSubmitting(false); return; } throw error; }
      cacheBookingSnapshot(inserted);
      await saveBookingProperty(inserted.id); await saveBookingAddons(inserted.id);
      await supabase.from("clients").update({ last_service_type: extractedBooking.service_type }).eq("id", client.id);
      await notifyBookingCreated(inserted.id, extractedBooking.service_type, extractedBooking.start_date, extractedBooking.end_date, bookingStatus, extractedBooking.notes || null);
      import("@/lib/onboarding-events").then(({ logOnboardingEvent }) =>
        logOnboardingEvent("first_booking", { service: extractedBooking.service_type })
      ).catch(() => {});
      clearDraft(); navigate(`/client/booking/${inserted.id}`);
    } catch (err: any) {
      const friendlyMsg = err?.message?.includes("row-level security") ? "Your session may have expired. Please sign out and back in." : err?.message?.includes("check") ? "Something didn't match — please double-check your service and dates." : err?.message || "Something went wrong. Please try again.";
      toast({ title: "Couldn't save booking request", description: friendlyMsg, variant: "destructive" });
    }
    setSubmitting(false);
  };

  // ── Form submit ──
  const handleFormSubmit = async () => {
    if (!client || !selectedService || !startDate || (!endDate && !isSingleDayService(selectedService))) { toast({ title: "Missing info", description: "Please select a service and dates.", variant: "destructive" }); return; }
    if (missingRequiredContact) { toast({ title: "Email required", description: "We need a valid email address to send your confirmation and booking updates.", variant: "destructive" }); return; }
    if (!selectedPropertyId) { toast({ title: "Please select a property", description: "We need to know which property needs service.", variant: "destructive" }); return; }
    setSubmitting(true);
    const effectiveEndDate = isSingleDayService(selectedService) ? startDate : endDate!;
    const timeNote = buildTimeNote();
    const fullNotes = [timeNote, notes.trim()].filter(Boolean).join("\n") || null;
    const bookingStatus = getInitialBookingStatus(currentPricingModel, selectedService);
    try {
      const { data: inserted, error } = await supabase.from("bookings").insert({
        client_id: client.id, service_type: selectedService, start_date: format(startDate, "yyyy-MM-dd"), end_date: format(effectiveEndDate, "yyyy-MM-dd"), notes: fullNotes, recurrence_rule: recurrence !== "none" ? recurrence : null, payment_contact_email: contactEmail, payment_contact_phone: phoneValid ? toE164(inlinePhoneCountry, inlinePhoneDigits) : (contactPhone || null), status: bookingStatus, payment_state: "none", assigned_caregiver: null,
      }).select().single();
      if (error) {
        if (error.code === "23505") { toast({ title: "That slot was just taken", description: "Please choose different dates and try again." }); await fetchAvailability(); setSubmitting(false); return; }
        if (error.code === "23514") { toast({ title: "Invalid service type", description: "Please select a valid service and try again.", variant: "destructive" }); setSubmitting(false); return; }
        if (error.code === "42501") { toast({ title: "Session expired", description: "Please sign out and sign back in, then try again.", variant: "destructive" }); setSubmitting(false); return; }
        throw error;
      }
      cacheBookingSnapshot(inserted);
      await saveBookingProperty(inserted.id);

      if (selectedAddonIds.length > 0) {
        try {
          await saveBookingAddons(inserted.id);
        } catch (addonErr) {
          console.error("[ADAM] Failed to save booking add-ons:", addonErr);
        }
      }

      if (recurrence !== "none") {
        try {
          const duration = differenceInDays(endDate, startDate);
          const recurringBookings = []; let nextStart = startDate;
          for (let i = 0; i < 12; i++) {
            if (recurrence === "weekly") nextStart = addWeeks(nextStart, 1);
            else if (recurrence === "biweekly") nextStart = addWeeks(nextStart, 2);
            else if (recurrence === "monthly") nextStart = addMonths(nextStart, 1);
            recurringBookings.push({ client_id: client.id, service_type: selectedService, start_date: format(nextStart, "yyyy-MM-dd"), end_date: format(addDays(nextStart, duration), "yyyy-MM-dd"), notes: notes.trim() || null, recurrence_rule: recurrence, parent_booking_id: inserted.id, status: bookingStatus, payment_state: "none", assigned_caregiver: null });
          }
          const { error: recurErr } = await supabase.from("bookings").insert(recurringBookings);
          if (recurErr) {
            console.error("[ADAM] Failed to create recurring bookings:", recurErr);
            toast({ title: "Recurring bookings issue", description: "Your initial booking was created, but we couldn't set up all recurring dates. Please contact us to arrange the remaining visits.", variant: "destructive" });
          } else if (selectedPropertyId) {
            const { data: allRecurring } = await supabase.from("bookings").select("id").eq("parent_booking_id", inserted.id);
            if (allRecurring?.length) {
              await supabase.from("booking_properties").insert(allRecurring.map((b: any) => ({ booking_id: b.id, property_id: selectedPropertyId })));
            }
          }
        } catch (recurrenceErr) {
          console.error("[ADAM] Recurring booking follow-up failed:", recurrenceErr);
          toast({ title: "Recurring bookings issue", description: "Your main booking was saved, but the repeat visits still need to be set up.", variant: "destructive" });
        }
      }

      void supabase.from("clients").update({ last_service_type: selectedService }).eq("id", client.id);
      await notifyBookingCreated(inserted.id, selectedService, format(startDate, "yyyy-MM-dd"), format(effectiveEndDate, "yyyy-MM-dd"), bookingStatus, fullNotes);
      clearDraft();
      toast({
        title: bookingStatus === "requested" ? "Request Sent" : "Booking Confirmed",
        description: bookingStatus === "requested"
          ? getRequestedToastDescription(selectedService)
          : "Your job is booked. Adam has the details and your status page is ready.",
      });
      setSuccessData({ bookingId: inserted.id, bookingStatus, serviceType: selectedService, startDate: startDate!, endDate: effectiveEndDate, propertyName: selectedPropertyName || "Property", notes: notes.trim() || undefined, estimatedTotal: currentPricingModel === "flat" ? estimatedTotal : undefined, cashTotal: currentPricingModel === "flat" && cashDiscountEnabled ? cashTotal : undefined, cashDiscountEnabled, arrivalTime: dropOffTime || undefined, addons: selectedAddonDetails, basePrice: currentPricingModel === "flat" ? currentBasePrice : 0, baseServiceTotal: currentPricingModel === "flat" ? baseServiceTotal : undefined, premiumSummary, needsPhoneFollowUp: missingPhone, contactEmail });
    } catch (err: any) {
      const friendlyMsg = err?.message?.includes("row-level security") ? "Your session may have expired. Please sign out and back in." : err?.message?.includes("check") ? "Something didn't match — please double-check your service and dates." : err?.message || "Something went wrong. Please try again.";
      toast({ title: "Couldn't save booking request", description: friendlyMsg, variant: "destructive" });
    }
    setSubmitting(false);
  };

  // ── Derived values ──
  const selectedServiceData = services.find((s) => s.service_type === selectedService);
  const activeBookingsWithPremium = useMemo(
    () =>
      activeBookings.map((booking) => {
        const premium = getClientBookingPremiumSummary({
          endDate: booking.end_date,
          pricingRules: services,
          rateOverrides,
          serviceType: booking.service_type,
          startDate: booking.start_date,
        });
        return { ...booking, premiumSummary: premium?.detail || null };
      }),
    [activeBookings, rateOverrides, services],
  );
  const selectedServicePricing = selectedServiceData ? resolveServicePricing(selectedServiceData) : null;
  const isSingleDay = isSingleDayService(selectedService);
  const daysCount = isSingleDay ? (startDate ? 1 : 0) : (startDate && endDate ? differenceInDays(endDate, startDate) : 0);
  const currentBasePrice = selectedServicePricing ? Number(selectedServicePricing.basePrice) : 0;
  const addonTotal = selectedAddonIds.reduce((s, id) => s + (visibleAddons.find((a: any) => a.id === id)?.price || 0), 0);
  const selectedAddonDetails = selectedAddonIds.map((id) => { const a = visibleAddons.find((x: any) => x.id === id); return a ? { name: a.name, price: Number(a.price) } : null; }).filter(Boolean) as { name: string; price: number }[];
  const getDiscount = (serviceType: string): number => {
    const specific = clientPricing.find((p: any) => p.service_type === serviceType && p.discount_pct);
    if (specific) return Number(specific.discount_pct);
    const global = clientPricing.find((p: any) => p.service_type === "all" && p.discount_pct);
    return global ? Number(global.discount_pct) : 0;
  };
  const currentDiscount = selectedService ? getDiscount(selectedService) : 0;
  const currentPricingModel = selectedServicePricing?.model ?? "flat";
  const currentPriceLabel = selectedServicePricing?.displayLabel;
  const currentPricingSummary = selectedServicePricing?.pricingSummary ?? "";
  const currentQuoteNote = selectedServicePricing?.quoteNote;
  const currentUnitLabel = selectedServicePricing?.unitLabel;
  const calendarRateOverrides = useMemo(
    () => mergeRateOverridesWithCanadianHolidayFallbacks(
      rateOverrides,
      selectedServicePricing?.holidayMultiplier ?? 1.5,
      selectedService || null,
    ),
    [rateOverrides, selectedService, selectedServicePricing?.holidayMultiplier],
  );
  const holidayMatchers = useMemo(
    () => buildRateOverrideMatchers(calendarRateOverrides.filter((override) => override.is_holiday), selectedService || null),
    [calendarRateOverrides, selectedService],
  );
  const premiumMatchers = useMemo(
    () => buildRateOverrideMatchers(calendarRateOverrides.filter((override) => !override.is_holiday), selectedService || null),
    [calendarRateOverrides, selectedService],
  );
  const selectedRangeOverrides = useMemo(() => {
    if (!startDate) return [];
    const rangeEnd = isSingleDay ? startDate : (endDate || startDate);
    return getApplicableRateOverridesForRange(startDate, rangeEnd, calendarRateOverrides, selectedService || null);
  }, [calendarRateOverrides, endDate, isSingleDay, selectedService, startDate]);
  const selectedDateOverridePricing = useMemo(() => {
    if (!startDate) return null;
    return resolveDateOverridePricing(currentBasePrice, startDate, calendarRateOverrides, selectedService || null);
  }, [calendarRateOverrides, currentBasePrice, selectedService, startDate]);
  const selectedPremiumDetails = useMemo(() => {
    if (!startDate || selectedRangeOverrides.length === 0) return null;

    const details = selectedRangeOverrides
      .map((override) => {
        const multiplier = override.override_type === "multiplier" ? (override.multiplier ?? 1) : null;
        const fixedPrice = override.override_type === "fixed" ? override.fixed_price : null;
        const priceText = fixedPrice !== null && fixedPrice !== undefined
          ? `$${Number(fixedPrice).toFixed(0)} fixed`
          : multiplier && multiplier > 1
            ? `${multiplier}x`
            : "Premium date";

        return {
          fixedPrice: fixedPrice ?? null,
          isHoliday: Boolean(override.is_holiday),
          label: override.label,
          multiplier,
          priceText,
          startDate: override.start_date,
        };
      })
      .filter((detail, index, list) =>
        list.findIndex((candidate) =>
          candidate.label === detail.label
          && candidate.priceText === detail.priceText
          && candidate.startDate === detail.startDate
        ) === index,
      );

    const primary = details.find((detail) => detail.isHoliday) || details[0];
    const remaining = details.length - 1;

    return {
      details,
      hasHoliday: details.some((detail) => detail.isHoliday),
      primary,
      shortLabel: `${primary.label} · ${primary.priceText}`,
      summaryLabel: `${primary.label} (${format(parseISO(primary.startDate), "MMM d")}) · ${primary.priceText}${remaining > 0 ? ` · +${remaining} more` : ""}`,
    };
  }, [selectedRangeOverrides, startDate]);
  const premiumSummary = useMemo(() => {
    if (!startDate || selectedRangeOverrides.length === 0) return null;
    const prefix = selectedRangeOverrides.some((override) => override.is_holiday)
      ? "Canadian holiday premium applies"
      : "Premium calendar override applies";
    const labelText = selectedPremiumDetails?.summaryLabel || Array.from(new Set(selectedRangeOverrides.map((override) => override.label))).join(", ");

    if (currentPricingModel !== "flat") {
      return `${prefix}: ${labelText}. Final pricing will follow Adam's calendar override for the selected date.`;
    }

    if (isSingleDay && selectedDateOverridePricing?.fixedPrice !== null) {
      return `${prefix}: ${labelText}. Adam currently has this date set to a fixed price of $${selectedDateOverridePricing.fixedPrice.toFixed(0)}.`;
    }

    if (isSingleDay && selectedDateOverridePricing && selectedDateOverridePricing.multiplier > 1) {
      return `${prefix}: ${labelText}. This selected date is currently ${selectedDateOverridePricing.multiplier}x the base rate.`;
    }

    return `${prefix}: ${labelText}.`;
  }, [currentPricingModel, isSingleDay, selectedDateOverridePricing, selectedPremiumDetails, selectedRangeOverrides, startDate]);
  const baseServiceTotal =
    currentPricingModel === "flat" && isSingleDay && selectedDateOverridePricing
      ? selectedDateOverridePricing.adjustedPrice
      : daysCount * currentBasePrice;
  const baseLineLabelOverride =
    currentPricingModel === "flat" && isSingleDay && selectedRangeOverrides.length > 0 && startDate
      ? selectedPremiumDetails?.primary.isHoliday
        ? `${selectedPremiumDetails.primary.label} premium rate`
        : `${format(startDate, "MMM d")} premium-date rate`
      : undefined;
  const premiumPricingDisplay = useMemo(() => {
    if (!selectedPremiumDetails || currentPricingModel === "flat") return currentPriceLabel;
    const primary = selectedPremiumDetails.primary;
    if (primary.fixedPrice !== null) return `${primary.label} · $${primary.fixedPrice.toFixed(0)} fixed`;
    if (!primary.multiplier || primary.multiplier <= 1) return currentPriceLabel;
    if (selectedServicePricing?.hourlyRates?.length) {
      const lowestRate = selectedServicePricing.hourlyRates[0].rate * primary.multiplier;
      const baseFeePrefix = selectedServicePricing.baseFee > 0
        ? `$${formatMoney(selectedServicePricing.baseFee)} + `
        : "";
      return `${primary.label} · From ${baseFeePrefix}$${formatMoney(lowestRate)}/hr`;
    }
    return `${primary.label} · ${primary.multiplier}x`;
  }, [currentPriceLabel, currentPricingModel, selectedPremiumDetails, selectedServicePricing?.baseFee, selectedServicePricing?.hourlyRates]);
  const premiumPricingSummary = useMemo(() => {
    if (!selectedPremiumDetails || currentPricingModel === "flat") return currentPricingSummary;
    const primary = selectedPremiumDetails.primary;
    if (!primary.multiplier || primary.multiplier <= 1 || !selectedServicePricing?.hourlyRates?.length) {
      return currentPricingSummary;
    }
    const adjustedRates = selectedServicePricing.hourlyRates
      .map((tier) => {
        const label = tier.label || (tier.crew_size === 1 ? "Solo" : `${tier.crew_size}-person crew`);
        return `${label} $${(tier.rate * primary.multiplier).toFixed(0)}/hr`;
      })
      .join(" • ");
    const baseFeePrefix = selectedServicePricing.baseFee > 0
      ? `Base fee $${formatMoney(selectedServicePricing.baseFee)} + `
      : "";
    return `${primary.label} holiday rate (${primary.multiplier}x): ${baseFeePrefix}${adjustedRates}`;
  }, [currentPricingModel, currentPricingSummary, selectedPremiumDetails, selectedServicePricing?.baseFee, selectedServicePricing?.hourlyRates]);
  const baseTotal = baseServiceTotal + addonTotal;
  const discountAmount = currentDiscount > 0 ? baseTotal * (currentDiscount / 100) : 0;
  const estimatedTotal = baseTotal - discountAmount;
  const cashTotal = cashDiscountEnabled ? estimatedTotal * (1 - cashDiscountPct / 100) : estimatedTotal;
  const canSubmit = selectedService && startDate && (isSingleDay || endDate) && !!selectedPropertyId && !datesUnavailable;
  const datesPreFilled = !!(preselectedStart && preselectedEnd);

  // ── Render: Success ──
  if (successData) return <DashboardLayout navItems={clientNavItems} title="Book Service"><BookingSuccessScreen {...successData} /></DashboardLayout>;

  // ── Render: Loading ──
  if (loading) return <DashboardLayout navItems={clientNavItems} title="Book Service"><div className="h-64 bg-muted rounded-2xl animate-pulse" /></DashboardLayout>;

  // Client record not found yet — avoid sending the user into the
  // property flow before provisioning has completed.
  if (!client) {
    return (
      <DashboardLayout navItems={clientNavItems} title="Book Service">
        <div className="flex flex-col items-center justify-center py-16 text-center max-w-md mx-auto">
          <div className="w-16 h-16 rounded-2xl bg-primary/10 flex items-center justify-center mb-6">
            <Wrench className="h-8 w-8 text-primary" />
          </div>
          <h2 className="text-xl font-medium mb-2">Almost there!</h2>
          <p className="text-sm text-muted-foreground font-sans mb-2 max-w-sm">
            We're finishing setting up your client account so you can book your first service.
          </p>
          <p className="text-xs text-muted-foreground/60 font-sans mb-6 max-w-sm">
            Logged in as: {user?.email}
          </p>
          <div className="flex gap-3">
            <Button onClick={() => window.location.reload()} variant="outline" className="rounded-full">
              Refresh
            </Button>
            <Button
              onClick={async () => {
                await supabase.auth.signOut();
                window.location.href = "/";
              }}
              variant="ghost"
              className="rounded-full text-muted-foreground"
            >
              Sign Out
            </Button>
          </div>
        </div>
      </DashboardLayout>
    );
  }

  // ── Render: No properties gate ──
  if (properties.length === 0) {
    const hasIntent = !!(preselectedStart && preselectedEnd);
    const intentStart = preselectedStart ? new Date(preselectedStart + "T00:00:00") : undefined;
    const intentEnd = preselectedEnd ? new Date(preselectedEnd + "T00:00:00") : undefined;
    let intentUnavailable = false;
    if (intentStart && intentEnd) {
      const s = format(intentStart, "yyyy-MM-dd"); const e = format(intentEnd, "yyyy-MM-dd");
      intentUnavailable = availabilityBlocks.some((block: any) => s <= block.end_date && e >= block.start_date);
    }
    const serviceName = (preselectedService || "").replace(/_/g, " ").replace(/\b\w/g, (c: string) => c.toUpperCase()) || "Service";
    return (
      <DashboardLayout navItems={clientNavItems} title="Book Service">
        <div className="max-w-md mx-auto space-y-6 pt-4">
          <BookingProgress currentStep={1} mode="form" datesPreFilled={datesPreFilled} />
          {hasIntent && !intentUnavailable && (
            <div className="rounded-2xl border border-primary/30 bg-primary/5 p-6 space-y-3 text-center">
              <div className="w-14 h-14 mx-auto rounded-2xl bg-primary/15 flex items-center justify-center"><CheckCircle2 className="h-7 w-7 text-primary" /></div>
              <h2 className="text-lg font-medium tracking-tight" style={{ fontFamily: "'Playfair Display', serif" }}>Great news — we're available!</h2>
              <p className="text-sm text-muted-foreground font-sans">{serviceName} · {intentStart && format(intentStart, "MMM d")} – {intentEnd && format(intentEnd, "MMM d, yyyy")}</p>
            </div>
          )}
          {hasIntent && intentUnavailable && (
            <div className="rounded-2xl border border-amber-500/30 bg-amber-500/5 p-6 space-y-3 text-center">
              <div className="w-14 h-14 mx-auto rounded-2xl bg-amber-500/15 flex items-center justify-center"><AlertCircle className="h-7 w-7 text-amber-600 dark:text-amber-400" /></div>
              <h2 className="text-lg font-medium tracking-tight" style={{ fontFamily: "'Playfair Display', serif" }}>Those dates aren't available</h2>
              <p className="text-sm text-muted-foreground font-sans">{serviceName} · {intentStart && format(intentStart, "MMM d")} – {intentEnd && format(intentEnd, "MMM d, yyyy")}</p>
              <Button variant="outline" size="sm" className="rounded-full" onClick={() => navigate("/")}>← Pick different dates</Button>
            </div>
          )}
          {(!hasIntent || !intentUnavailable) && (
            <div className="text-center space-y-4">
              <h1 className="text-xl font-medium tracking-tight" style={{ fontFamily: "'Playfair Display', serif" }}>{hasIntent ? "Next — tell us about your property" : "Which property needs service?"}</h1>
              <p className="text-sm text-muted-foreground font-sans max-w-sm mx-auto">{hasIntent ? "We just need your property details to schedule the service." : "Add your first property so we can get started."}</p>
              <Button onClick={handleAddProperty} className="rounded-full gap-2"><Home className="h-4 w-4" /> Add your first property</Button>
            </div>
          )}
        </div>
      </DashboardLayout>
    );
  }

  // ── Render: Chat mode ──
  if (mode === "chat") {
    return (
      <DashboardLayout navItems={clientNavItems} title="Book Service">
        <BookingChat
          chatMessages={chatMessages}
          chatInput={chatInput}
          setChatInput={setChatInput}
          streaming={streaming}
          isListening={isListening}
          extractedBooking={extractedBooking}
          bookingConfirmed={bookingConfirmed}
          submitting={submitting}
          selectedPropertyName={selectedPropertyName}
          caregiver={caregiver}
          onSendMessage={sendChatMessage}
          onConfirmBooking={confirmAIBooking}
          onAdjust={() => { setExtractedBooking(null); setChatInput("I'd like to adjust the plan..."); }}
          onToggleVoice={toggleVoice}
          onBack={() => {
            setGuidedBookingState(null);
            setGuidedBookingRoute(null);
            setMode("choose");
          }}
          onSwitchToForm={() => {
            if (guidedBookingRoute) {
              continueWithGuidedRoute();
              return;
            }
            setMode("form");
          }}
          recognitionRef={recognitionRef}
          services={services}
          clientId={client?.id}
          clientDiscount={currentDiscount}
          cashDiscountEnabled={cashDiscountEnabled}
          cashDiscountPct={cashDiscountPct}
          addons={visibleAddons}
          selectedAddonIds={selectedAddonIds}
          onToggleAddon={(id: string) => setSelectedAddonIds((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id])}
          clientAddress={client?.address}
          clientSecondaryAddress={(client as any)?.secondary_address}
          guidedRoute={guidedBookingRoute}
          onContinueToForm={continueWithGuidedRoute}
        />
      </DashboardLayout>
    );
  }

  // ── Render: Choose mode (pre-filled review or method chooser) ──
  if (mode === "choose") {
    if (datesPreFilled && selectedService && selectedPropertyId) {
      const resolvedService = services.find((s) => s.service_type === selectedService);
      const resolvedPricing = resolvedService ? resolveServicePricing(resolvedService) : null;
      const ResolvedIcon = serviceIcons[selectedService] || Sparkles;
      const disc = getDiscount(selectedService);
      const basePrice = Number(resolvedPricing?.basePrice || 0);

      return (
        <DashboardLayout navItems={clientNavItems} title="Book Service">
          <div className="max-w-lg mx-auto space-y-5 pt-2 pb-24">
            <BookingProgress currentStep={3} mode="form" datesPreFilled={datesPreFilled} />
            <div className="text-center space-y-1">
              <h1 className="text-xl font-medium tracking-tight" style={{ fontFamily: "'Playfair Display', serif" }}>Review & Confirm</h1>
              <p className="text-sm text-muted-foreground font-sans">Check your details, add any notes, and submit.</p>
            </div>
            {/* Service card */}
            <button onClick={() => setMode("form")} className="w-full rounded-xl border border-primary/30 bg-primary/5 p-4 flex items-center gap-3 text-left hover:bg-primary/10 transition-colors">
              <div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center shrink-0"><ResolvedIcon className="h-5 w-5 text-primary" /></div>
              <div className="flex-1 min-w-0">
                <p className="text-sm font-medium font-sans capitalize">{selectedService.replace(/_/g, " ")}</p>
                <p className="text-xs text-muted-foreground font-sans">
                  {resolvedPricing?.displayLabel || `$${basePrice}`}
                  {disc > 0 && resolvedPricing?.model === "flat" && <span className="text-amber-700 ml-1">({disc}% VIP discount)</span>}
                </p>
                {resolvedPricing?.pricingSummary && (
                  <p className="text-[11px] text-muted-foreground/80 font-sans mt-0.5">{resolvedPricing.pricingSummary}</p>
                )}
              </div>
              <span className="text-[11px] text-primary font-sans underline underline-offset-2 shrink-0">Change</span>
            </button>
            {/* Dates card */}
            <button onClick={() => { setStartDate(undefined); setEndDate(undefined); setCalendarOpen(true); setMode("form"); }} className="w-full rounded-xl border border-primary/30 bg-primary/5 p-4 flex items-center gap-3 text-left hover:bg-primary/10 transition-colors">
              <div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center shrink-0"><CalendarIcon className="h-5 w-5 text-primary" /></div>
              <div className="flex-1 min-w-0">
                <p className="text-sm font-medium font-sans">{startDate && format(startDate, "EEE, d MMM")}{dropOffTime && ` at ${formatTime12(dropOffTime)}`}{!isSingleDay && <>{" → "}{endDate && format(endDate, "EEE, d MMM yyyy")}</>}</p>
                <p className="text-xs text-muted-foreground font-sans">{daysCount} {daysCount === 1 ? "day" : "days"}</p>
              </div>
              <span className="text-[11px] text-primary font-sans underline underline-offset-2 shrink-0">Change</span>
            </button>
            {/* Property */}
            <div className="space-y-2">
              <div className="flex items-center justify-between px-1">
                <p className="text-xs font-medium font-sans text-muted-foreground">Your property</p>
                <button onClick={() => setShowPropertySelector(!showPropertySelector)} className="text-[11px] text-primary font-sans underline underline-offset-2">{showPropertySelector ? "Done" : "Change"}</button>
              </div>
              {showPropertySelector ? (
                <div className="grid gap-2">
                  {properties.map((p) => (
                    <button key={p.id} onClick={() => selectProperty(p.id)} className={cn("flex items-center gap-3 px-4 py-3 rounded-xl border-2 transition-all text-left w-full", selectedPropertyId === p.id ? "border-primary bg-primary/5 shadow-sm" : "border-border/50 hover:border-primary/30")}>
                      <Home className={cn("h-4 w-4 shrink-0", selectedPropertyId === p.id ? "text-primary" : "text-muted-foreground")} />
                      <div className="flex-1 min-w-0">
                        <p className="text-sm font-sans font-medium">{formatPropertyDisplayName(p)}</p>
                        {p.address_line1 && <p className="text-[11px] text-muted-foreground font-sans truncate">{formatPropertyAddress(p)}</p>}
                      </div>
                      {selectedPropertyId === p.id && <CheckCircle2 className="h-4 w-4 text-primary shrink-0" />}
                    </button>
                  ))}
                  <Button variant="outline" size="sm" className="rounded-full gap-1.5 w-full" onClick={handleAddProperty}><Home className="h-3.5 w-3.5" /> Add property</Button>
                </div>
              ) : (
                <div className="rounded-xl border border-primary/30 bg-primary/5 p-4 flex items-center gap-3">
                  <div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center shrink-0"><Home className="h-5 w-5 text-primary" /></div>
                  <div className="flex-1 min-w-0">
                    <p className="text-sm font-medium font-sans">{selectedPropertyName || "Property"}</p>
                    {selectedPropertyAddress && <p className="text-[11px] text-muted-foreground font-sans truncate">{selectedPropertyAddress}</p>}
                  </div>
                  <CheckCircle2 className="h-4 w-4 text-primary shrink-0 ml-auto" />
                </div>
              )}
            </div>
            {/* Notes */}
            <div className="space-y-1.5">
              <p className="text-xs font-medium font-sans text-muted-foreground px-1">Notes (optional)</p>
              <div className="relative">
                <Textarea placeholder={isNotesListening ? "Listening — speak now…" : "Anything we should know…"} value={notes} onChange={(e) => setNotes(e.target.value)} rows={3} className="rounded-xl text-sm pr-12" />
                <Button type="button" size="icon" variant={isNotesListening ? "default" : "ghost"} className={cn("absolute right-2 top-2 h-8 w-8 rounded-full", isNotesListening && "bg-destructive hover:bg-destructive/90 animate-pulse")} onClick={toggleNotesVoice}>
                  {isNotesListening ? <MicOff className="h-3.5 w-3.5" /> : <Mic className="h-3.5 w-3.5" />}
                </Button>
              </div>
            </div>
            {/* Estimate */}
            <BookingEstimate daysCount={daysCount} basePrice={basePrice} baseServiceTotalOverride={baseServiceTotal} baseLineLabelOverride={baseLineLabelOverride} addonTotal={addonTotal} discountPct={disc} cashDiscountEnabled={cashDiscountEnabled} cashDiscountPct={cashDiscountPct} submitting={submitting} onSubmit={handleFormSubmit} contactEmail={client?.email} contactPhone={client?.phone} selectedAddons={selectedAddonDetails} serviceType={selectedService} pricingModel={resolvedPricing?.model} pricingDisplay={premiumPricingDisplay} pricingSummary={premiumPricingSummary} pricingUnitLabel={resolvedPricing?.unitLabel} premiumSummary={premiumSummary} quoteNote={resolvedPricing?.quoteNote} />
            <div className="flex items-center justify-center gap-4 pt-1">
              <button onClick={startChat} className="text-xs text-muted-foreground hover:text-primary font-sans transition-colors">
                <MessageCircle className="h-3.5 w-3.5 inline mr-1.5" />Chat instead
              </button>
              <span className="text-border">·</span>
              <button onClick={handleStartOver} className="text-xs text-muted-foreground hover:text-destructive font-sans transition-colors">
                Start over
              </button>
            </div>
          </div>
        </DashboardLayout>
      );
    }

    return (
      <DashboardLayout navItems={clientNavItems} title="Book Service">
        <div className="max-w-lg mx-auto space-y-5">
          {showResumeBanner && crashDraft && (
            <Card className="rounded-2xl border-2 border-primary/20 bg-primary/5">
              <CardContent className="py-4 space-y-3">
                <div className="space-y-1">
                  <p className="text-sm font-medium">You have a saved booking</p>
                  <p className="text-xs text-muted-foreground font-sans">
                    {crashDraftAge?.hours && crashDraftAge.hours >= 24
                      ? `Started ${crashDraftAge.label}. Would you like to continue or start fresh?`
                      : `Started ${crashDraftAge?.label || "earlier"}. Pick up where you left off?`}
                  </p>
                  {(crashDraft.service || crashDraft.start) && (
                    <p className="text-xs text-muted-foreground font-sans mt-1">
                      {crashDraft.service && <span className="capitalize">{crashDraft.service.replace(/_/g, " ")}</span>}
                      {crashDraft.start && crashDraft.end && <span> · {crashDraft.start} → {crashDraft.end}</span>}
                    </p>
                  )}
                </div>
                <div className="flex gap-2">
                  <Button size="sm" className="rounded-full flex-1" onClick={() => setShowResumeBanner(false)}>
                    Continue
                  </Button>
                  <Button size="sm" variant="outline" className="rounded-full flex-1" onClick={() => {
                    clearDraft();
                    setShowResumeBanner(false);
                    setSelectedService("");
                    setStartDate(undefined);
                    setEndDate(undefined);
                    setDropOffTime("");
                    setNotes("");
                    setSelectedAddonIds([]);
                    setRecurrence("none");
                    setMode("choose");
                    if (properties.length) setSelectedPropertyId(properties[0]?.id || null);
                  }}>
                    Start fresh
                  </Button>
                </div>
              </CardContent>
            </Card>
          )}
          {activeBookingsWithPremium.length > 0 && (
            <ActiveBookingsBanner
              bookings={activeBookingsWithPremium}
              onCancel={handleCancelActiveBooking}
            />
          )}
          <BookingModeChooser properties={properties} selectedPropertyId={selectedPropertyId} onSelectProperty={selectProperty} onAddProperty={handleAddProperty} onStartChat={startChat} onStartForm={() => setMode("form")} />
        </div>
      </DashboardLayout>
    );
  }

  // ── Render: Form mode ──
  const formStep = !selectedPropertyId ? 1 : !selectedService ? 2 : datesPreFilled ? 3 : !(startDate && (isSingleDay || endDate)) ? 3 : 4;

  return (
    <DashboardLayout navItems={clientNavItems} title="Book Service">
      <div className="max-w-2xl mx-auto space-y-5 pb-8">
        {activeBookingsWithPremium.length > 0 && (
          <ActiveBookingsBanner
            bookings={activeBookingsWithPremium}
            onCancel={handleCancelActiveBooking}
          />
        )}
        <BookingProgress currentStep={formStep} mode="form" datesPreFilled={datesPreFilled} />
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-3">
            <Button variant="ghost" size="icon" className="rounded-full" onClick={() => setMode("choose")}><ArrowLeft className="h-4 w-4" /></Button>
            <h1 className="text-xl font-medium tracking-tight">Book Service</h1>
          </div>
          <div className="flex items-center gap-2">
            {client?.vip && <Badge className="bg-amber-50 text-amber-700 border border-amber-200 gap-1"><Star className="h-3 w-3 fill-amber-500" /> VIP</Badge>}
            <Button variant="ghost" size="sm" className="text-xs rounded-full gap-1" onClick={startChat}><MessageCircle className="h-3.5 w-3.5" /> Try AI</Button>
          </div>
        </div>

        {/* Property */}
        <div className="space-y-2">
          <p className="text-xs font-medium font-sans text-muted-foreground px-1">Which property?</p>
          <div className="grid gap-2">
            {properties.map((p) => (
              <button key={p.id} onClick={() => selectProperty(p.id)} className={cn("flex items-center gap-3 px-4 py-3 rounded-xl border-2 transition-all text-left w-full", selectedPropertyId === p.id ? "border-primary bg-primary/5 shadow-sm" : "border-border/50 hover:border-primary/30")}>
                <Home className={cn("h-4 w-4 shrink-0", selectedPropertyId === p.id ? "text-primary" : "text-muted-foreground")} />
                <div className="flex-1 min-w-0">
                  <p className="text-sm font-sans font-medium">{formatPropertyDisplayName(p)}</p>
                  {p.address_line1 && <p className="text-[11px] text-muted-foreground font-sans truncate">{formatPropertyAddress(p)}</p>}
                </div>
                {selectedPropertyId === p.id && <CheckCircle2 className="h-4 w-4 text-primary shrink-0" />}
              </button>
            ))}
            <Button variant="outline" size="sm" className="rounded-full gap-1.5 w-full" onClick={handleAddProperty}><Home className="h-3.5 w-3.5" /> Add property</Button>
          </div>
        </div>

        {/* Promo / Invite Code */}
        {!promoApplied ? (
          <div className="space-y-1.5">
            {!showPromo ? (
              <button onClick={() => setShowPromo(true)} className="text-xs text-primary hover:underline font-sans flex items-center gap-1.5 px-1">
                <Gift className="h-3.5 w-3.5" /> Have an invite or promo code?
              </button>
            ) : (
              <div className="flex gap-2 items-start">
                <Input
                  value={promoInput}
                  onChange={(e) => { setPromoInput(e.target.value.toUpperCase()); setPromoError(""); }}
                  placeholder="e.g. ADAM-FRIEND"
                  className="font-mono uppercase text-sm h-9 rounded-xl"
                  onKeyDown={(e) => e.key === "Enter" && applyPromoCode()}
                />
                <Button onClick={applyPromoCode} disabled={promoLoading || !promoInput.trim()} size="sm" className="rounded-full shrink-0 h-9">
                  {promoLoading ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : "Apply"}
                </Button>
              </div>
            )}
            {promoError && <p className="text-xs text-destructive font-sans px-1">{promoError}</p>}
          </div>
        ) : (
          <div className="flex items-center gap-2 px-3 py-2.5 rounded-xl bg-primary/5 border border-primary/20">
            <Check className="h-4 w-4 text-primary shrink-0" />
            <div className="flex-1 min-w-0">
              <span className="font-mono text-xs">{promoApplied.code}</span>
              {promoApplied.description && <p className="text-[10px] text-muted-foreground truncate">{promoApplied.description}</p>}
            </div>
            <Badge variant="secondary" className="text-[10px] shrink-0">{promoApplied.discount}</Badge>
          </div>
        )}

        {/* Service selector */}
        <div className="space-y-2">
          <p className="text-xs font-medium font-sans text-muted-foreground px-1">What do you need?</p>
          <div className="grid gap-2">
            {[...services].sort((a, b) => {
              const aR = (featuredService === a.service_type) || isSeasonallyRecommended(a.service_type) ? 0 : 1;
              const bR = (featuredService === b.service_type) || isSeasonallyRecommended(b.service_type) ? 0 : 1;
              return aR - bR;
            }).map((s) => {
              const Icon = serviceIcons[s.service_type] || Sparkles;
              const pricing = resolveServicePricing(s);
              const isLast = client?.last_service_type === s.service_type;
              const active = selectedService === s.service_type;
              const disc = getDiscount(s.service_type);
              const basePrice = Number(s.base_price);
              const discPrice = pricing.model === "flat" && disc > 0 ? basePrice * (1 - disc / 100) : basePrice;
              return (
                <button key={s.id} onClick={() => setSelectedService(s.service_type)} className={cn("flex items-center gap-3 px-4 py-3 rounded-xl border-2 transition-all text-left w-full", active ? "border-primary bg-primary/5 shadow-sm" : "border-border/50 hover:border-primary/30")}>
                  <div className="w-9 h-9 rounded-lg bg-primary/10 flex items-center justify-center shrink-0"><Icon className="h-4 w-4 text-primary" /></div>
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center gap-2">
                      <span className="text-sm font-medium font-sans capitalize">{s.service_type.replace(/_/g, " ")}</span>
                      {isLast && <Badge variant="secondary" className="text-[9px]">Last booked</Badge>}
                      {featuredService === s.service_type ? <Badge className="bg-emerald-50 text-emerald-700 border border-emerald-200 text-[9px]">Featured</Badge> : isSeasonallyRecommended(s.service_type) && <Badge className="bg-emerald-50 text-emerald-700 border border-emerald-200 text-[9px]">Seasonal</Badge>}
                      {disc > 0 && <Badge className="bg-amber-50 text-amber-700 border border-amber-200 text-[9px]">{disc}% off</Badge>}
                    </div>
                    {(s.modifiers as any)?.description && <p className="text-[11px] text-muted-foreground font-sans mt-0.5 line-clamp-1">{(s.modifiers as any).description}</p>}
                    {pricing.pricingSummary && <p className="text-[11px] text-muted-foreground/80 font-sans mt-0.5 line-clamp-1">{pricing.pricingSummary}</p>}
                  </div>
                  <div className="text-right shrink-0">
                    {disc > 0 && pricing.model === "flat" ? (
                      <div className="flex flex-col items-end">
                        <span className="text-sm font-mono text-primary font-medium">${discPrice.toFixed(0)}/{pricing.unitLabel}</span>
                        <span className="text-[10px] font-mono text-muted-foreground line-through">${basePrice.toFixed(0)}/{pricing.unitLabel}</span>
                      </div>
                    ) : <span className="text-sm font-mono text-muted-foreground">{pricing.displayLabel}</span>}
                  </div>
                </button>
              );
            })}
          </div>
        </div>

        {/* Dates — compact summary when pre-filled */}
        {selectedService && datesPreFilled && !datesUnavailable && (
          <div className="space-y-2">
            <p className="text-xs font-medium font-sans text-muted-foreground px-1">Your dates</p>
            <div className="border border-primary/30 rounded-xl bg-primary/5 p-3">
              <div className="flex items-center gap-2 text-sm font-sans">
                <CalendarIcon className="h-4 w-4 text-primary shrink-0" />
                <span className="font-medium">
                  {startDate && format(startDate, "EEE, d MMM")}{dropOffTime && ` at ${formatTime12(dropOffTime)}`}
                  {!isSingleDay && <>{" → "}{endDate && format(endDate, "EEE, d MMM yyyy")}</>}
                </span>
                <Badge variant="secondary" className="text-[10px] shrink-0 ml-auto">{daysCount} {daysCount === 1 ? "day" : "days"}</Badge>
              </div>
              {selectedPremiumDetails && (
                <div className={cn(
                  "mt-2 rounded-lg border px-3 py-2 text-xs font-sans",
                  selectedPremiumDetails.hasHoliday
                    ? "border-amber-200 bg-amber-50 text-amber-900"
                    : "border-primary/20 bg-primary/5 text-primary",
                )}>
                  <span className="font-medium">
                    {selectedPremiumDetails.hasHoliday ? "Holiday premium: " : "Premium date: "}
                  </span>
                  {selectedPremiumDetails.summaryLabel}
                </div>
              )}
              <button onClick={() => { setStartDate(undefined); setEndDate(undefined); setCalendarOpen(true); }} className="text-[11px] text-muted-foreground hover:text-primary font-sans mt-1.5 underline underline-offset-2">Change dates</button>
            </div>
          </div>
        )}

        {/* Dates — calendar */}
        {selectedService && (!datesPreFilled || datesUnavailable || (!startDate && !endDate)) && (
          <div className="space-y-2">
            <p className="text-xs font-medium font-sans text-muted-foreground px-1">When?</p>

            {/* Collapsed date summary */}
            {!calendarOpen && startDate && (isSingleDay || endDate) && !datesUnavailable && (
              <button
                onClick={() => setCalendarOpen(true)}
                className="w-full rounded-xl border border-primary/30 bg-primary/5 p-4 flex items-center gap-3 text-left hover:bg-primary/10 transition-colors"
              >
                <div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center shrink-0">
                  <CalendarIcon className="h-5 w-5 text-primary" />
                </div>
                <div className="flex-1 min-w-0">
                  <p className="text-sm font-medium font-sans">
                    {format(startDate, "EEE, d MMM")}
                    {!isSingleDay && endDate && <>{" → "}{format(endDate, "EEE, d MMM yyyy")}</>}
                  </p>
                  <p className="text-xs text-muted-foreground font-sans">
                    {selectedPremiumDetails ? selectedPremiumDetails.summaryLabel : `${daysCount} ${daysCount === 1 ? "day" : "days"}`}
                  </p>
                </div>
                <div className="flex items-center gap-2 shrink-0">
                  <CheckCircle2 className="h-4 w-4 text-primary" />
                  <span className="text-[11px] text-primary font-sans underline underline-offset-2">Change</span>
                </div>
              </button>
            )}

            {/* Open calendar */}
            {(calendarOpen || !(startDate && (isSingleDay || endDate)) || datesUnavailable) && (
              <div className="border border-border/50 rounded-xl bg-card p-3 space-y-3">
                {isSingleDay ? (
                  <>
                    <div className="flex items-center gap-2 text-sm font-sans">
                      <div className={cn("flex-1 rounded-lg border px-3 py-2 text-center text-xs", startDate ? "border-primary bg-primary/5 font-medium" : "border-border text-muted-foreground")}>{startDate ? format(startDate, "MMM d") : "Select date"}</div>
                      {startDate && <Badge variant="secondary" className="text-[10px] shrink-0">1 day</Badge>}
                      {selectedPremiumDetails && (
                        <Badge
                          className={cn(
                            "text-[10px] shrink-0",
                            selectedPremiumDetails.hasHoliday
                              ? "bg-amber-100 text-amber-900 border border-amber-200"
                              : "bg-primary/10 text-primary border border-primary/20",
                          )}
                        >
                          {selectedPremiumDetails.shortLabel}
                        </Badge>
                      )}
                    </div>
                    <Calendar mode="single" selected={startDate} onSelect={(date) => {
                      setStartDate(date || undefined); setEndDate(date || undefined); checkAvailability(date || undefined, date || undefined);
                      if (date) { setCalendarOpen(false); setTimeout(() => afterCalendarRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }), 150); }
                    }} disabled={(d) => d < new Date()} modifiers={{ holiday: holidayMatchers, premium: premiumMatchers }} modifiersClassNames={{ holiday: "bg-amber-100 text-amber-900 ring-1 ring-inset ring-amber-300 hover:bg-amber-100", premium: "bg-primary/10 text-primary ring-1 ring-inset ring-primary/20 hover:bg-primary/15" }} numberOfMonths={1} className="p-2 pointer-events-auto mx-auto" />
                  </>
                ) : (
                  <>
                    <div className="flex items-center gap-2 text-sm font-sans">
                      <div className={cn("flex-1 rounded-lg border px-3 py-2 text-center text-xs", startDate ? "border-primary bg-primary/5 font-medium" : "border-border text-muted-foreground")}>{startDate ? format(startDate, "MMM d") : "Start"}</div>
                      <ArrowRight className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
                      <div className={cn("flex-1 rounded-lg border px-3 py-2 text-center text-xs", endDate ? "border-primary bg-primary/5 font-medium" : "border-border text-muted-foreground")}>{endDate ? format(endDate, "MMM d") : "End"}</div>
                      {daysCount > 0 && <Badge variant="secondary" className="text-[10px] shrink-0">{daysCount} {daysCount === 1 ? "day" : "days"}</Badge>}
                      {selectedPremiumDetails && (
                        <Badge
                          className={cn(
                            "text-[10px] shrink-0",
                            selectedPremiumDetails.hasHoliday
                              ? "bg-amber-100 text-amber-900 border border-amber-200"
                              : "bg-primary/10 text-primary border border-primary/20",
                          )}
                        >
                          {selectedPremiumDetails.shortLabel}
                        </Badge>
                      )}
                    </div>
                    <Calendar mode="range" selected={startDate && endDate ? { from: startDate, to: endDate } : startDate ? { from: startDate, to: undefined } : undefined} onSelect={(range) => {
                      setStartDate(range?.from); setEndDate(range?.to); checkAvailability(range?.from, range?.to);
                      if (range?.from && range?.to) { setCalendarOpen(false); setTimeout(() => afterCalendarRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }), 150); }
                    }} disabled={(d) => d < new Date()} modifiers={{ holiday: holidayMatchers, premium: premiumMatchers }} modifiersClassNames={{ holiday: "bg-amber-100 text-amber-900 ring-1 ring-inset ring-amber-300 hover:bg-amber-100", premium: "bg-primary/10 text-primary ring-1 ring-inset ring-primary/20 hover:bg-primary/15" }} numberOfMonths={1} className="p-2 pointer-events-auto mx-auto" />
                  </>
                )}
                <div className="flex flex-wrap items-center gap-3 px-1 text-[11px] font-sans text-muted-foreground">
                  <span className="inline-flex items-center gap-1.5">
                    <span className="h-2.5 w-2.5 rounded-full border border-amber-300 bg-amber-200" />
                    Canadian holiday premium
                  </span>
                  <span className="inline-flex items-center gap-1.5">
                    <span className="h-2.5 w-2.5 rounded-full border border-primary/40 bg-primary/20" />
                    Other premium date
                  </span>
                </div>
                {premiumSummary && (
                  <div className="rounded-xl border border-amber-200/70 bg-amber-50/80 p-3 text-xs font-sans text-amber-900">
                    {premiumSummary}
                  </div>
                )}
                {datesUnavailable && !exceptionRequested && (
                  <div className="rounded-xl border border-amber-500/30 bg-amber-50/50 dark:bg-amber-950/20 p-3 space-y-2">
                    <div className="flex items-start gap-2">
                      <AlertCircle className="h-4 w-4 text-amber-600 dark:text-amber-400 shrink-0 mt-0.5" />
                      <div><p className="text-xs font-medium font-sans text-amber-900 dark:text-amber-200">Not currently available</p><p className="text-[11px] text-amber-700/80 dark:text-amber-300/70 font-sans">Try different dates above.</p></div>
                    </div>
                    {client?.vip && <Button onClick={handleExceptionRequest} disabled={requestingException} variant="outline" size="sm" className="w-full rounded-lg text-xs gap-1">{requestingException ? <Loader2 className="h-3 w-3 animate-spin" /> : <Star className="h-3 w-3" />}Request as VIP</Button>}
                  </div>
                )}
                {exceptionRequested && (
                  <div className="rounded-xl border border-primary/30 bg-primary/5 p-3 flex items-start gap-2"><CheckCircle2 className="h-4 w-4 text-primary shrink-0 mt-0.5" /><p className="text-xs font-sans">Exception request sent — we'll be in touch.</p></div>
                )}
              </div>
            )}
          </div>
        )}
        <div ref={afterCalendarRef} />

        {/* Preferred Arrival Time */}
        {selectedService && startDate && (isSingleDay || endDate) && (
          <div className="space-y-2">
            <p className="text-xs font-medium font-sans text-muted-foreground px-1 flex items-center gap-1.5">
              <Clock className="h-3.5 w-3.5" /> Preferred time
            </p>
            <div className="grid grid-cols-2 gap-2">
              {TIME_SLOTS.map((slot) => {
                const Icon = slot.icon;
                const active = selectedTimeSlots.includes(slot.value);
                return (
                  <button
                    key={slot.value}
                    onClick={() => { setSelectedTimeSlots([slot.value]); setSpecificTime(""); setShowSpecificTime(false); }}
                    className={cn(
                      "flex items-center gap-2.5 px-3 py-3 rounded-xl border-2 transition-all text-left",
                      active ? "border-primary bg-primary/5 shadow-sm" : "border-border/50 hover:border-primary/30"
                    )}
                  >
                    <Icon className={cn("h-4 w-4 shrink-0", active ? "text-primary" : "text-muted-foreground")} />
                    <div>
                      <p className={cn("text-sm font-sans", active ? "font-medium text-primary" : "")}>{slot.label}</p>
                      <p className="text-[10px] text-muted-foreground font-sans">{slot.range}</p>
                    </div>
                  </button>
                );
              })}
            </div>
          </div>
        )}


        {/* Notes */}
        {selectedService && (
          <div className="space-y-1.5">
            <p className="text-xs font-medium font-sans text-muted-foreground px-1">Notes (optional)</p>
            <div className="relative">
              <Textarea placeholder={isNotesListening ? "Listening — speak now…" : "Anything we should know — what needs fixing, where to find the breaker panel, access details…"} value={notes} onChange={(e) => setNotes(e.target.value)} rows={3} className="rounded-xl text-sm pr-12" />
              <Button type="button" size="icon" variant={isNotesListening ? "default" : "ghost"} className={cn("absolute right-2 top-2 h-8 w-8 rounded-full", isNotesListening && "bg-destructive hover:bg-destructive/90 animate-pulse")} onClick={toggleNotesVoice}>
                {isNotesListening ? <MicOff className="h-3.5 w-3.5" /> : <Mic className="h-3.5 w-3.5" />}
              </Button>
            </div>
            {isNotesListening && <p className="text-[10px] text-primary font-sans px-1 animate-pulse">🎙️ Listening — your words will appear as you speak</p>}
          </div>
        )}

        {/* Recurrence */}
        {selectedService && startDate && endDate && (
          <div className="space-y-2">
            <p className="text-xs font-medium font-sans text-muted-foreground px-1 flex items-center gap-1.5"><Repeat className="h-3.5 w-3.5" /> Repeat this booking?</p>
            <div className="grid grid-cols-2 gap-2">
              {([{ value: "none", label: "Just once" }, { value: "weekly", label: "Every week" }, { value: "biweekly", label: "Every 2 weeks" }, { value: "monthly", label: "Every month" }] as const).map((opt) => (
                <button key={opt.value} onClick={() => setRecurrence(opt.value)} className={cn("px-3 py-2.5 rounded-xl border text-sm font-sans transition-all text-center", recurrence === opt.value ? "border-primary bg-primary/10 text-primary font-medium" : "border-border bg-card text-muted-foreground hover:border-primary/30")}>{opt.label}</button>
              ))}
            </div>
            {recurrence !== "none" && <p className="text-[10px] text-muted-foreground font-sans px-1">This will create 12 future bookings ({recurrence === "weekly" ? "weekly" : recurrence === "biweekly" ? "fortnightly" : "monthly"}). Each booking goes through the normal approval process.</p>}
          </div>
        )}


        {/* Other Add-ons */}
        {otherAddons.length > 0 && selectedService && (
          <div>
            <button onClick={() => setShowAddons(!showAddons)} className="w-full flex items-center justify-between px-4 py-2.5 bg-card border border-border/50 rounded-xl hover:bg-accent/30 transition-colors">
              <span className="text-sm font-sans text-muted-foreground">Add-ons {selectedAddonIds.filter((id) => otherAddons.some((a: any) => a.id === id)).length > 0 && `(${selectedAddonIds.filter((id) => otherAddons.some((a: any) => a.id === id)).length})`}</span>
              {showAddons ? <ChevronUp className="h-4 w-4 text-muted-foreground" /> : <ChevronDown className="h-4 w-4 text-muted-foreground" />}
            </button>
            {showAddons && (
              <div className="mt-2 grid gap-2">
                {otherAddons.map((addon: any) => {
                  const available = isAddonAvailable(addon);
                  const selected = selectedAddonIds.includes(addon.id);
                  return (
                    <button key={addon.id} disabled={!available} onClick={() => available && toggleAddon(addon.id)} className={cn("flex items-center gap-3 px-4 py-3 rounded-xl border-2 transition-all text-left w-full", !available ? "opacity-50 border-border/30" : selected ? "border-primary bg-primary/5" : "border-border/50 hover:border-primary/30")}>
                      <Checkbox checked={selected} disabled={!available} className="pointer-events-none" />
                      <div className="flex-1 min-w-0"><span className="text-sm font-sans">{addon.name}</span>{addon.requires_approval && !available && <span className="text-[10px] text-muted-foreground ml-2">Needs approval</span>}</div>
                      <span className="text-xs font-mono text-muted-foreground">+${Number(addon.price).toFixed(0)}</span>
                    </button>
                  );
                })}
              </div>
            )}
          </div>
        )}


        {/* Inline contact details */}
        {canSubmit && (missingRequiredContact || missingPhone || phoneInvalid) && (
          <div className="space-y-3 rounded-xl border-2 border-primary/30 bg-primary/5 p-4" id="booking-contact-fields">
            <div className="flex items-center gap-2">
              <Phone className="h-4 w-4 text-primary" />
              <p className="text-sm font-medium font-sans">
                {missingRequiredContact ? "Email required to send your request" : "Add a mobile for text updates"}
              </p>
            </div>
            <p className="text-xs text-muted-foreground font-sans">
              {missingRequiredContact
                ? "We only need a valid email to submit your booking request. Add a mobile too if you want text updates."
                : "You can submit without a mobile number. Add one now if you want text updates and smoother follow-up."}
            </p>
            <div className="space-y-1.5">
              <label htmlFor="booking-email" className="text-xs font-medium font-sans text-muted-foreground">Email</label>
              <Input
                id="booking-email"
                type="email"
                value={inlineEmail}
                onChange={(e) => setInlineEmail(e.target.value)}
                placeholder="your@email.com"
                className="rounded-xl"
              />
              {inlineEmail && !emailValid && (
                <p className="text-[10px] text-destructive font-sans">Please enter a valid email address.</p>
              )}
            </div>
            <StructuredPhoneInput
              countryCode={inlinePhoneCountry}
              phoneNumber={inlinePhoneDigits}
              onCountryCodeChange={setInlinePhoneCountry}
              onPhoneNumberChange={setInlinePhoneDigits}
            />
            <Button
              onClick={saveInlineContact}
              disabled={contactSaving || !emailValid || phoneInvalid}
              className="w-full rounded-xl gap-2"
              size="sm"
            >
              {contactSaving ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Check className="h-3.5 w-3.5" />}
              {contactSaving ? "Saving…" : (missingRequiredContact ? "Save Email" : "Save Contact Details")}
            </Button>
          </div>
        )}

        {/* Estimate + submit */}
        {canSubmit && (
          <BookingEstimate
            daysCount={daysCount}
            basePrice={currentBasePrice}
            baseServiceTotalOverride={baseServiceTotal}
            baseLineLabelOverride={baseLineLabelOverride}
            addonTotal={addonTotal}
            discountPct={currentDiscount}
            cashDiscountEnabled={cashDiscountEnabled}
            cashDiscountPct={cashDiscountPct}
            submitting={submitting}
            onSubmit={handleFormSubmit}
            contactEmail={contactEmail}
            contactPhone={phoneValid ? toE164(inlinePhoneCountry, inlinePhoneDigits) : contactPhone}
            selectedAddons={selectedAddonDetails}
            serviceType={selectedService}
            pricingModel={currentPricingModel}
            pricingDisplay={premiumPricingDisplay}
            pricingSummary={premiumPricingSummary}
            pricingUnitLabel={currentUnitLabel}
            premiumSummary={premiumSummary}
            quoteNote={currentQuoteNote}
            disabled={missingRequiredContact}
            disabledReason={missingRequiredContact ? "Add a valid email address to unlock your booking request." : undefined}
            submitLabel={missingRequiredContact ? "Add Email First" : getInitialBookingStatus(currentPricingModel, selectedService) === "requested" ? getRequestedSubmitLabel(selectedService) : "Book Service"}
          />
        )}

        {/* Start over */}
        <div className="text-center pt-2 pb-4">
          <button onClick={handleStartOver} className="text-xs text-muted-foreground hover:text-destructive font-sans transition-colors">
            Clear & start over
          </button>
        </div>
      </div>
    </DashboardLayout>
  );
};

export default ClientBooking;
