/* ============================================================================
   booking.jsx — Functional booking widget for Lighthouse
   • Mapbox-powered address autocomplete (FR/EN/RU)
   • Custom time picker (presets + grid)
   • Mini calendar (re-exported from old impl)
   • Recap modal: vehicle, passenger, price estimate
   • localStorage persistence
   ============================================================================ */
const { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } = React;

const MAPBOX_TOKEN = window.MAPBOX_TOKEN;
const STORAGE_KEY = "lh_booking_v1";

/* ---------- useAnchoredPosition: computes fixed-pos coords for a popover ---------- */
function useAnchoredPosition(anchorRef, popRef, open, opts = {}) {
  const { gap = 12, align = "left", minWidth = 320, preferredWidth, popMaxHeight = 360, preferUp = false } = opts;
  const [pos, setPos] = useState(null);

  useLayoutEffect(() => {
    if (!open) return;
    function compute() {
      const a = anchorRef.current;
      if (!a) return;
      const r = a.getBoundingClientRect();
      const vw = window.innerWidth, vh = window.innerHeight;
      // Width
      let width = preferredWidth || Math.max(r.width, minWidth);
      width = Math.min(width, vw - 24);
      // Horizontal placement
      let left;
      if (align === "right") left = r.right - width;
      else if (align === "center") left = r.left + r.width / 2 - width / 2;
      else left = r.left;
      // Clamp horizontally
      left = Math.max(12, Math.min(left, vw - width - 12));
      // Vertical: use popMaxHeight as the threshold so behavior is stable across renders.
      const popH = (popRef.current && popRef.current.offsetHeight) || popMaxHeight;
      const spaceBelow = vh - r.bottom - gap;
      const spaceAbove = r.top - gap;
      // Flip up when below can't fit the typical max height AND above has more room
      const needs = Math.min(popMaxHeight, popH);
      let top, flipUp = false, maxH = popMaxHeight;
      if (preferUp) {
        // Always above; clamp height to the room available above the anchor.
        maxH = Math.max(120, Math.min(popMaxHeight, r.top - gap - 12));
        top = Math.max(12, r.top - gap - Math.min(popH, maxH));
        flipUp = true;
      } else if (spaceBelow >= needs) {
        top = r.bottom + gap; flipUp = false;
        maxH = Math.min(popMaxHeight, vh - top - 12);
      } else if (spaceAbove >= needs) {
        top = r.top - popH - gap; flipUp = true;
        maxH = Math.min(popMaxHeight, r.top - gap - 12);
      } else if (spaceAbove > spaceBelow) {
        maxH = Math.max(120, r.top - gap - 12);
        top = Math.max(12, r.top - gap - Math.min(popH, maxH));
        flipUp = true;
      } else {
        top = r.bottom + gap; flipUp = false;
        maxH = Math.max(120, vh - top - 12);
      }
      top = Math.max(12, Math.min(top, vh - 12));
      setPos({ left, top, width, flipUp, maxH: Math.floor(maxH) });
    }
    compute();
    const onScroll = () => compute();
    const onResize = () => compute();
    window.addEventListener("scroll", onScroll, true);
    window.addEventListener("resize", onResize);
    // Recompute after popover renders (height known)
    const raf = requestAnimationFrame(compute);
    // Track for ~500ms to follow the widget's expand animation (transform:scale)
    let animFrames = [];
    const startAnim = performance.now();
    function tick() {
      compute();
      if (performance.now() - startAnim < 500) {
        animFrames.push(requestAnimationFrame(tick));
      }
    }
    animFrames.push(requestAnimationFrame(tick));
    // Recompute when content size changes (e.g. new search results)
    let ro;
    if (popRef.current && window.ResizeObserver) {
      ro = new ResizeObserver(() => compute());
      ro.observe(popRef.current);
      if (anchorRef.current) ro.observe(anchorRef.current);
    }
    return () => {
      window.removeEventListener("scroll", onScroll, true);
      window.removeEventListener("resize", onResize);
      cancelAnimationFrame(raf);
      animFrames.forEach(cancelAnimationFrame);
      if (ro) ro.disconnect();
    };
  }, [open, align, minWidth, preferredWidth, gap]);

  return pos;
}

function Portal({ children }) {
  const [el] = useState(() => document.createElement("div"));
  useEffect(() => {
    document.body.appendChild(el);
    return () => { document.body.removeChild(el); };
  }, [el]);
  return ReactDOM.createPortal(children, el);
}

