/**
 * mypro.ch · app.central — Application réservation client (4 étapes)
 *
 *  Étape 1 : Trajet (origine, destination, date, heure, passagers, bagages)
 *  Étape 2 : Véhicule (sélection gamme + prix)
 *  Étape 3 : Coordonnées (client + paiement)
 *  Étape 4 : Confirmation (récap → POST /public/bookings)
 */
const { useState, useEffect, useRef, useCallback, useMemo, useLayoutEffect } = React;
const { createPortal } = ReactDOM;
const cfg = window.MyProConfig;
const api = window.MyProApi;
const Maps = window.MyProMaps;

/* ─────────────────────────────────────────────────────────────────────────
   DatePicker / TimePicker (cf DOCS/APIS/ULTIME_DESIGN_ELEMENTS.md)
   Portail vers document.body pour ne jamais être clippé.
   ─────────────────────────────────────────────────────────────────────── */

const LANG = (typeof navigator !== 'undefined' && navigator.language) || 'fr';

const localizedMonthName = (lang, monthIndex) => {
    try {
        const d = new Date(2000, monthIndex, 1);
        const name = new Intl.DateTimeFormat(lang, { month: 'long' }).format(d);
        return name.charAt(0).toUpperCase() + name.slice(1);
    } catch (e) {
        return ['Jan','Fév','Mar','Avr','Mai','Juin','Juil','Août','Sep','Oct','Nov','Déc'][monthIndex];
    }
};
const localizedDayShort = (lang, dayIndex) => {
    try {
        const d = new Date(2024, 0, 1 + dayIndex);
        const name = new Intl.DateTimeFormat(lang, { weekday: 'short' }).format(d).replace(/\.$/, '');
        return name.charAt(0).toUpperCase() + name.slice(1);
    } catch (e) {
        return ['Lun','Mar','Mer','Jeu','Ven','Sam','Dim'][dayIndex];
    }
};
const getDefaultTime = () => {
    const now = new Date();
    const m = now.getMinutes();
    let nextQ = Math.ceil((m + 1) / 15) * 15;
    let h = now.getHours();
    if (nextQ >= 60) { h++; nextQ = 0; }
    if (h >= 24) { h = 0; }
    return String(h).padStart(2, '0') + ':' + String(nextQ).padStart(2, '0');
};

/** Hook commun : pilote l'ouverture + calcul des coords du popover en fixed/portal. */
function usePopoverPosition(open, setOpen, { width = 360, estHeight = 360, position = 'auto' } = {}) {
    const btnRef = useRef(null);
    const popRef = useRef(null);
    const [coords, setCoords] = useState(null);

    const computePosition = useCallback(() => {
        const btn = btnRef.current;
        if (!btn) return;
        const r = btn.getBoundingClientRect();
        const spaceBelow = window.innerHeight - r.bottom;
        const spaceAbove = r.top;
        const wantTop = position === 'top'
            || (position === 'auto' && spaceBelow < estHeight && spaceAbove > spaceBelow);

        let left = r.left;
        const w = Math.min(width, window.innerWidth - 16);
        if (left + w > window.innerWidth - 8) {
            left = Math.max(8, window.innerWidth - w - 8);
        }
        setCoords({
            top: wantTop ? r.top - 8 : r.bottom + 8,
            left,
            btnWidth: r.width,
            placement: wantTop ? 'top' : 'bottom',
        });
    }, [width, estHeight, position]);

    useLayoutEffect(() => {
        if (!open) { setCoords(null); return; }
        computePosition();
        const onScroll = () => computePosition();
        window.addEventListener('scroll', onScroll, true);
        window.addEventListener('resize', onScroll);
        return () => {
            window.removeEventListener('scroll', onScroll, true);
            window.removeEventListener('resize', onScroll);
        };
    }, [open, computePosition]);

    useEffect(() => {
        if (!open) return;
        const onClick = (e) => {
            const t = e.target;
            if (btnRef.current?.contains(t)) return;
            if (popRef.current?.contains(t)) return;
            setOpen(false);
        };
        document.addEventListener('mousedown', onClick);
        return () => document.removeEventListener('mousedown', onClick);
    }, [open, setOpen]);

    return { btnRef, popRef, coords };
}

function PickerButton({ open, icon, value, placeholder, onClick, btnRef }) {
    const accent = 'var(--color-primary, #B8956B)';
    return (
        <button ref={btnRef} type="button" onClick={onClick}
            style={{
                width: '100%', padding: '14px 14px', background: '#fff',
                border: '2px solid ' + (open ? accent : '#e5e7eb'),
                borderRadius: 12, textAlign: 'left',
                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                gap: 8, cursor: 'pointer', fontSize: 14, fontWeight: 600,
                fontFamily: 'inherit', color: '#1e293b', transition: 'border-color 0.2s',
            }}>
            <span style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <i className={`fas ${icon}`} style={{ color: accent, fontSize: 14 }}></i>
                <span style={{ color: value ? '#1e293b' : '#94a3b8', fontWeight: 600 }}>
                    {value || placeholder}
                </span>
            </span>
            <i className={'fas fa-chevron-' + (open ? 'up' : 'down')} style={{ color: '#cbd5e1', fontSize: 10 }}></i>
        </button>
    );
}