/* Anchored: portal-rendered popover positioned next to anchorRef, auto-flips up. */
function Anchored({ anchorRef, open, onClose, opts, className, onFlip, children }) {
  const popRef = useRef(null);
  const pos = useAnchoredPosition(anchorRef, popRef, open, opts || {});
  useEffect(() => {
    if (!open) return;
    function onDoc(e) {
      const a = anchorRef.current, p = popRef.current;
      if (a && a.contains(e.target)) return;
      if (p && p.contains(e.target)) return;
      onClose && onClose();
    }
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, [open, onClose]);
  useEffect(() => { onFlip && onFlip(open ? !!(pos && pos.flipUp) : false); }, [open, pos && pos.flipUp]);
  if (!open) return null;
  const style = pos
    ? { position: "fixed", left: pos.left, top: pos.top, width: pos.width, maxHeight: pos.maxH, overflowY: "auto", visibility: "visible" }
    : { position: "fixed", left: -9999, top: -9999, visibility: "hidden" };
  return (
    <Portal>
      <div ref={popRef} className={`${className || ""} ${pos && pos.flipUp ? "flip-up" : ""}`} style={style} onClick={(e) => e.stopPropagation()}>
        {children}
      </div>
    </Portal>
  );
}

/* ---------- i18n ---------- */
const BK_I18N = {
  fr: {
    durLabel: "Durée",
    durations: [
      { v: 2,  l: "2 heures" },
      { v: 4,  l: "4 heures" },
      { v: 8,  l: "Demi-journée (8h)" },
      { v: 12, l: "Journée (12h)" },
    ],
    presets: { now: "Maintenant", in1h: "Dans 1 h", tomMorn: "Demain matin", tomEve: "Demain soir" },
    timeHours: "Heures", timeMinutes: "Minutes",
    addrEmpty: "Tapez au moins 2 caractères",
    addrNoRes: "Aucun résultat",
    poweredBy: "via Mapbox",
    err: { pickup: "Sélectionnez une adresse de départ", drop: "Sélectionnez une destination", date: "Choisissez une date", time: "Choisissez une heure" },
    modal: {
      eyebrow: "Configurez votre trajet",
      title: ["Une dernière", "étape."],
      titleHourly: ["À votre", "rythme."],
      tripPick: "Prise en charge", tripDrop: "Destination", tripWhen: "Date & heure", tripDur: "Durée",
      distance: "Distance", duration: "Durée estimée", route: "via",
      vehicle: "Véhicule",
      passenger: "Voyageur",
      name: "Nom complet", phone: "Téléphone", bags: "Bagages", paxN: "Passagers", notes: "Note pour le chauffeur",
      notesPh: "Vol AF1234, panneau d'accueil, sièges enfant, etc.",
      confirm: "Confirmer la réservation",
      confirmHourly: "Confirmer la mise à disposition",
      estTotal: "Estimation",
      from: "À partir de",
      disclaimer: "Tarif définitif confirmé par votre concierge sous 8 minutes.",
      confirmed: "Demande envoyée.",
      confirmedSub: "Notre conciergerie vous rappelle dans les 8 minutes pour confirmer le chauffeur, le véhicule et le tarif.",
      ref: "Référence",
      sumPick: "Départ", sumDrop: "Arrivée", sumWhen: "Quand", sumVeh: "Véhicule", sumPax: "Passagers", sumPrice: "Estimation",
      close: "Fermer",
    },
    vehicles: [
      { id: "berline",  name: "Berline",  model: "Mercedes Classe E / BMW Série 5", cap: 3, bags: 3, basePerKm: 2.4, baseFlat: 65,  hourly: 95  },
      { id: "premium",  name: "Premium",  model: "Mercedes Classe S / BMW Série 7", cap: 3, bags: 3, basePerKm: 3.6, baseFlat: 95,  hourly: 145 },
      { id: "van",      name: "Van",      model: "Mercedes Classe V (jusqu'à 7)",   cap: 7, bags: 7, basePerKm: 3.0, baseFlat: 90,  hourly: 130 },
    ],
  },
  en: {
    durLabel: "Duration",
    durations: [
      { v: 2,  l: "2 hours" },
      { v: 4,  l: "4 hours" },
      { v: 8,  l: "Half-day (8h)" },
      { v: 12, l: "Full day (12h)" },
    ],
    presets: { now: "Now", in1h: "In 1 hour", tomMorn: "Tomorrow morning", tomEve: "Tomorrow evening" },
    timeHours: "Hours", timeMinutes: "Minutes",
    addrEmpty: "Type at least 2 characters",
    addrNoRes: "No results",
    poweredBy: "via Mapbox",
    err: { pickup: "Pick a pickup address", drop: "Pick a destination", date: "Choose a date", time: "Choose a time" },
    modal: {
      eyebrow: "Configure your ride",
      title: ["One last", "step."],
      titleHourly: ["At your own", "pace."],
      tripPick: "Pickup", tripDrop: "Destination", tripWhen: "Date & time", tripDur: "Duration",
      distance: "Distance", duration: "Est. duration", route: "via",
      vehicle: "Vehicle",
      passenger: "Passenger",
      name: "Full name", phone: "Phone", bags: "Bags", paxN: "Passengers", notes: "Note for the chauffeur",
      notesPh: "Flight AF1234, meet & greet sign, child seats, etc.",
      confirm: "Confirm booking",
      confirmHourly: "Confirm hourly service",
      estTotal: "Estimate",
      from: "From",
      disclaimer: "Final fare confirmed by your concierge within 8 minutes.",
      confirmed: "Request sent.",
      confirmedSub: "Our concierge will call you back within 8 minutes to confirm the chauffeur, vehicle and final price.",
      ref: "Reference",
      sumPick: "Pickup", sumDrop: "Drop-off", sumWhen: "When", sumVeh: "Vehicle", sumPax: "Passengers", sumPrice: "Estimate",
      close: "Close",
    },
    vehicles: [
      { id: "berline",  name: "Sedan",    model: "Mercedes E-Class / BMW 5 Series", cap: 3, bags: 3, basePerKm: 2.4, baseFlat: 65,  hourly: 95  },
      { id: "premium",  name: "Premium",  model: "Mercedes S-Class / BMW 7 Series", cap: 3, bags: 3, basePerKm: 3.6, baseFlat: 95,  hourly: 145 },
      { id: "van",      name: "Van",      model: "Mercedes V-Class (up to 7)",      cap: 7, bags: 7, basePerKm: 3.0, baseFlat: 90,  hourly: 130 },
    ],
  },
  ru: {
    durLabel: "Длительность",
    durations: [
      { v: 2,  l: "2 часа" },
      { v: 4,  l: "4 часа" },
      { v: 8,  l: "Полдня (8ч)" },
      { v: 12, l: "Полный день (12ч)" },
    ],
    presets: { now: "Сейчас", in1h: "Через 1 час", tomMorn: "Завтра утром", tomEve: "Завтра вечером" },
    timeHours: "Часы", timeMinutes: "Минуты",
    addrEmpty: "Введите минимум 2 символа",
    addrNoRes: "Ничего не найдено",
    poweredBy: "через Mapbox",
    err: { pickup: "Выберите адрес подачи", drop: "Выберите назначение", date: "Выберите дату", time: "Выберите время" },
    modal: {
      eyebrow: "Настройте поездку",
      title: ["Последний", "шаг."],
      titleHourly: ["В вашем", "темпе."],
      tripPick: "Подача", tripDrop: "Назначение", tripWhen: "Дата и время", tripDur: "Длительность",
      distance: "Расстояние", duration: "Время в пути", route: "через",
      vehicle: "Автомобиль",
      passenger: "Пассажир",
      name: "Полное имя", phone: "Телефон", bags: "Багаж", paxN: "Пассажиры", notes: "Комментарий водителю",
      notesPh: "Рейс AF1234, встреча с табличкой, детское кресло…",
      confirm: "Подтвердить бронь",
      confirmHourly: "Подтвердить почасовую",
      estTotal: "Оценка",
      from: "От",
      disclaimer: "Финальная цена подтверждается консьержем в течение 8 минут.",
      confirmed: "Запрос отправлен.",
      confirmedSub: "Наш консьерж перезвонит в течение 8 минут, чтобы подтвердить водителя, автомобиль и стоимость.",
      ref: "Ссылка",
      sumPick: "Подача", sumDrop: "Назначение", sumWhen: "Когда", sumVeh: "Автомобиль", sumPax: "Пассажиры", sumPrice: "Оценка",
      close: "Закрыть",
    },
    vehicles: [
      { id: "berline",  name: "Седан",    model: "Mercedes E-Class / BMW 5 серии",  cap: 3, bags: 3, basePerKm: 2.4, baseFlat: 65,  hourly: 95  },
      { id: "premium",  name: "Премиум",  model: "Mercedes S-Class / BMW 7 серии",  cap: 3, bags: 3, basePerKm: 3.6, baseFlat: 95,  hourly: 145 },
      { id: "van",      name: "Вэн",      model: "Mercedes V-Class (до 7 человек)", cap: 7, bags: 7, basePerKm: 3.0, baseFlat: 90,  hourly: 130 },
    ],
  },
};

/* ---------- Mapbox helpers ---------- */
async function mbxSearch(q, lang) {
  if (!q || q.trim().length < 2 || !MAPBOX_TOKEN) return [];
  const types = "address,place,locality,neighborhood,poi";
  const url = `https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(q)}.json` +
    `?access_token=${MAPBOX_TOKEN}` +
    `&autocomplete=true&limit=6&language=${lang}&types=${types}`;
  try {
    const r = await fetch(url);
    if (!r.ok) return [];
    const j = await r.json();
    return j.features || [];
  } catch (e) { return []; }
}

async function mbxRoute(a, b) {
  if (!a || !b || !MAPBOX_TOKEN) return null;
  const url = `https://api.mapbox.com/directions/v5/mapbox/driving/${a.lng},${a.lat};${b.lng},${b.lat}` +
    `?access_token=${MAPBOX_TOKEN}&overview=false`;
  try {
    const r = await fetch(url);
    if (!r.ok) return null;
    const j = await r.json();
    const route = j.routes && j.routes[0];
    if (!route) return null;
    return { distanceKm: route.distance / 1000, durationMin: route.duration / 60 };
  } catch (e) { return null; }
}

function featureCategory(f) {
  const cats = (f.properties && f.properties.category) || "";
  const place = f.place_type && f.place_type[0];
  if (/airport/.test(cats) || /aéroport|airport|аэропорт/i.test(f.text)) return "airport";
  if (/train|gare|railway|вокзал/i.test(f.text) || /train|station/.test(cats)) return "train";
  if (/hotel|hôtel|отель/i.test(f.text) || /hotel|lodging/.test(cats)) return "hotel";
  if (place === "address") return "address";
  if (place === "poi") return "poi";
  return "place";
}

function AddrIcon({ kind }) {
  const paths = {
    airport: <path d="M2.5 9l11-3.5L13 4 6 6 4 4l-1 .5 1.2 3-1.7.5L1 6.5 0 7l1 2-1 .5L1.5 11l11-3.5L2.5 9z" transform="translate(1.5 2)"/>,
    train:   <path d="M5 1h6a3 3 0 0 1 3 3v6.5a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V4a3 3 0 0 1 3-3zm0 1.5a1.5 1.5 0 0 0-1.5 1.5v3.5h9V4A1.5 1.5 0 0 0 11 2.5H5zM3.5 10a1.5 1.5 0 0 0 1.5 1.5h6A1.5 1.5 0 0 0 12.5 10v-1h-9v1zM5 13.5l-1.5 2h2L7 14h2l1.5 1.5h2L11 13.5H5z" />,
    hotel:   <path d="M2 3h2v6h6V5h4v6h1v1H1v-1h1V3zm4 4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z" />,
    address: <path d="M8 1a5 5 0 0 0-5 5c0 3.5 5 8 5 8s5-4.5 5-8a5 5 0 0 0-5-5zm0 7a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" />,
    poi:     <circle cx="8" cy="8" r="3" />,
    place:   <path d="M8 1a5 5 0 0 0-5 5c0 3.5 5 8 5 8s5-4.5 5-8a5 5 0 0 0-5-5zm0 7a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" />,
  };
  return (
    <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
      {paths[kind] || paths.place}
    </svg>
  );
}

/* ---------- AddressInput ---------- */
function AddressInput({ value, onChange, placeholder, lang, onFocus, onFlip, hasError }) {
  const [q, setQ] = useState(value?.text || "");
  const [open, setOpen] = useState(false);
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(false);
  const [hl, setHl] = useState(0);
  const wrapRef = useRef(null);
  const anchorRef = useRef(null);
  const debounceRef = useRef(null);
  const i18n = BK_I18N[lang] || BK_I18N.fr;

  // Anchor popover to the .booking-field parent so it aligns with date/time popovers.
  useLayoutEffect(() => {
    if (wrapRef.current) {
      anchorRef.current = wrapRef.current.closest(".booking-field") || wrapRef.current;
    }
  }, []);

  useEffect(() => { if (value?.text !== undefined && value.text !== q) setQ(value.text); /* eslint-disable-next-line */ }, [value]);

  function handleChange(e) {
    const text = e.target.value;
    setQ(text);
    setOpen(true);
    setHl(0);
    clearTimeout(debounceRef.current);
    if (text.trim().length < 2) {
      setResults([]); setLoading(false);
      onChange({ text, lng: null, lat: null }); // clear coords
      return;
    }
    setLoading(true);
    debounceRef.current = setTimeout(async () => {
      const f = await mbxSearch(text, lang);
      setResults(f);
      setLoading(false);
    }, 220);
    onChange({ text, lng: null, lat: null });
  }

  function pick(f) {
    const sel = {
      text: f.text || f.place_name,
      sub: f.place_name,
      lng: f.center[0],
      lat: f.center[1],
      cat: featureCategory(f),
    };
    setQ(sel.text);
    onChange(sel);
    setOpen(false);
  }

  function onKey(e) {
    if (!open || results.length === 0) return;
    if (e.key === "ArrowDown") { e.preventDefault(); setHl(h => (h + 1) % results.length); }
    else if (e.key === "ArrowUp") { e.preventDefault(); setHl(h => (h - 1 + results.length) % results.length); }
    else if (e.key === "Enter") { e.preventDefault(); pick(results[hl]); }
    else if (e.key === "Escape") setOpen(false);
  }

  return (
    <div className="addr-wrap" ref={wrapRef}>
      <input
        type="text"
        value={q}
        placeholder={placeholder}
        onChange={handleChange}
        onFocus={() => { setOpen(true); onFocus && onFocus(); }}
        onKeyDown={onKey}
        autoComplete="off"
        spellCheck="false"
      />
      <Anchored anchorRef={anchorRef} open={open} onClose={() => setOpen(false)} onFlip={onFlip} className="addr-pop" opts={{ minWidth: 360, popMaxHeight: 360, preferUp: true, gap: 12 }}>
        <div role="listbox">
          {loading && (
            <div className="addr-empty addr-loading">
              <span className="addr-spinner"></span>
              <span>{q}…</span>
            </div>
          )}
          {!loading && q.trim().length < 2 && (
            <div className="addr-empty">{i18n.addrEmpty}</div>
          )}
          {!loading && q.trim().length >= 2 && results.length === 0 && (
            <div className="addr-empty">{i18n.addrNoRes}</div>
          )}
          {!loading && results.map((f, i) => {
            const cat = featureCategory(f);
            const sub = (f.place_name || "").replace(`${f.text}, `, "").replace(f.text, "");
            return (
              <button
                key={f.id || i}
                className={`addr-row ${i === hl ? "hl" : ""}`}
                onMouseEnter={() => setHl(i)}
                onClick={() => pick(f)}
                type="button"
              >
                <span className="addr-icon"><AddrIcon kind={cat} /></span>
                <span className="addr-text">
                  <span className="addr-name">{f.text}</span>
                  {sub && <span className="addr-sub">{sub}</span>}
                </span>
              </button>
            );
          })}
          {!loading && results.length > 0 && (
            <div className="addr-foot">{i18n.poweredBy}</div>
          )}
        </div>
      </Anchored>
    </div>
  );
}

/* ---------- Mini Calendar (kept identical to original) ---------- */
function MiniCalendar({ value, onChange, lang, anchorRef, open, onClose, onFlip }) {
  const today = new Date(); today.setHours(0, 0, 0, 0);
  const [viewDate, setViewDate] = useState(() => value || new Date());
  const year = viewDate.getFullYear();
  const month = viewDate.getMonth();
  const MONTHS_BY_LANG = {
    fr: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
    en: ['January','February','March','April','May','June','July','August','September','October','November','December'],
    ru: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
  };
  const DAYS_BY_LANG = {
    fr: ['Lu','Ma','Me','Je','Ve','Sa','Di'],
    en: ['Mo','Tu','We','Th','Fr','Sa','Su'],
    ru: ['Пн','Вт','Ср','Чт','Пт','Сб','Вс'],
  };
  const MONTHS = MONTHS_BY_LANG[lang] || MONTHS_BY_LANG.fr;
  const DAYS = DAYS_BY_LANG[lang] || DAYS_BY_LANG.fr;
  let startOffset = new Date(year, month, 1).getDay() - 1;
  if (startOffset < 0) startOffset = 6;
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const cells = [...Array(startOffset).fill(null), ...Array.from({ length: daysInMonth }, (_, i) => i + 1)];
  const isSame = (d, ref) => ref && new Date(year, month, d).getTime() === new Date(ref.getFullYear(),ref.getMonth(),ref.getDate()).getTime();
  const isPast = (d) => new Date(year, month, d) < today;
  return (
    <Anchored anchorRef={anchorRef} open={open} onClose={onClose} onFlip={onFlip} className="cal-popover" opts={{ align: "center", minWidth: 320, popMaxHeight: 360 }}>
      <div className="cal-head">
        <button className="cal-nav" onClick={() => setViewDate(new Date(year, month - 1, 1))} type="button">
          <svg viewBox="0 0 8 14" width="7" fill="none"><path d="M7 1L1 7l6 6" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" /></svg>
        </button>
        <div className="cal-month">{MONTHS[month]} {year}</div>
        <button className="cal-nav" onClick={() => setViewDate(new Date(year, month + 1, 1))} type="button">
          <svg viewBox="0 0 8 14" width="7" fill="none"><path d="M1 1l6 6-6 6" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" /></svg>
        </button>
      </div>
      <div className="cal-grid">
        {DAYS.map((d) => <div key={d} className="cal-dow">{d}</div>)}
        {cells.map((d, i) => {
          const isToday = d && isSame(d, today);
          const isSelected = d && value && isSame(d, value);
          const past = d && isPast(d);
          return (
            <div
              key={i}
              className={`cal-day${!d ? ' cal-empty' : ''}${isToday ? ' cal-today' : ''}${isSelected ? ' cal-selected' : ''}${past ? ' cal-past' : ''}`}
              onClick={() => { if (d && !past) onChange(new Date(year, month, d)); }}>
              {d || ''}
            </div>
          );
        })}
      </div>
    </Anchored>
  );
}

/* ---------- Time Picker ---------- */
function TimePicker({ value, onChange, onClose, lang, dateContext, onSetDate, anchorRef, open, onFlip }) {
  const i18n = BK_I18N[lang] || BK_I18N.fr;
  const [h, m] = (value || "").split(":").map(x => parseInt(x, 10));
  const selH = isNaN(h) ? null : h;
  const selM = isNaN(m) ? null : m;

  function pad(n) { return String(n).padStart(2, "0"); }

  function setTime(hh, mm) { onChange(`${pad(hh)}:${pad(mm)}`); }
  function pickH(hh) { setTime(hh, selM == null ? 0 : selM); }
  function pickM(mm) { setTime(selH == null ? new Date().getHours() : selH, mm); }

  function applyPreset(kind) {
    const now = new Date();
    if (kind === "now") {
      onSetDate && onSetDate(now);
      const next = new Date(now.getTime() + 15 * 60000);
      setTime(next.getHours(), Math.ceil(next.getMinutes() / 5) * 5 % 60);
    } else if (kind === "in1h") {
      const t = new Date(now.getTime() + 60 * 60000);
      onSetDate && onSetDate(t);
      setTime(t.getHours(), Math.ceil(t.getMinutes() / 5) * 5 % 60);
    } else if (kind === "tomMorn") {
      const t = new Date(now); t.setDate(t.getDate() + 1);
      onSetDate && onSetDate(t);
      setTime(8, 0);
    } else if (kind === "tomEve") {
      const t = new Date(now); t.setDate(t.getDate() + 1);
      onSetDate && onSetDate(t);
      setTime(19, 0);
    }
    onClose && onClose();
  }

  const hours = Array.from({ length: 24 }, (_, i) => i);
  const minutes = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55];

  return (
    <Anchored anchorRef={anchorRef} open={open} onClose={onClose} onFlip={onFlip} className="time-popover" opts={{ align: "right", preferredWidth: 320, popMaxHeight: 380 }}>
      <div className="time-presets">
        <button type="button" className="time-preset" onClick={() => applyPreset("now")}>
          <span className="time-preset-dot"></span>{i18n.presets.now}
        </button>
        <button type="button" className="time-preset" onClick={() => applyPreset("in1h")}>{i18n.presets.in1h}</button>
        <button type="button" className="time-preset" onClick={() => applyPreset("tomMorn")}>{i18n.presets.tomMorn}</button>
        <button type="button" className="time-preset" onClick={() => applyPreset("tomEve")}>{i18n.presets.tomEve}</button>
      </div>
      <div className="time-cols">
        <div>
          <div className="time-col-h">{i18n.timeHours}</div>
          <div className="time-col-list">
            {hours.map(hh => (
              <button key={hh} type="button"
                className={`time-cell ${selH === hh ? "sel" : ""}`}
                onClick={() => pickH(hh)}>
                {String(hh).padStart(2, "0")}
              </button>
            ))}
          </div>
        </div>
        <div>
          <div className="time-col-h">{i18n.timeMinutes}</div>
          <div className="time-col-list">
            {minutes.map(mm => (
              <button key={mm} type="button"
                className={`time-cell ${selM === mm ? "sel" : ""}`}
                onClick={() => pickM(mm)}>
                {String(mm).padStart(2, "0")}
              </button>
            ))}
          </div>
        </div>
      </div>
    </Anchored>
  );
}