function DatePicker({ value, onChange, minDate, placeholder = 'Choisir une date' }) {
    const [open, setOpen] = useState(false);
    const today = new Date(); today.setHours(0, 0, 0, 0);
    const initial = value ? new Date(value + 'T00:00:00') : today;
    const [viewYear, setViewYear] = useState(initial.getFullYear());
    const [viewMonth, setViewMonth] = useState(initial.getMonth());
    const { btnRef, popRef, coords } = usePopoverPosition(open, setOpen, { width: 360, estHeight: 380 });
    const monthNames = useMemo(() => Array.from({ length: 12 }, (_, i) => localizedMonthName(LANG, i)), []);
    const dayNames = useMemo(() => Array.from({ length: 7 }, (_, i) => localizedDayShort(LANG, i)), []);

    const todayStr = today.getFullYear() + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0');
    const minStr = minDate || todayStr;

    const firstDay = new Date(viewYear, viewMonth, 1);
    let startDay = firstDay.getDay() - 1; if (startDay < 0) startDay = 6;
    const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
    const cells = [];
    for (let i = 0; i < startDay; i++) cells.push(null);
    for (let d = 1; d <= daysInMonth; d++) cells.push(d);

    const accent = 'var(--color-primary, #B8956B)';
    const accentDark = 'var(--color-primary-dark, #A07D55)';

    const selectDay = (d) => {
        const v = viewYear + '-' + String(viewMonth + 1).padStart(2, '0') + '-' + String(d).padStart(2, '0');
        onChange(v);
        setOpen(false);
    };
    const prevMonth = () => {
        if (viewMonth === 0) { setViewMonth(11); setViewYear(viewYear - 1); }
        else setViewMonth(viewMonth - 1);
    };
    const nextMonth = () => {
        if (viewMonth === 11) { setViewMonth(0); setViewYear(viewYear + 1); }
        else setViewMonth(viewMonth + 1);
    };
    const formatDisplay = (v) => {
        if (!v) return null;
        const p = v.split('-');
        return p[2] + '/' + p[1] + '/' + p[0];
    };

    const popover = open && coords ? (
        <div ref={popRef}
            style={{
                position: 'fixed',
                top: coords.placement === 'top' ? undefined : coords.top,
                bottom: coords.placement === 'top' ? window.innerHeight - coords.top : undefined,
                left: coords.left,
                width: 360, maxWidth: 'calc(100vw - 16px)',
                background: '#fff', borderRadius: 16,
                border: '2px solid #f1f5f9', padding: 16, zIndex: 1000,
                boxShadow: coords.placement === 'top'
                    ? '0 -10px 40px rgba(0,0,0,0.18)'
                    : '0 10px 40px rgba(0,0,0,0.18)',
            }}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 }}>
                <button type="button" onClick={prevMonth} style={{ width: 36, height: 36, borderRadius: '50%', border: 'none', background: '#f8fafc', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                    <i className="fas fa-chevron-left" style={{ color: '#64748b', fontSize: 12 }}></i>
                </button>
                <span style={{ fontWeight: 700, fontSize: 15, color: '#1e293b' }}>{monthNames[viewMonth]} {viewYear}</span>
                <button type="button" onClick={nextMonth} style={{ width: 36, height: 36, borderRadius: '50%', border: 'none', background: '#f8fafc', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                    <i className="fas fa-chevron-right" style={{ color: '#64748b', fontSize: 12 }}></i>
                </button>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 4, marginBottom: 6 }}>
                {dayNames.map((dn, i) => (
                    <div key={i} style={{ height: 32, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, fontWeight: 700, color: '#94a3b8', textTransform: 'uppercase', letterSpacing: 0.5 }}>{dn}</div>
                ))}
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 4 }}>
                {cells.map((d, i) => {
                    if (!d) return <div key={'e' + i} style={{ height: 40 }}></div>;
                    const dateStr = viewYear + '-' + String(viewMonth + 1).padStart(2, '0') + '-' + String(d).padStart(2, '0');
                    const isSelected = dateStr === value;
                    const isToday = dateStr === todayStr;
                    const isPast = dateStr < minStr;
                    return (
                        <button key={d} type="button" disabled={isPast}
                            onClick={() => { if (!isPast) selectDay(d); }}
                            style={{
                                width: 40, height: 40, borderRadius: 12,
                                border: isToday && !isSelected ? '2px solid ' + accent : '2px solid transparent',
                                background: isSelected ? `linear-gradient(135deg, ${accent}, ${accentDark})` : (isToday ? 'rgba(184,149,107,0.08)' : 'transparent'),
                                color: isSelected ? '#fff' : (isPast ? '#cbd5e1' : '#1e293b'),
                                fontSize: 14, fontWeight: isSelected || isToday ? 700 : 500,
                                cursor: isPast ? 'not-allowed' : 'pointer',
                                display: 'flex', alignItems: 'center', justifyContent: 'center',
                                margin: '0 auto', transition: 'all 0.15s',
                            }}>
                            {d}
                        </button>
                    );
                })}
            </div>
        </div>
    ) : null;

    return (
        <div style={{ position: 'relative' }}>
            <PickerButton open={open} icon="fa-calendar-alt" value={formatDisplay(value)} placeholder={placeholder} onClick={() => setOpen(!open)} btnRef={btnRef} />
            {popover && createPortal(popover, document.body)}
        </div>
    );
}

function TimePicker({ value, onChange, placeholder = 'Choisir une heure' }) {
    const [open, setOpen] = useState(false);
    const { btnRef, popRef, coords } = usePopoverPosition(open, setOpen, { width: 200, estHeight: 240 });
    const listRef = useRef(null);

    useEffect(() => { if (!value) onChange(getDefaultTime()); }, []);

    useEffect(() => {
        if (open && listRef.current && value) {
            const container = listRef.current;
            const slots = container.children;
            for (let i = 0; i < slots.length; i++) {
                if (slots[i].dataset.slot === value) {
                    const target = slots[i];
                    container.scrollTop = target.offsetTop - (container.clientHeight / 2) + (target.clientHeight / 2);
                    break;
                }
            }
        }
    }, [open, coords]);

    const slots = useMemo(() => {
        const s = [];
        for (let hh = 0; hh < 24; hh++) {
            for (let mm = 0; mm < 60; mm += 15) {
                s.push(String(hh).padStart(2, '0') + ':' + String(mm).padStart(2, '0'));
            }
        }
        return s;
    }, []);

    const accent = 'var(--color-primary, #B8956B)';
    const accentDark = 'var(--color-primary-dark, #A07D55)';

    const popover = open && coords ? (
        <div ref={(el) => { popRef.current = el; listRef.current = el; }}
            style={{
                position: 'fixed',
                top: coords.placement === 'top' ? undefined : coords.top,
                bottom: coords.placement === 'top' ? window.innerHeight - coords.top : undefined,
                left: coords.left,
                width: Math.max(coords.btnWidth || 200, 180),
                maxWidth: 'calc(100vw - 16px)',
                background: '#fff', borderRadius: 16,
                border: '2px solid #f1f5f9', zIndex: 1000,
                boxShadow: coords.placement === 'top'
                    ? '0 -10px 40px rgba(0,0,0,0.18)'
                    : '0 10px 40px rgba(0,0,0,0.18)',
                maxHeight: 240, overflowY: 'auto',
            }}>
            {slots.map((slot) => {
                const isSelected = slot === value;
                return (
                    <button key={slot} data-slot={slot} type="button"
                        onClick={() => { onChange(slot); setOpen(false); }}
                        style={{
                            display: 'block', width: '100%', padding: '10px 16px',
                            fontSize: 14, fontWeight: isSelected ? 700 : 400,
                            fontFamily: 'monospace', border: 'none',
                            borderBottom: '1px solid #f1f5f9',
                            background: isSelected ? `linear-gradient(135deg, ${accent}, ${accentDark})` : 'transparent',
                            color: isSelected ? '#fff' : '#1e293b',
                            cursor: 'pointer', textAlign: 'left',
                        }}>
                        {slot}
                    </button>
                );
            })}
        </div>
    ) : null;

    return (
        <div style={{ position: 'relative' }}>
            <PickerButton open={open} icon="fa-clock" value={value} placeholder={placeholder} onClick={() => setOpen(!open)} btnRef={btnRef} />
            {popover && createPortal(popover, document.body)}
        </div>
    );
}