/* ---------- Recap Modal ---------- */
function RecapModal({ data, t, lang, onClose }) {
  const i18n = BK_I18N[lang] || BK_I18N.fr;
  const isHourly = data.tab === 1;
  const [vehicle, setVehicle] = useState("berline");
  const [paxN, setPaxN] = useState(1);
  const [bags, setBags] = useState(1);
  const [name, setName] = useState("");
  const [phone, setPhone] = useState("");
  const [notes, setNotes] = useState("");
  const [hours, setHours] = useState(2);
  const [route, setRoute] = useState(null); // {distanceKm, durationMin}
  const [routeLoading, setRouteLoading] = useState(false);
  const [confirmed, setConfirmed] = useState(false);
  const [refCode] = useState(() => "LH-" + Math.random().toString(36).slice(2, 8).toUpperCase());

  // Lock body scroll
  useEffect(() => {
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => { document.body.style.overflow = prev; };
  }, []);

  // Esc to close
  useEffect(() => {
    function onKey(e) { if (e.key === "Escape") onClose(); }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  // Fetch route distance for one-way
  useEffect(() => {
    if (isHourly) { setRoute(null); return; }
    if (!data.pickup?.lng || !data.drop?.lng) return;
    setRouteLoading(true);
    mbxRoute(data.pickup, data.drop).then(r => {
      setRoute(r); setRouteLoading(false);
    });
  }, [isHourly, data.pickup?.lng, data.pickup?.lat, data.drop?.lng, data.drop?.lat]);

  const veh = i18n.vehicles.find(v => v.id === vehicle) || i18n.vehicles[0];

  const price = useMemo(() => {
    if (isHourly) return Math.round(veh.hourly * hours);
    if (route) return Math.round(veh.baseFlat + veh.basePerKm * route.distanceKm);
    return null;
  }, [isHourly, hours, route, vehicle]);

  function fmtDateLong(d) {
    if (!d) return "";
    const opts = { day: "numeric", month: "long", year: "numeric" };
    const loc = lang === "fr" ? "fr-FR" : lang === "ru" ? "ru-RU" : "en-US";
    return new Date(d).toLocaleDateString(loc, opts);
  }

  function fmtMin(min) {
    if (!min) return "—";
    const h = Math.floor(min / 60), m = Math.round(min % 60);
    if (h > 0) return `${h}h${String(m).padStart(2,"0")}`;
    return `${m} min`;
  }

  function submit(e) {
    e.preventDefault();
    if (!name.trim() || !phone.trim()) return;
    setConfirmed(true);
  }

  const titleArr = isHourly ? i18n.modal.titleHourly : i18n.modal.title;

  if (confirmed) {
    return (
      <div className="lh-modal-back" onClick={onClose}>
        <div className="lh-modal is-confirmed" onClick={(e) => e.stopPropagation()}>
          <button className="lh-modal-close" onClick={onClose} aria-label={i18n.modal.close} type="button">
            <svg viewBox="0 0 16 16" width="14" height="14"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" /></svg>
          </button>
          <div className="lh-confirmed">
            <div className="lh-confirmed-tick">
              <svg width="68" height="68" viewBox="0 0 68 68" fill="none">
                <circle className="lh-tick-ring" cx="34" cy="34" r="28" stroke="currentColor" strokeWidth="2" fill="none" />
                <path className="lh-tick-mark" d="M22 35l9 9 16-18" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" fill="none" />
              </svg>
            </div>
            <div className="lh-modal-title">{i18n.modal.confirmed}</div>
            <p className="lh-confirmed-sub">{i18n.modal.confirmedSub}</p>
            <div className="lh-confirmed-ref">
              <span>{i18n.modal.ref}</span>
              <strong>{refCode}</strong>
            </div>
            <div className="lh-confirmed-summary">
              <div><span>{i18n.modal.sumPick}</span><strong>{data.pickup?.text}</strong></div>
              {!isHourly && <div><span>{i18n.modal.sumDrop}</span><strong>{data.drop?.text}</strong></div>}
              <div><span>{i18n.modal.sumWhen}</span><strong>{fmtDateLong(data.date)} · {data.time}</strong></div>
              <div><span>{i18n.modal.sumVeh}</span><strong>{veh.name}{isHourly ? ` · ${hours}h` : ""}</strong></div>
              <div><span>{i18n.modal.sumPax}</span><strong>{paxN}{!isHourly ? ` · ${bags} ${i18n.modal.bags.toLowerCase()}` : ""}</strong></div>
              <div><span>{i18n.modal.sumPrice}</span><strong>≈ {price} €</strong></div>
            </div>
            <button className="lh-confirm" onClick={onClose} type="button">{i18n.modal.close}</button>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="lh-modal-back" onClick={onClose}>
      <form className="lh-modal" onClick={(e) => e.stopPropagation()} onSubmit={submit}>
        <button className="lh-modal-close" onClick={onClose} aria-label={i18n.modal.close} type="button">
          <svg viewBox="0 0 16 16" width="14" height="14"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" /></svg>
        </button>
        <div className="lh-modal-body">
          <div className="lh-modal-head">
            <div className="lh-modal-eyebrow">{i18n.modal.eyebrow}</div>
            <div className="lh-modal-title">
              {titleArr[0]} <em style={{ fontStyle: "italic", color: "var(--accent)" }}>{titleArr[1]}</em>
            </div>
          </div>

          <div className="lh-trip">
            <div className="lh-trip-line">
              <div className="lh-trip-dot lh-trip-dot-pick"></div>
              <div className="lh-trip-col">
                <div className="lh-trip-l">{i18n.modal.tripPick}</div>
                <div className="lh-trip-v">{data.pickup?.text}</div>
                {data.pickup?.sub && <div className="lh-trip-s">{data.pickup.sub}</div>}
              </div>
            </div>
            {!isHourly && (
              <div className="lh-trip-line">
                <div className="lh-trip-dot lh-trip-dot-drop"></div>
                <div className="lh-trip-col">
                  <div className="lh-trip-l">{i18n.modal.tripDrop}</div>
                  <div className="lh-trip-v">{data.drop?.text}</div>
                  {data.drop?.sub && <div className="lh-trip-s">{data.drop.sub}</div>}
                </div>
              </div>
            )}
            <div className="lh-trip-line">
              <div className="lh-trip-dot lh-trip-dot-when"></div>
              <div className="lh-trip-col">
                <div className="lh-trip-l">{i18n.modal.tripWhen}</div>
                <div className="lh-trip-v">{fmtDateLong(data.date)} · {data.time}</div>
              </div>
            </div>
            {!isHourly && (
              <div className="lh-trip-meta">
                <span>{i18n.modal.distance}: <strong>{routeLoading ? "…" : route ? `${route.distanceKm.toFixed(1)} km` : "—"}</strong></span>
                <span>{i18n.modal.duration}: <strong>{routeLoading ? "…" : route ? fmtMin(route.durationMin) : "—"}</strong></span>
              </div>
            )}
          </div>

          {isHourly && (
            <>
              <div className="lh-section-h">{i18n.modal.tripDur}</div>
              <div className="lh-hours">
                {i18n.durations.map(d => (
                  <button key={d.v} type="button"
                    className={`lh-hour-pill ${hours === d.v ? "sel" : ""}`}
                    onClick={() => setHours(d.v)}>{d.l}</button>
                ))}
              </div>
            </>
          )}

          <div className="lh-section-h">{i18n.modal.vehicle}</div>
          <div className="lh-vehicles">
            {i18n.vehicles.map(v => {
              const p = isHourly ? Math.round(v.hourly * hours)
                : route ? Math.round(v.baseFlat + v.basePerKm * route.distanceKm)
                : null;
              return (
                <button key={v.id} type="button"
                  className={`lh-vehicle ${vehicle === v.id ? "sel" : ""}`}
                  onClick={() => setVehicle(v.id)}>
                  <div className="lh-veh-head">
                    <div className="lh-veh-name">{v.name}</div>
                    <div className="lh-veh-price">{p ? `${p} €` : "—"}</div>
                  </div>
                  <div className="lh-veh-model">{v.model}</div>
                  <div className="lh-veh-cap">
                    <span>
                      <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-6 6a6 6 0 1 1 12 0H2z" /></svg>
                      {v.cap}
                    </span>
                    <span>
                      <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor"><path d="M5 2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2V2zm1 0v2h4V2H6z" /></svg>
                      {v.bags}
                    </span>
                  </div>
                </button>
              );
            })}
          </div>

          <div className="lh-section-h">{i18n.modal.passenger}</div>
          <div className="lh-pax">
            <div className="lh-field">
              <label>{i18n.modal.name}</label>
              <input value={name} onChange={(e) => setName(e.target.value)} required />
            </div>
            <div className="lh-field">
              <label>{i18n.modal.phone}</label>
              <input type="tel" value={phone} onChange={(e) => setPhone(e.target.value)} required placeholder="+33 6 12 34 56 78" />
            </div>
            <div className="lh-field">
              <label>{i18n.modal.paxN}</label>
              <div className="lh-stepper">
                <button type="button" onClick={() => setPaxN(Math.max(1, paxN - 1))}>−</button>
                <span>{paxN}</span>
                <button type="button" onClick={() => setPaxN(Math.min(veh.cap, paxN + 1))}>+</button>
              </div>
            </div>
            {!isHourly && (
              <div className="lh-field">
                <label>{i18n.modal.bags}</label>
                <div className="lh-stepper">
                  <button type="button" onClick={() => setBags(Math.max(0, bags - 1))}>−</button>
                  <span>{bags}</span>
                  <button type="button" onClick={() => setBags(Math.min(veh.bags, bags + 1))}>+</button>
                </div>
              </div>
            )}
            <div className="lh-field lh-field-wide">
              <label>{i18n.modal.notes}</label>
              <textarea rows="2" value={notes} onChange={(e) => setNotes(e.target.value)} placeholder={i18n.modal.notesPh} />
            </div>
          </div>

          <div className="lh-modal-foot">
            <div className="lh-foot-price">
              <div className="lh-foot-l">{i18n.modal.from}</div>
              <strong>{price ? `${price} €` : "—"}</strong>
              <div className="lh-foot-sub">{i18n.modal.disclaimer}</div>
            </div>
            <button className="lh-confirm" type="submit" disabled={!name.trim() || !phone.trim()}>
              {isHourly ? i18n.modal.confirmHourly : i18n.modal.confirm}
              <svg viewBox="0 0 20 20" width="14" height="14" fill="none">
                <path d="M3 10h13m0 0l-5-5m5 5l-5 5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
            </button>
          </div>
        </div>
      </form>
    </div>
  );
}

/* ---------- Booking widget (replaces components.jsx Booking) ---------- */
function Booking({ t, lang, light = false, center = false, onSubmit = null }) {
  const i18n = BK_I18N[lang] || BK_I18N.fr;
  // Persisted state
  const [tab, setTab] = useState(0);
  const [pickup, setPickup] = useState(null); // {text,sub,lng,lat,cat}
  const [drop, setDrop] = useState(null);
  const [date, setDate] = useState(null);     // Date object
  const [time, setTime] = useState("");       // "HH:MM"
  const [focused, setFocused] = useState(false);
  const [showCal, setShowCal] = useState(false);
  const [showTime, setShowTime] = useState(false);
  const [errors, setErrors] = useState({});
  const [showRecap, setShowRecap] = useState(false);
  const [hydrated, setHydrated] = useState(false);
  const [popFlipped, setPopFlipped] = useState({ pickup: false, drop: false, date: false, time: false });
  const dateAnchorRef = useRef(null);
  const timeAnchorRef = useRef(null);
  const tabsHidden = popFlipped.pickup || popFlipped.drop || popFlipped.date || popFlipped.time;

  // Body scroll lock when widget is focused
  useEffect(() => {
    if (!focused) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => { document.body.style.overflow = prev; };
  }, [focused]);

  // Hydrate from localStorage
  useEffect(() => {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (raw) {
        const j = JSON.parse(raw);
        if (j.tab != null) setTab(j.tab);
        if (j.pickup) setPickup(j.pickup);
        if (j.drop) setDrop(j.drop);
        if (j.date) setDate(new Date(j.date));
        if (j.time) setTime(j.time);
      }
    } catch (e) {}
    setHydrated(true);
  }, []);

  // Persist
  useEffect(() => {
    if (!hydrated) return;
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify({
        tab, pickup, drop,
        date: date ? date.toISOString() : null,
        time,
      }));
    } catch (e) {}
  }, [hydrated, tab, pickup, drop, date, time]);

  const formatDate = (d) => {
    if (!d) return "";
    const loc = lang === "fr" ? "fr-FR" : lang === "ru" ? "ru-RU" : "en-GB";
    return d.toLocaleDateString(loc, { day: "2-digit", month: "2-digit", year: "numeric" });
  };

  const open = () => setFocused(true);
  const closeAll = () => { setFocused(false); setShowCal(false); setShowTime(false); };

  function tryGo() {
    const errs = {};
    if (!pickup || !pickup.lng) errs.pickup = i18n.err.pickup;
    if (tab !== 1 && (!drop || !drop.lng)) errs.drop = i18n.err.drop;
    if (!date) errs.date = i18n.err.date;
    if (!time) errs.time = i18n.err.time;
    setErrors(errs);
    if (Object.keys(errs).length > 0) return;
    setFocused(false); setShowCal(false); setShowTime(false);
    if (onSubmit) {
      onSubmit({ tab, pickup, drop, date, time });
      return;
    }
    if (window.LEON_BOOKING_REDIRECT) {
      const params = new URLSearchParams();
      params.set("tab", String(tab));
      if (pickup) params.set("pickup", JSON.stringify(pickup));
      if (drop) params.set("drop", JSON.stringify(drop));
      if (date) params.set("date", date.toISOString());
      if (time) params.set("time", time);
      window.location.href = "Reservation.html?" + params.toString();
      return;
    }
    setShowRecap(true);
  }

  const isHourly = tab === 1;

  return (
    <>
      <div className={`booking-backdrop ${focused ? "visible" : ""}`} onClick={closeAll} />
      <div className={`booking-wrap ${center ? "center" : ""}`}>
        <div className={`booking-tabs ${light ? "in-light" : ""} ${tabsHidden ? "tabs-hidden" : ""}`}>
          {t.booking.tabs.map((x, i) => (
            <button key={i} className={`booking-tab ${tab === i ? "active" : ""}`} onClick={() => setTab(i)} type="button">{x}</button>
          ))}
        </div>
        <div className={`booking ${light ? "dark-on-light" : ""} ${focused ? "booking-expanded" : ""}`}>
          <div className={`booking-fields ${isHourly ? "no-drop" : ""}`}>
            <div className={`booking-field ${errors.pickup ? "has-error" : ""}`}>
              <label>{t.booking.pickup}</label>
              <AddressInput
                value={pickup}
                onChange={(v) => { setPickup(v); if (v?.lng) setErrors(e => ({ ...e, pickup: null })); }}
                placeholder={t.booking.pickupPh}
                lang={lang}
                onFocus={open}
                onFlip={(f) => setPopFlipped(s => ({ ...s, pickup: f }))}
              />
            </div>
            {!isHourly && (
              <div className={`booking-field ${errors.drop ? "has-error" : ""}`}>
                <label>{t.booking.drop}</label>
                <AddressInput
                  value={drop}
                  onChange={(v) => { setDrop(v); if (v?.lng) setErrors(e => ({ ...e, drop: null })); }}
                  placeholder={t.booking.dropPh}
                  lang={lang}
                  onFocus={open}
                  onFlip={(f) => setPopFlipped(s => ({ ...s, drop: f }))}
                />
              </div>
            )}
            <div ref={dateAnchorRef} className={`booking-field ${errors.date ? "has-error" : ""}`} style={{ position: "relative" }}>
              <label>{t.booking.date}</label>
              <input
                placeholder={lang === "fr" ? "JJ / MM / AAAA" : lang === "ru" ? "ДД / ММ / ГГГГ" : "DD / MM / YYYY"}
                value={formatDate(date)}
                readOnly
                onFocus={open}
                onClick={() => { open(); setShowCal(s => !s); setShowTime(false); }}
                style={{ cursor: "pointer" }}
              />
              <MiniCalendar
                anchorRef={dateAnchorRef}
                open={showCal}
                onClose={() => setShowCal(false)}
                onFlip={(f) => setPopFlipped(s => ({ ...s, date: f }))}
                value={date}
                lang={lang}
                onChange={(d) => { setDate(d); setShowCal(false); setErrors(e => ({ ...e, date: null })); }}
              />
            </div>
            <div ref={timeAnchorRef} className={`booking-field ${errors.time ? "has-error" : ""}`} style={{ position: "relative" }}>
              <label>{t.booking.time}</label>
              <input
                placeholder="12:30"
                value={time}
                readOnly
                onFocus={open}
                onClick={() => { open(); setShowTime(s => !s); setShowCal(false); }}
                style={{ cursor: "pointer" }}
              />
              <TimePicker
                anchorRef={timeAnchorRef}
                open={showTime}
                onFlip={(f) => setPopFlipped(s => ({ ...s, time: f }))}
                value={time}
                lang={lang}
                dateContext={date}
                onSetDate={(d) => { setDate(d); setErrors(e => ({ ...e, date: null })); }}
                onChange={(v) => { setTime(v); setErrors(e => ({ ...e, time: null })); }}
                onClose={() => setShowTime(false)}
              />
            </div>
          </div>
          <button className="booking-go" aria-label={t.booking.go} onClick={tryGo} type="button">
            <svg viewBox="0 0 20 20" fill="none" aria-hidden="true">
              <path d="M3 10h13m0 0l-5-5m5 5l-5 5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
          </button>
        </div>
      </div>
      {showRecap && (
        <RecapModal
          data={{ tab, pickup, drop, date, time }}
          t={t}
          lang={lang}
          onClose={() => setShowRecap(false)}
        />
      )}
    </>
  );
}

// Expose to other Babel scripts
window.Booking = Booking;
window.MiniCalendar = MiniCalendar;