/* ─────────────────────────────────────────────────────────────────────────
   Carte Leaflet — look Google Maps
   - Tuiles CARTO Voyager (rendu street très proche de Google Maps)
   - Pins SVG style Google (rouge A / vert B) avec ombre douce
   - Polyline bleu Google #4285F4 + halo blanc en dessous
   ─────────────────────────────────────────────────────────────────────── */

// Couleurs Google Maps officielles
const GMAP_BLUE   = '#4285F4';
const GMAP_RED    = '#EA4335';
const GMAP_GREEN  = '#34A853';

function googlePinSvg(letter, color) {
    return (
        '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 44" width="32" height="44">' +
            '<defs>' +
                '<filter id="ps" x="-50%" y="-30%" width="200%" height="160%">' +
                    '<feGaussianBlur in="SourceAlpha" stdDeviation="1.2"/>' +
                    '<feOffset dx="0" dy="1.5" result="o"/>' +
                    '<feComponentTransfer><feFuncA type="linear" slope="0.45"/></feComponentTransfer>' +
                    '<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>' +
                '</filter>' +
            '</defs>' +
            '<path filter="url(#ps)" fill="' + color + '" stroke="#ffffff" stroke-width="1.4" ' +
                'd="M16 1C8.27 1 2 7.27 2 15c0 9.6 11.45 24.6 13.06 26.66a1.2 1.2 0 0 0 1.88 0C18.55 39.6 30 24.6 30 15 30 7.27 23.73 1 16 1z"/>' +
            '<text x="16" y="20" text-anchor="middle" font-family="Arial, system-ui, sans-serif" ' +
                'font-size="13" font-weight="700" fill="#ffffff">' + letter + '</text>' +
        '</svg>'
    );
}

function MapView({ origin, destination, polyline, center, zoom }) {
    const ref = useRef(null);
    const mapRef = useRef(null);
    const tileRef = useRef(null);
    const layersRef = useRef({ origin: null, dest: null, line: null, halo: null });
    const [layerType, setLayerType] = useState(() => localStorage.getItem('mypro_map_type') || 'google');

    useEffect(() => {
        if (!ref.current || mapRef.current) return;
        const map = L.map(ref.current, {
            zoomControl: false,
            attributionControl: true,
            scrollWheelZoom: true,
            zoomSnap: 0.5,
        }).setView(center || cfg.DEFAULT_CENTER, zoom || cfg.DEFAULT_ZOOM);

        const layer = (cfg.TILE_LAYERS && cfg.TILE_LAYERS[layerType]) || { url: cfg.TILE_URL, attribution: cfg.TILE_ATTR };
        tileRef.current = L.tileLayer(layer.url, {
            attribution: layer.attribution,
            maxZoom: 20,
            detectRetina: true,
            subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
        }).addTo(map);

        L.control.zoom({ position: 'bottomright' }).addTo(map);
        mapRef.current = map;

        // Fix Leaflet "tuiles grises" — le container n'a souvent pas sa taille
        // finale au mount. On force le recalcul plusieurs fois pendant le layout.
        const invalidate = () => map.invalidateSize();
        requestAnimationFrame(invalidate);
        setTimeout(invalidate, 100);
        setTimeout(invalidate, 350);
        setTimeout(invalidate, 800);

        // Observer les changements de taille du container (responsive, sidebar, etc.)
        let ro = null;
        if (typeof ResizeObserver !== 'undefined') {
            ro = new ResizeObserver(() => map.invalidateSize());
            ro.observe(ref.current);
        }
        window.addEventListener('resize', invalidate);

        return () => {
            window.removeEventListener('resize', invalidate);
            if (ro) ro.disconnect();
        };
    }, []);

    // Switch de couche
    useEffect(() => {
        const map = mapRef.current;
        if (!map || !cfg.TILE_LAYERS) return;
        const layer = cfg.TILE_LAYERS[layerType] || cfg.TILE_LAYERS.google;
        if (tileRef.current) map.removeLayer(tileRef.current);
        tileRef.current = L.tileLayer(layer.url, {
            attribution: layer.attribution,
            maxZoom: 20,
            detectRetina: true,
            subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
        }).addTo(map);
        localStorage.setItem('mypro_map_type', layerType);
    }, [layerType]);

    useEffect(() => {
        const map = mapRef.current;
        if (!map) return;
        const layers = layersRef.current;
        ['origin', 'dest', 'line', 'halo'].forEach(k => {
            if (layers[k]) { map.removeLayer(layers[k]); layers[k] = null; }
        });

        const makeIcon = (letter, color) => L.divIcon({
            className: 'gmap-pin',
            html: googlePinSvg(letter, color),
            iconSize: [32, 44],
            iconAnchor: [16, 42],
            popupAnchor: [0, -38],
        });

        const bounds = [];
        if (origin?.lat) {
            layers.origin = L.marker([origin.lat, origin.lng], {
                icon: makeIcon('A', GMAP_GREEN),
            }).addTo(map);
            bounds.push([origin.lat, origin.lng]);
        }
        if (destination?.lat) {
            layers.dest = L.marker([destination.lat, destination.lng], {
                icon: makeIcon('B', GMAP_RED),
            }).addTo(map);
            bounds.push([destination.lat, destination.lng]);
        }
        if (polyline) {
            const coords = Maps.decodePolyline(polyline);
            if (coords.length > 1) {
                // Halo blanc (effet "border") sous le tracé bleu — c'est ce qui donne le look Google
                layers.halo = L.polyline(coords, {
                    color: '#ffffff', weight: 9, opacity: 1, lineCap: 'round', lineJoin: 'round',
                }).addTo(map);
                layers.line = L.polyline(coords, {
                    color: GMAP_BLUE, weight: 5, opacity: 1, lineCap: 'round', lineJoin: 'round',
                }).addTo(map);
                coords.forEach(c => bounds.push(c));
            }
        }
        if (bounds.length === 1) map.setView(bounds[0], 14);
        else if (bounds.length > 1) map.fitBounds(bounds, { padding: [50, 50], maxZoom: 16 });
        else if (center) map.setView(center, zoom || cfg.DEFAULT_ZOOM);
    }, [origin, destination, polyline, center && center[0], center && center[1], zoom]);

    const LAYERS = [
        { id: 'google',    label: 'Plan',      icon: 'fa-map' },
        { id: 'satellite', label: 'Satellite', icon: 'fa-satellite' },
        { id: 'hybrid',    label: 'Hybride',   icon: 'fa-layer-group' },
    ];

    return (
        <div className="mp-map-wrap w-full h-full">
            <div ref={ref} className="mp-map w-full h-full rounded-2xl overflow-hidden border border-ink-200" />
            <div className="mp-map-switch">
                {LAYERS.map(l => (
                    <button key={l.id}
                            className={`mp-map-switch__btn ${layerType === l.id ? 'active' : ''}`}
                            onClick={() => setLayerType(l.id)}
                            title={l.label}>
                        <i className={`fas ${l.icon}`}></i>
                        <span>{l.label}</span>
                    </button>
                ))}
            </div>
        </div>
    );
}

/* ─────────────────────────────────────────────────────────────────────────
   Champ adresse avec autocomplete (api.maps.swissapp.net /place)
   ─────────────────────────────────────────────────────────────────────── */
function AddressInput({ value, onChange, placeholder, icon = 'fa-location-dot' }) {
    const [text, setText] = useState(value?.label || '');
    const [suggestions, setSuggestions] = useState([]);
    const [open, setOpen] = useState(false);
    const [loading, setLoading] = useState(false);
    const debounceRef = useRef(null);

    useEffect(() => { setText(value?.label || ''); }, [value?.label]);

    const onTextChange = (q) => {
        setText(q);
        if (debounceRef.current) clearTimeout(debounceRef.current);
        if (q.length < 3) { setSuggestions([]); setOpen(false); return; }
        setLoading(true);
        debounceRef.current = setTimeout(async () => {
            const res = await Maps.autocomplete(q);
            setSuggestions(res);
            setOpen(true);
            setLoading(false);
        }, 250);
    };

    const select = async (s) => {
        setOpen(false);
        setText(s.label);
        const geo = await Maps.geocode(s.label);
        onChange({ label: s.label, ...(geo || {}) });
    };

    return (
        <div className="relative">
            <div className="relative">
                <i className={`fa-solid ${icon} absolute left-4 top-1/2 -translate-y-1/2 text-ink-400`}></i>
                <input
                    type="text"
                    className="mp-input pl-11"
                    placeholder={placeholder}
                    value={text}
                    onChange={(e) => onTextChange(e.target.value)}
                    onFocus={() => suggestions.length > 0 && setOpen(true)}
                    onBlur={() => setTimeout(() => setOpen(false), 150)}
                />
                {loading && <i className="fa-solid fa-spinner fa-spin absolute right-4 top-1/2 -translate-y-1/2 text-ink-400"></i>}
            </div>
            {open && suggestions.length > 0 && (
                <div className="suggest">
                    {suggestions.map((s, i) => (
                        <div key={i} className="suggest-item" onMouseDown={() => select(s)}>
                            <div className="font-medium text-ink-900">{s.main}</div>
                            {s.secondary && <div className="text-xs text-ink-500">{s.secondary}</div>}
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
}

/* ─────────────────────────────────────────────────────────────────────────
   Stepper
   ─────────────────────────────────────────────────────────────────────── */
function Stepper({ step }) {
    const steps = ['Trajet', 'Véhicule', 'Coordonnées', 'Confirmation'];
    return (
        <div className="flex items-center gap-2 sm:gap-4">
            {steps.map((label, i) => {
                const idx = i + 1;
                const cls = idx === step ? 'active' : (idx < step ? 'done' : '');
                return (
                    <React.Fragment key={i}>
                        <div className="flex items-center gap-2">
                            <span className={`step-dot ${cls}`}>
                                {idx < step ? <i className="fa-solid fa-check"></i> : idx}
                            </span>
                            <span className={`text-sm hidden sm:inline ${idx === step ? 'font-semibold text-ink-900' : 'text-ink-500'}`}>{label}</span>
                        </div>
                        {idx < steps.length && <div className={`flex-1 h-px ${idx < step ? 'bg-green-500' : 'bg-ink-200'}`}></div>}
                    </React.Fragment>
                );
            })}
        </div>
    );
}

/* ─────────────────────────────────────────────────────────────────────────
   Étape 1 : Trajet
   ─────────────────────────────────────────────────────────────────────── */
function StepRoute({ booking, setBooking, onNext, route, setRoute, central }) {
    const [computing, setComputing] = useState(false);
    const [error, setError] = useState('');

    const compute = useCallback(async () => {
        if (!booking.origin?.label || !booking.destination?.label) return;
        setComputing(true); setError('');
        try {
            const r = await Maps.directions(booking.origin.label, booking.destination.label);
            if (!r) throw new Error('Itinéraire introuvable');
            setRoute(r);
        } catch (e) {
            setError(e.message);
            setRoute(null);
        } finally {
            setComputing(false);
        }
    }, [booking.origin?.label, booking.destination?.label, setRoute]);

    useEffect(() => { compute(); }, [compute]);

    const valid = booking.origin?.lat && booking.destination?.lat && booking.date && booking.time && route;

    return (
        <div className="grid lg:grid-cols-5 gap-6">
            <div className="lg:col-span-2 space-y-4">
                <div>
                    <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Départ</label>
                    <AddressInput
                        value={booking.origin}
                        onChange={(v) => setBooking({ ...booking, origin: v })}
                        placeholder="Adresse de départ"
                        icon="fa-circle-dot"
                    />
                </div>
                <div>
                    <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Destination</label>
                    <AddressInput
                        value={booking.destination}
                        onChange={(v) => setBooking({ ...booking, destination: v })}
                        placeholder="Adresse d'arrivée"
                        icon="fa-location-dot"
                    />
                </div>

                <div className="grid grid-cols-2 gap-3">
                    <div>
                        <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Date</label>
                        <DatePicker
                            value={booking.date}
                            onChange={(v) => setBooking({ ...booking, date: v })}
                            minDate={new Date().toISOString().slice(0, 10)}
                        />
                    </div>
                    <div>
                        <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Heure</label>
                        <TimePicker
                            value={booking.time}
                            onChange={(v) => setBooking({ ...booking, time: v })}
                        />
                    </div>
                </div>

                <div className="grid grid-cols-2 gap-3">
                    <div>
                        <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Passagers</label>
                        <select className="mp-input" value={booking.passengers}
                                onChange={(e) => setBooking({ ...booking, passengers: parseInt(e.target.value) })}>
                            {[1,2,3,4,5,6,7,8].map(n => <option key={n} value={n}>{n} pers.</option>)}
                        </select>
                    </div>
                    <div>
                        <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Bagages</label>
                        <select className="mp-input" value={booking.luggage}
                                onChange={(e) => setBooking({ ...booking, luggage: parseInt(e.target.value) })}>
                            {[0,1,2,3,4,5,6,7,8].map(n => <option key={n} value={n}>{n} bagage{n>1?'s':''}</option>)}
                        </select>
                    </div>
                </div>

                {error && <div className="text-sm text-red-600 bg-red-50 px-3 py-2 rounded-lg">{error}</div>}

                {route && (
                    <div className="bg-ink-50 rounded-xl p-4 border border-ink-200">
                        <div className="flex items-center justify-between text-sm">
                            <div className="flex items-center gap-2 text-ink-700">
                                <i className="fa-solid fa-route text-ink-400"></i>
                                <span className="font-semibold">{route.distance_text}</span>
                            </div>
                            <div className="flex items-center gap-2 text-ink-700">
                                <i className="fa-solid fa-clock text-ink-400"></i>
                                <span className="font-semibold">{route.duration_text}</span>
                            </div>
                        </div>
                    </div>
                )}

                <button
                    className="mp-btn mp-btn-primary w-full"
                    disabled={!valid || computing}
                    onClick={onNext}
                >
                    {computing ? <><span className="spinner"></span> Calcul…</> : <>Continuer <i className="fa-solid fa-arrow-right"></i></>}
                </button>
            </div>

            <div className="lg:col-span-3 h-[400px] lg:h-[560px]">
                <MapView
                    origin={booking.origin} destination={booking.destination} polyline={route?.polyline}
                    center={central?.latitude && central?.longitude ? [central.latitude, central.longitude] : null}
                    zoom={central?.radius || null}
                />
            </div>
        </div>
    );
}

/* ─────────────────────────────────────────────────────────────────────────
   Étape 2 : Véhicule
   ─────────────────────────────────────────────────────────────────────── */
function StepVehicle({ booking, setBooking, ranges, route, onBack, onNext }) {
    const distanceKm = (route?.distance_m || 0) / 1000;
    const durationMin = (route?.duration_s || 0) / 60;

    const computePrice = (range) => {
        const basePrice = range.base_price ?? 0;
        const ppk = range.cp_price_per_km ?? range.range_price_per_km ?? 0;
        const ppm = range.price_per_minute ?? 0;
        const pph = range.price_per_hour ?? 0;
        const min = range.minimum_price ?? range.price_minimum ?? 0;

        let p = basePrice + (distanceKm * ppk) + (durationMin * ppm);

        // Fallback "tarif horaire" si aucun prix au km/min n'est défini (mode disposition)
        if (p === 0 && pph > 0 && durationMin > 0) {
            p = (durationMin / 60) * pph;
        }

        if (p < min) p = min;
        return Math.round(p * 100) / 100;
    };

    const select = (range) => {
        const price = computePrice(range);
        setBooking({ ...booking, range_id: range.id, range_label: range.label, price });
    };

    return (
        <div className="space-y-4">
            <p className="text-sm text-ink-500">Distance estimée : <strong>{route?.distance_text}</strong> · Durée : <strong>{route?.duration_text}</strong></p>

            {ranges.length === 0 && (
                <div className="bg-amber-50 border border-amber-200 rounded-xl p-4 text-sm text-amber-800">
                    Aucune gamme tarifaire configurée pour cette centrale.
                </div>
            )}

            <div className="grid sm:grid-cols-2 gap-3">
                {ranges.map(r => {
                    const price = computePrice(r);
                    const sel = booking.range_id === r.id;
                    return (
                        <div key={r.id} className={`vehicle-card ${sel ? 'selected' : ''}`} onClick={() => select(r)}>
                            <div className="flex items-center gap-3">
                                {r.photo_url ? (
                                    <img src={r.photo_url} alt={r.label}
                                         className="vehicle-card__img"
                                         loading="lazy"
                                         onError={e => { e.target.style.display = 'none'; }} />
                                ) : (
                                    <div className="vehicle-card__img vehicle-card__img--ph">
                                        <i className="fa-solid fa-car text-ink-400 text-2xl"></i>
                                    </div>
                                )}
                                <div className="flex-1 min-w-0">
                                    <h3 className="font-bold text-ink-900">{r.label}</h3>
                                    {r.description && <p className="text-xs text-ink-500 mt-0.5 truncate">{r.description}</p>}
                                    <div className="flex gap-3 mt-1.5 text-xs text-ink-600">
                                        {r.seats_max && <span><i className="fa-solid fa-user mr-1"></i>{r.seats_max}</span>}
                                        {r.luggage_max && <span><i className="fa-solid fa-suitcase mr-1"></i>{r.luggage_max}</span>}
                                    </div>
                                </div>
                                <div className="text-right shrink-0">
                                    {price > 0 ? (
                                        <div className="text-2xl font-extrabold text-ink-900">{price}<span className="text-sm font-medium text-ink-500"> CHF</span></div>
                                    ) : (
                                        <div className="text-sm font-semibold text-ink-500">Sur demande</div>
                                    )}
                                </div>
                            </div>
                        </div>
                    );
                })}
            </div>

            <div className="flex gap-3 pt-2">
                <button className="mp-btn mp-btn-ghost" onClick={onBack}>
                    <i className="fa-solid fa-arrow-left"></i> Retour
                </button>
                <button className="mp-btn mp-btn-primary flex-1" disabled={!booking.range_id} onClick={onNext}>
                    Continuer <i className="fa-solid fa-arrow-right"></i>
                </button>
            </div>
        </div>
    );
}

/* ─────────────────────────────────────────────────────────────────────────
   Étape 3 : Coordonnées
   ─────────────────────────────────────────────────────────────────────── */
function StepCustomer({ booking, setBooking, central, onBack, onNext }) {
    const upd = (k, v) => setBooking({ ...booking, [k]: v });
    const methods = useMemo(() => [
        central.is_method_cash && { id: 'cash', label: 'Espèces (au chauffeur)', icon: 'fa-money-bill-wave' },
        central.is_method_card && { id: 'card', label: 'Carte bancaire (en ligne)', icon: 'fa-credit-card' },
        central.is_method_card_to_driver && { id: 'card_driver', label: 'Carte (au chauffeur)', icon: 'fa-credit-card' },
        central.is_method_bill && { id: 'bill', label: 'Facture', icon: 'fa-file-invoice' },
    ].filter(Boolean), [central]);

    useEffect(() => {
        if (!booking.payment_method && methods.length > 0) {
            setBooking({ ...booking, payment_method: methods[0].id });
        }
    }, [methods]);

    const valid = booking.firstname && booking.lastname && booking.email && booking.mobile && booking.payment_method;

    return (
        <div className="space-y-4">
            <div className="grid sm:grid-cols-2 gap-3">
                <div>
                    <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Prénom *</label>
                    <input className="mp-input" value={booking.firstname || ''} onChange={(e) => upd('firstname', e.target.value)} />
                </div>
                <div>
                    <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Nom *</label>
                    <input className="mp-input" value={booking.lastname || ''} onChange={(e) => upd('lastname', e.target.value)} />
                </div>
                <div>
                    <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Email *</label>
                    <input type="email" className="mp-input" value={booking.email || ''} onChange={(e) => upd('email', e.target.value)} />
                </div>
                <div>
                    <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Mobile *</label>
                    <input type="tel" className="mp-input" placeholder="+41 …" value={booking.mobile || ''} onChange={(e) => upd('mobile', e.target.value)} />
                </div>
                <div>
                    <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">N° de vol</label>
                    <input className="mp-input" value={booking.flight_number || ''} onChange={(e) => upd('flight_number', e.target.value)} />
                </div>
                <div>
                    <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Société (facture)</label>
                    <input className="mp-input" value={booking.company || ''} onChange={(e) => upd('company', e.target.value)} />
                </div>
            </div>

            <div>
                <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Notes pour le chauffeur</label>
                <textarea className="mp-input" rows="3" value={booking.notes || ''} onChange={(e) => upd('notes', e.target.value)}></textarea>
            </div>

            <div>
                <label className="block text-xs font-semibold text-ink-700 uppercase tracking-wide mb-2">Mode de paiement *</label>
                <div className="grid sm:grid-cols-2 gap-2">
                    {methods.map(m => (
                        <label key={m.id} className={`vehicle-card ${booking.payment_method === m.id ? 'selected' : ''} !p-3 flex items-center gap-3`}>
                            <input type="radio" name="pay" className="hidden" checked={booking.payment_method === m.id} onChange={() => upd('payment_method', m.id)} />
                            <i className={`fa-solid ${m.icon} text-ink-700`}></i>
                            <span className="text-sm font-medium">{m.label}</span>
                        </label>
                    ))}
                </div>
            </div>

            <div className="flex gap-3 pt-2">
                <button className="mp-btn mp-btn-ghost" onClick={onBack}>
                    <i className="fa-solid fa-arrow-left"></i> Retour
                </button>
                <button className="mp-btn mp-btn-primary flex-1" disabled={!valid} onClick={onNext}>
                    Vérifier la réservation <i className="fa-solid fa-arrow-right"></i>
                </button>
            </div>
        </div>
    );
}

/* ─────────────────────────────────────────────────────────────────────────
   Étape 4 : Confirmation
   ─────────────────────────────────────────────────────────────────────── */
function StepConfirm({ booking, central, route, onBack, onSubmit, submitting, error }) {
    const Row = ({ label, value, icon }) => (
        <div className="flex items-start justify-between py-2.5 border-b border-ink-100 last:border-0">
            <div className="text-sm text-ink-500 flex items-center gap-2">
                {icon && <i className={`fa-solid ${icon} w-4 text-ink-400`}></i>}
                {label}
            </div>
            <div className="text-sm font-medium text-ink-900 text-right max-w-[60%]">{value}</div>
        </div>
    );

    return (
        <div className="grid lg:grid-cols-3 gap-6">
            <div className="lg:col-span-2 space-y-4">
                <div className="bg-white rounded-2xl border border-ink-200 p-5">
                    <h3 className="font-bold text-ink-900 mb-2"><i className="fa-solid fa-route mr-2 text-ink-400"></i>Trajet</h3>
                    <Row icon="fa-circle-dot" label="Départ" value={booking.origin.label} />
                    <Row icon="fa-location-dot" label="Destination" value={booking.destination.label} />
                    <Row icon="fa-calendar" label="Date & heure" value={`${booking.date} à ${booking.time}`} />
                    <Row icon="fa-route" label="Distance / durée" value={`${route?.distance_text || ''} · ${route?.duration_text || ''}`} />
                    <Row icon="fa-user-group" label="Passagers / bagages" value={`${booking.passengers} pers. · ${booking.luggage} bagage(s)`} />
                </div>

                <div className="bg-white rounded-2xl border border-ink-200 p-5">
                    <h3 className="font-bold text-ink-900 mb-2"><i className="fa-solid fa-car mr-2 text-ink-400"></i>Véhicule</h3>
                    <Row label="Gamme" value={booking.range_label} />
                </div>

                <div className="bg-white rounded-2xl border border-ink-200 p-5">
                    <h3 className="font-bold text-ink-900 mb-2"><i className="fa-solid fa-user mr-2 text-ink-400"></i>Coordonnées</h3>
                    <Row label="Nom" value={`${booking.firstname} ${booking.lastname}`} />
                    <Row label="Email" value={booking.email} />
                    <Row label="Mobile" value={booking.mobile} />
                    {booking.flight_number && <Row label="N° vol" value={booking.flight_number} />}
                    {booking.company && <Row label="Société" value={booking.company} />}
                    {booking.notes && <Row label="Notes" value={booking.notes} />}
                </div>
            </div>

            <div className="space-y-3">
                <div className="bg-ink-900 text-white rounded-2xl p-5 sticky top-4">
                    <div className="text-xs uppercase tracking-wide text-ink-400">Total</div>
                    <div className="text-4xl font-extrabold mt-1">{booking.price}<span className="text-base font-medium text-ink-300"> CHF</span></div>
                    <div className="text-xs text-ink-400 mt-1">TTC · Mode : {booking.payment_method}</div>

                    {error && <div className="mt-3 bg-red-500/20 text-red-200 text-sm px-3 py-2 rounded-lg">{error}</div>}

                    <button className="mp-btn mp-btn-primary w-full !bg-amber-500 !text-ink-900 hover:!bg-amber-400 mt-4" disabled={submitting} onClick={onSubmit}>
                        {submitting ? <><span className="spinner"></span> Envoi…</> : <><i className="fa-solid fa-check"></i> Confirmer la réservation</>}
                    </button>
                    <button className="mp-btn w-full text-ink-300 hover:text-white mt-2" onClick={onBack} disabled={submitting}>
                        <i className="fa-solid fa-arrow-left"></i> Modifier
                    </button>
                </div>
            </div>
        </div>
    );
}

/* ─────────────────────────────────────────────────────────────────────────
   Écran succès
   ─────────────────────────────────────────────────────────────────────── */
function SuccessScreen({ result, central, onNew }) {
    return (
        <div className="max-w-xl mx-auto text-center py-10">
            <div className="w-20 h-20 rounded-full bg-green-100 text-green-600 flex items-center justify-center mx-auto mb-6">
                <i className="fa-solid fa-check text-4xl"></i>
            </div>
            <h2 className="text-3xl font-extrabold text-ink-900">Réservation confirmée</h2>
            <p className="text-ink-500 mt-2">Un email de confirmation vient de vous être envoyé.</p>

            <div className="bg-white border border-ink-200 rounded-2xl p-6 mt-6 text-left inline-block">
                <div className="text-xs text-ink-500 uppercase">Référence</div>
                <div className="text-2xl font-bold text-ink-900 font-mono">{result?.reference || result?.id || '—'}</div>
                {central?.phone_booking && (
                    <div className="text-sm text-ink-600 mt-3">
                        <i className="fa-solid fa-phone mr-2 text-ink-400"></i>
                        Une question ? <a href={`tel:${central.phone_booking}`} className="font-semibold text-ink-900">{central.phone_booking}</a>
                    </div>
                )}
            </div>

            <div className="mt-6">
                <button className="mp-btn mp-btn-ghost" onClick={onNew}>Nouvelle réservation</button>
            </div>
        </div>
    );
}

/* ─────────────────────────────────────────────────────────────────────────
   App
   ─────────────────────────────────────────────────────────────────────── */
function App() {
    const [config, setConfig] = useState(null);
    const [loadError, setLoadError] = useState('');
    const [step, setStep] = useState(1);
    const [route, setRoute] = useState(null);
    const [submitting, setSubmitting] = useState(false);
    const [submitError, setSubmitError] = useState('');
    const [result, setResult] = useState(null);

    const today = new Date(); today.setHours(today.getHours() + 2);
    const [booking, setBooking] = useState({
        origin: null, destination: null,
        date: today.toISOString().slice(0, 10),
        time: today.toTimeString().slice(0, 5),
        passengers: 2, luggage: 1,
        range_id: null, range_label: '', price: 0,
        firstname: '', lastname: '', email: '', mobile: '',
        flight_number: '', company: '', notes: '',
        payment_method: '',
    });

    useEffect(() => {
        if (!cfg.CENTRAL_KEY) {
            setLoadError('Aucune centrale identifiée. Ajoutez `?central=CODE` à l\'URL.');
            return;
        }
        api.config()
            .then(setConfig)
            .catch(e => setLoadError(e.message || 'Erreur de chargement'));
    }, []);

    // Brand color → CSS variables (utilisées par DatePicker/TimePicker entre autres)
    const brandColor = config?.website?.color || config?.central?.color || '#0f172a';
    useEffect(() => {
        const darken = (hex, amt = 0.85) => {
            if (!hex || !hex.startsWith('#') || hex.length !== 7) return hex;
            const n = parseInt(hex.slice(1), 16);
            const r = Math.max(0, Math.min(255, Math.round(((n >> 16) & 0xff) * amt)));
            const g = Math.max(0, Math.min(255, Math.round(((n >> 8)  & 0xff) * amt)));
            const b = Math.max(0, Math.min(255, Math.round( (n        & 0xff) * amt)));
            return '#' + ((r << 16) | (g << 8) | b).toString(16).padStart(6, '0');
        };
        document.documentElement.style.setProperty('--color-primary', brandColor);
        document.documentElement.style.setProperty('--color-primary-dark', darken(brandColor, 0.82));
    }, [brandColor]);

    const submit = async () => {
        setSubmitting(true); setSubmitError('');
        try {
            const payload = {
                origin_address: booking.origin.label,
                origin_lat: booking.origin.lat,
                origin_lng: booking.origin.lng,
                destination_address: booking.destination.label,
                destination_lat: booking.destination.lat,
                destination_lng: booking.destination.lng,
                pickup_date: booking.date,
                pickup_time: booking.time,
                passengers: booking.passengers,
                luggage: booking.luggage,
                range_id: booking.range_id,
                range_label: booking.range_label,
                price: booking.price,
                currency: 'CHF',
                distance_m: route?.distance_m || 0,
                duration_s: route?.duration_s || 0,
                customer: {
                    firstname: booking.firstname,
                    lastname: booking.lastname,
                    email: booking.email,
                    mobile: booking.mobile,
                },
                flight_number: booking.flight_number,
                company: booking.company,
                notes: booking.notes,
                payment_method: booking.payment_method,
                source: 'app.central',
            };
            const r = await api.createBooking(payload);
            setResult(r);
            setStep(5);
        } catch (e) {
            setSubmitError(e.message || 'Échec de la création');
        } finally {
            setSubmitting(false);
        }
    };

    const reset = () => {
        setStep(1); setRoute(null); setResult(null); setSubmitError('');
        setBooking({
            ...booking,
            origin: null, destination: null,
            range_id: null, range_label: '', price: 0,
            notes: '', flight_number: '',
        });
    };

    if (loadError) {
        return (
            <div className="min-h-screen flex items-center justify-center p-6">
                <div className="text-center max-w-md">
                    <div className="w-16 h-16 rounded-full bg-red-100 text-red-600 flex items-center justify-center mx-auto mb-4">
                        <i className="fa-solid fa-triangle-exclamation text-2xl"></i>
                    </div>
                    <h1 className="text-xl font-bold text-ink-900">Service indisponible</h1>
                    <p className="text-ink-500 mt-2">{loadError}</p>
                </div>
            </div>
        );
    }

    if (!config) {
        return (
            <div className="min-h-screen flex items-center justify-center">
                <div className="text-center">
                    <div className="inline-block w-12 h-12 border-4 border-ink-200 border-t-ink-900 rounded-full animate-spin"></div>
                    <p className="mt-4 text-ink-500 text-sm">Chargement de la centrale…</p>
                </div>
            </div>
        );
    }

    const { central, website, ranges } = config;

    return (
        <div className="min-h-screen flex flex-col">

            <header className="bg-white border-b border-ink-200 sticky top-0 z-20">
                <div className="max-w-6xl mx-auto px-4 py-3 flex items-center justify-between">
                    <div className="flex items-center gap-3">
                        {(website?.logo_url || central.logo_url) ? (
                            <img src={website?.logo_url || central.logo_url} alt="" className="h-9" />
                        ) : (
                            <div className="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold" style={{ background: brandColor }}>
                                {(website?.display_name || website?.company || central.label || 'M').charAt(0)}
                            </div>
                        )}
                        <div>
                            <div className="font-bold text-ink-900 leading-tight">{website?.display_name || website?.company || central.label}</div>
                            <div className="text-xs text-ink-500">Réservation en ligne</div>
                        </div>
                    </div>
                    {central.phone_booking && (
                        <a href={`tel:${central.phone_booking}`} className="hidden sm:flex items-center gap-2 text-sm text-ink-700 hover:text-ink-900">
                            <i className="fa-solid fa-phone text-ink-400"></i>
                            <span className="font-semibold">{central.phone_booking}</span>
                        </a>
                    )}
                </div>
            </header>

            <main className="flex-1 max-w-6xl mx-auto w-full px-4 py-6">
                {step <= 4 && (
                    <div className="mb-6">
                        <Stepper step={step} />
                    </div>
                )}

                {step === 1 && (
                    <StepRoute
                        booking={booking} setBooking={setBooking}
                        route={route} setRoute={setRoute}
                        central={central}
                        onNext={() => setStep(2)}
                    />
                )}
                {step === 2 && (
                    <StepVehicle
                        booking={booking} setBooking={setBooking}
                        ranges={ranges} route={route}
                        onBack={() => setStep(1)} onNext={() => setStep(3)}
                    />
                )}
                {step === 3 && (
                    <StepCustomer
                        booking={booking} setBooking={setBooking}
                        central={central}
                        onBack={() => setStep(2)} onNext={() => setStep(4)}
                    />
                )}
                {step === 4 && (
                    <StepConfirm
                        booking={booking} central={central} route={route}
                        onBack={() => setStep(3)}
                        onSubmit={submit} submitting={submitting} error={submitError}
                    />
                )}
                {step === 5 && (
                    <SuccessScreen result={result} central={central} onNew={reset} />
                )}
            </main>

            <footer className="border-t border-ink-200 bg-white">
                <div className="max-w-6xl mx-auto px-4 py-4 text-xs text-ink-500 flex flex-col sm:flex-row items-center justify-between gap-2">
                    <div>© {new Date().getFullYear()} {website?.company_name || central.label} · Propulsé par mypro.ch</div>
                    <div className="flex items-center gap-3">
                        {central.email_management && <a href={`mailto:${central.email_management}`} className="hover:text-ink-900">{central.email_management}</a>}
                    </div>
                </div>
            </footer>
        </div>
    );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
