// BookingSection — réservation native : coordonnées → naissance → créneau → paiement → confirmation
// Le choix du créneau interroge le Worker (/api/slots) qui lit le Google Agenda de Mélanie.

// Lien Stripe de secours si l'API /api/hold n'est pas joignable (ex. aperçu purement statique).
const STRIPE_FALLBACK = 'https://buy.stripe.com/7sY14ofZJddzcTP26d3sI0d';
// Lien visio affiché à la cliente sur l'écran de confirmation (après paiement réussi).
const ZOOM_LINK = 'https://us06web.zoom.us/j/85205837130?pwd=raqFxykH85Xul8meXbtmTIJvkLDg7V.1';

const VIEWER_TZ = Intl.DateTimeFormat().resolvedOptions().timeZone;
const pad2 = (n) => String(n).padStart(2, '0');

// "14h30" — format français 24h, sans AM/PM
function frTime(iso) {
  const parts = new Intl.DateTimeFormat('fr-FR', { timeZone: VIEWER_TZ, hour: 'numeric', minute: '2-digit', hour12: false }).formatToParts(new Date(iso));
  const h = parts.find(p => p.type === 'hour').value;
  const m = parts.find(p => p.type === 'minute').value;
  return `${h}h${m}`;
}
// "2026-06-23" dans le fuseau du visiteur
function dateKeyOf(iso) {
  return new Intl.DateTimeFormat('en-CA', { timeZone: VIEWER_TZ, year: 'numeric', month: '2-digit', day: '2-digit' }).format(new Date(iso));
}
// "vendredi 19 juin à 14h30"
function frSlotLabel(iso) {
  if (!iso) return '—';
  const date = new Date(iso).toLocaleDateString('fr-FR', { timeZone: VIEWER_TZ, weekday: 'long', day: 'numeric', month: 'long' });
  return `${date} à ${frTime(iso)}`;
}

const DOW = ['lun', 'mar', 'mer', 'jeu', 'ven', 'sam', 'dim'];

// Sélecteur de créneau : calendrier mensuel + créneaux horaires du jour choisi
function SlotPicker({ value, onPick }) {
  const [state, setState] = React.useState({ loading: true, error: null, slots: [] });
  const [month, setMonth] = React.useState(null);   // { year, m }  (m: 0-11)
  const [selDate, setSelDate] = React.useState(null); // "YYYY-MM-DD"

  React.useEffect(() => {
    let alive = true;
    fetch('/api/slots')
      .then(r => r.json())
      .then(d => { if (alive) setState({ loading: false, error: d.error || null, slots: d.slots || [] }); })
      .catch(() => { if (alive) setState({ loading: false, error: 'network', slots: [] }); });
    return () => { alive = false; };
  }, []);

  // Créneaux regroupés par jour (fuseau du visiteur)
  const avail = React.useMemo(() => {
    const map = new Map();
    for (const iso of state.slots) {
      const key = dateKeyOf(iso);
      if (!map.has(key)) map.set(key, []);
      map.get(key).push(iso);
    }
    return map;
  }, [state.slots]);

  const keys = React.useMemo(() => [...avail.keys()].sort(), [avail]);

  React.useEffect(() => {
    if (keys.length && !month) {
      const [y, mo] = keys[0].split('-').map(Number);
      setMonth({ year: y, m: mo - 1 });
      setSelDate(keys[0]);
    }
  }, [keys, month]);

  if (state.loading) return <div className="mi-slots__msg">Chargement des créneaux disponibles…</div>;
  if (state.error || keys.length === 0) {
    return (
      <div className="mi-slots__msg">
        Aucun créneau disponible pour le moment. Écris-moi à <a href="mailto:melaniecoachbusiness@gmail.com">melaniecoachbusiness@gmail.com</a> et on trouve un moment ensemble.
      </div>
    );
  }
  if (!month) return null;

  const { year, m } = month;
  const first = new Date(year, m, 1);
  const offset = (first.getDay() + 6) % 7;       // décalage lundi=0
  const daysIn = new Date(year, m + 1, 0).getDate();
  const cells = [];
  for (let i = 0; i < offset; i++) cells.push(null);
  for (let d = 1; d <= daysIn; d++) cells.push(d);

  const firstIdx = (() => { const [y, mo] = keys[0].split('-').map(Number); return y * 12 + (mo - 1); })();
  const lastIdx = (() => { const [y, mo] = keys[keys.length - 1].split('-').map(Number); return y * 12 + (mo - 1); })();
  const curIdx = year * 12 + m;
  const canPrev = curIdx > firstIdx;
  const canNext = curIdx < lastIdx;

  const monthLabel = first.toLocaleDateString('fr-FR', { month: 'long', year: 'numeric' });
  const times = (selDate && avail.get(selDate)) || [];
  const selDateObj = selDate ? (() => { const [y, mo, d] = selDate.split('-').map(Number); return new Date(y, mo - 1, d, 12); })() : null;

  const go = (delta) => setMonth(({ year, m }) => {
    const idx = year * 12 + m + delta;
    return { year: Math.floor(idx / 12), m: ((idx % 12) + 12) % 12 };
  });

  return (
    <div className="mi-slots">
      <div className="mi-cal">
        <div className="mi-cal__head">
          <button type="button" className="mi-cal__nav" onClick={() => go(-1)} disabled={!canPrev} aria-label="Mois précédent">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"/></svg>
          </button>
          <span className="mi-cal__title">{monthLabel}</span>
          <button type="button" className="mi-cal__nav" onClick={() => go(1)} disabled={!canNext} aria-label="Mois suivant">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
          </button>
        </div>
        <div className="mi-cal__grid">
          {DOW.map(d => <div key={d} className="mi-cal__dow">{d}</div>)}
          {cells.map((d, i) => {
            if (d === null) return <span key={`e${i}`} className="mi-cal__cell is-empty" />;
            const key = `${year}-${pad2(m + 1)}-${pad2(d)}`;
            const isAvail = avail.has(key);
            const isSel = key === selDate;
            return (
              <button
                key={key}
                type="button"
                className={`mi-cal__cell ${isAvail ? 'is-available' : ''} ${isSel ? 'is-selected' : ''}`}
                disabled={!isAvail}
                onClick={() => setSelDate(key)}
              >{d}</button>
            );
          })}
        </div>
      </div>

      {selDateObj && (
        <div className="mi-cal__times-head">
          Créneaux du {selDateObj.toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'long' })}
        </div>
      )}
      <div className="mi-slots__times">
        {times.map(iso => (
          <button
            key={iso}
            type="button"
            className={`mi-slots__time ${value === iso ? 'is-active' : ''}`}
            onClick={() => onPick(iso)}
          >{frTime(iso)}</button>
        ))}
      </div>
      <p className="mi-slots__tz">Heures affichées dans ton fuseau ({VIEWER_TZ.replace('_', ' ')}). Appel connexion · 15 min · visio Zoom.</p>
    </div>
  );
}

// Autocomplétion de ville (API de géocodage Open-Meteo, gratuite, sans clé)
function CityAutocomplete({ value, onChange }) {
  const [results, setResults] = React.useState([]);
  const [open, setOpen] = React.useState(false);
  const [active, setActive] = React.useState(-1);
  const timer = React.useRef(null);

  const search = (q) => {
    if (timer.current) clearTimeout(timer.current);
    if (!q || q.trim().length < 2) { setResults([]); setOpen(false); return; }
    timer.current = setTimeout(async () => {
      try {
        const r = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(q)}&count=6&language=fr&format=json`);
        const d = await r.json();
        const list = (d.results || []).map(x => ({
          label: [x.name, x.admin1, x.country].filter(Boolean).join(', '),
        }));
        setResults(list); setOpen(list.length > 0); setActive(-1);
      } catch (e) { setResults([]); setOpen(false); }
    }, 250);
  };

  const choose = (item) => { onChange(item.label); setOpen(false); setResults([]); };

  const onKey = (e) => {
    if (!open) return;
    if (e.key === 'ArrowDown') { e.preventDefault(); setActive(a => Math.min(a + 1, results.length - 1)); }
    else if (e.key === 'ArrowUp') { e.preventDefault(); setActive(a => Math.max(a - 1, 0)); }
    else if (e.key === 'Enter' && active >= 0 && results[active]) { e.preventDefault(); choose(results[active]); }
    else if (e.key === 'Escape') setOpen(false);
  };

  return (
    <div className="mi-city">
      <input
        value={value}
        onChange={e => { onChange(e.target.value); search(e.target.value); }}
        onFocus={() => { if (results.length) setOpen(true); }}
        onBlur={() => setTimeout(() => setOpen(false), 150)}
        onKeyDown={onKey}
        placeholder="Commence à taper ta ville…"
        autoComplete="off"
      />
      {open && (
        <div className="mi-city__menu" onMouseDown={e => e.preventDefault()}>
          {results.map((it, i) => (
            <div
              key={i}
              className={`mi-city__item ${i === active ? 'is-active' : ''}`}
              onClick={() => choose(it)}
            >{it.label}</div>
          ))}
        </div>
      )}
    </div>
  );
}

function BookingSection() {
  const [step, setStep] = React.useState(0);
  const [submitting, setSubmitting] = React.useState(false);
  const [confirmState, setConfirmState] = React.useState('idle'); // idle | checking | confirmed | pending
  const [data, setData] = React.useState({
    slot: null,
    firstName: '', lastName: '', email: '', phone: '',
    birthDate: '', birthTime: '', birthPlace: '',
  });

  const set = (k, v) => setData(d => ({ ...d, [k]: v }));

  // Retour depuis Stripe après paiement : on vérifie le vrai statut de la réservation
  React.useEffect(() => {
    let params;
    try { params = new URLSearchParams(window.location.search); } catch (e) { return; }
    if (params.get('paid') !== '1') return;
    let saved = null;
    try { saved = JSON.parse(localStorage.getItem('lc_booking') || 'null'); } catch (e) {}
    if (!saved || !saved.id || (Date.now() - saved.t > 2 * 3600 * 1000)) return;
    setData(d => ({ ...d, firstName: saved.firstName || '', slot: saved.slot || null }));
    setStep(4);
    setConfirmState('checking');
    const el = document.getElementById('booking');
    if (el) el.scrollIntoView();
    let tries = 0;
    const poll = async () => {
      tries++;
      try {
        const b = await (await fetch('/api/booking/' + saved.id)).json();
        if (b && b.status === 'confirmed') {
          setConfirmState('confirmed');
          try { localStorage.removeItem('lc_booking'); } catch (e) {}
          return;
        }
      } catch (e) {}
      if (tries < 8) setTimeout(poll, 2000);
      else setConfirmState('pending');
    };
    poll();
  }, []);

  const sentLeadRef = React.useRef(false);
  const sentInfosRef = React.useRef(false);

  const submitLead = () => {
    if (sentLeadRef.current) return;
    sentLeadRef.current = true;
    try {
      fetch('https://formspree.io/f/mpqenryk', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
        body: JSON.stringify({
          'Type': 'Lead (coordonnées seules)',
          'Prénom': data.firstName, 'Nom': data.lastName,
          'Email': data.email, 'Téléphone': data.phone,
          _replyto: data.email,
          _subject: 'Nouveau lead (coordonnées) — ' + data.firstName + ' ' + data.lastName,
        }),
      }).catch(() => {});
    } catch (e) { /* ne bloque jamais l'utilisateur */ }
  };

  const submitInfos = () => {
    if (sentInfosRef.current) return;
    sentInfosRef.current = true;
    try {
      fetch('https://formspree.io/f/mpqenryk', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
        body: JSON.stringify({
          'Type': 'Réservation (infos complètes)',
          'Prénom': data.firstName, 'Nom': data.lastName,
          'Email': data.email, 'Téléphone': data.phone,
          'Date de naissance': data.birthDate, 'Heure de naissance': data.birthTime,
          'Lieu de naissance': data.birthPlace,
          _replyto: data.email,
          _subject: 'Réservation — ' + data.firstName + ' ' + data.lastName,
        }),
      }).catch(() => {});
    } catch (e) { /* ne bloque jamais l'utilisateur */ }
  };

  const canNext = [
    data.firstName.trim() && data.lastName.trim() && data.email.includes('@'),
    data.birthDate && data.birthTime && data.birthPlace.trim(),
    !!data.slot,
    true,
  ][step];

  const stepTitles = ['Tes coordonnées', 'Tes infos de naissance', 'Choisis ton créneau', 'Paiement'];

  const next = () => setStep(s => Math.min(s + 1, 4));
  const back = () => setStep(s => Math.max(s - 1, 0));

  // Étape paiement : enregistre la réservation en attente puis redirige vers Stripe (même onglet).
  const goToPayment = async () => {
    setSubmitting(true);
    let payUrl = '', id = '';
    try {
      const r = await fetch('/api/hold', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ ...data, tz: VIEWER_TZ }),
      });
      const d = await r.json();
      payUrl = d.payUrl || ''; id = d.id || '';
    } catch (e) { /* fallback ci-dessous */ }
    if (id) {
      try { localStorage.setItem('lc_booking', JSON.stringify({ id, slot: data.slot, firstName: data.firstName, t: Date.now() })); } catch (e) {}
    }
    if (!payUrl) payUrl = STRIPE_FALLBACK;
    window.location.href = payUrl;
  };

  const resetForm = () => {
    setStep(0); setConfirmState('idle');
    setData({ slot: null, firstName: '', lastName: '', email: '', phone: '', birthDate: '', birthTime: '', birthPlace: '' });
    sentLeadRef.current = false; sentInfosRef.current = false;
    try { window.history.replaceState({}, '', window.location.pathname); } catch (e) {}
  };

  return (
    <section id="booking" className="mi-booking">
      <div className="mi-booking__inner">
        <div className="mi-booking__head">
          <h2>Réserver ta lecture <em>ici</em></h2>
          <p>Follow les étapes juste en dessous pour finaliser ta réservation.</p>
        </div>

        <div className="mi-booking__form">
          {step < 4 && (
            <div className="mi-steps">
              {[0,1,2,3].map(i => (
                <div key={i} className={`mi-steps__dot ${i < step ? 'is-done' : ''} ${i === step ? 'is-active' : ''}`} />
              ))}
            </div>
          )}

          {/* STEP 0 — contact */}
          {step === 0 && (
            <div className="mi-step-panel">
              <h3 className="mi-step-panel__title">{stepTitles[0]}</h3>
              <p className="mi-step-panel__sub">Pour que je puisse t'envoyer la confirmation et les détails de ton rendez-vous.</p>
              <div className="mi-grid-2">
                <div className="mi-field"><label>Prénom</label><input value={data.firstName} onChange={e => set('firstName', e.target.value)} /></div>
                <div className="mi-field"><label>Nom</label><input value={data.lastName} onChange={e => set('lastName', e.target.value)} /></div>
              </div>
              <div className="mi-field"><label>Email</label><input type="email" value={data.email} onChange={e => set('email', e.target.value)} /></div>
              <div className="mi-field"><label>Téléphone</label><input type="tel" value={data.phone} onChange={e => set('phone', e.target.value)} /></div>
            </div>
          )}

          {/* STEP 1 — birth info */}
          {step === 1 && (
            <div className="mi-step-panel">
              <h3 className="mi-step-panel__title">{stepTitles[1]}</h3>
              <p className="mi-step-panel__sub">La date, l'heure et le lieu sont essentiels pour préparer ta lecture. Si tu ne connais pas ton heure exactement, indique au plus proche.</p>
              <div className="mi-grid-2">
                <div className="mi-field"><label>Date de naissance</label><input type="date" value={data.birthDate} onChange={e => set('birthDate', e.target.value)} /></div>
                <div className="mi-field"><label>Heure de naissance</label><input type="time" value={data.birthTime} onChange={e => set('birthTime', e.target.value)} /></div>
              </div>
              <div className="mi-field"><label>Lieu de naissance (ville, pays)</label><CityAutocomplete value={data.birthPlace} onChange={v => set('birthPlace', v)} /></div>
            </div>
          )}

          {/* STEP 2 — créneau (natif) */}
          {step === 2 && (
            <div className="mi-step-panel">
              <h3 className="mi-step-panel__title">{stepTitles[2]}</h3>
              <p className="mi-step-panel__sub">Choisis le créneau de ton <strong>appel connexion (15 min)</strong>. <strong style={{ color: 'var(--tsc-gold-700)', fontWeight: 600 }}>Le paiement se fait à l'étape suivante pour finaliser ta réservation.</strong></p>
              <SlotPicker value={data.slot} onPick={(iso) => set('slot', iso)} />
              {data.slot && (
                <div style={{ marginTop: 18, padding: '16px 20px', borderRadius: 12, background: 'var(--tsc-gold-100)', border: '1px solid var(--tsc-gold-300)', display: 'flex', alignItems: 'center', gap: 12 }}>
                  <span style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 32, height: 32, borderRadius: 999, background: 'var(--tsc-gold)', color: 'var(--tsc-cream-100)', flexShrink: 0 }}>
                    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                  </span>
                  <span style={{ fontFamily: 'var(--font-display)', fontSize: 20, lineHeight: 1.25, color: 'var(--tsc-gold-800)' }}>{frSlotLabel(data.slot)} — continue vers le paiement&nbsp;!</span>
                </div>
              )}
            </div>
          )}

          {/* STEP 3 — payment */}
          {step === 3 && (
            <div className="mi-step-panel">
              <h3 className="mi-step-panel__title">{stepTitles[3]}</h3>
              <p className="mi-step-panel__sub">Dernière étape — le paiement se fait sur une page sécurisée Stripe. Ta réservation est confirmée une fois le paiement effectué.</p>

              <div className="mi-summary">
                <div className="mi-summary__row"><span className="lbl">Appel connexion</span><span className="val">{frSlotLabel(data.slot)}</span></div>
                <div className="mi-summary__row"><span className="lbl">Pour</span><span className="val">{data.firstName} {data.lastName}</span></div>
                <div className="mi-summary__row"><span className="lbl">Inclus</span><span className="val">Connexion 15 min + Lecture 90 min</span></div>
                <div className="mi-summary__total">
                  <span className="lbl">À régler</span>
                  <span><del>177€</del><span className="val">47 €</span></span>
                </div>
              </div>

              <div className="mi-pm" style={{ cursor: 'default', alignItems: 'center' }}>
                <span style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 36, height: 36, borderRadius: 10, background: 'var(--tsc-gold-100)', color: 'var(--tsc-gold-700)', flexShrink: 0 }}>
                  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="1" y="4" width="22" height="16" rx="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg>
                </span>
                <span>
                  <div className="mi-pm__name">Paiement sécurisé par Stripe</div>
                  <div className="mi-pm__sub">Carte bancaire · tu seras redirigée vers la page de paiement, puis revenue ici.</div>
                </span>
              </div>
            </div>
          )}

          {/* STEP 4 — confirmation (après retour de Stripe) */}
          {step === 4 && (
            <div className="mi-confirm">
              {confirmState === 'checking' && (
                <>
                  <div className="mi-confirm__icon mi-confirm__icon--wait">
                    <svg width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 14"/></svg>
                  </div>
                  <h3>Paiement reçu, {data.firstName || 'belle âme'} !</h3>
                  <p>On prépare ton rendez-vous… un petit instant. 🌙</p>
                </>
              )}
              {confirmState === 'confirmed' && (
                <>
                  <div className="mi-confirm__icon">
                    <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                  </div>
                  <h3>C'est confirmé, {data.firstName || 'belle âme'}.</h3>
                  <p>Ton appel connexion est réservé{data.slot ? ' — ' + frSlotLabel(data.slot) : ''}. On se retrouve très vite — prends bien soin de toi d'ici là. 🌞</p>
                  <div className="mi-confirm__zoom">
                    <span>Ton lien visio (garde-le précieusement) :</span>
                    <a href={ZOOM_LINK} target="_blank" rel="noopener">{ZOOM_LINK}</a>
                  </div>
                  <button className="mi-btn mi-btn--secondary" onClick={resetForm}>Réserver une autre lecture</button>
                </>
              )}
              {(confirmState === 'pending' || confirmState === 'idle') && (
                <>
                  <div className="mi-confirm__icon">
                    <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
                  </div>
                  <h3>Merci, {data.firstName || 'belle âme'} !</h3>
                  <p>Ton paiement est bien pris en compte. Ta réservation se confirme dans la minute et tu recevras un email avec tous les détails de ton appel connexion. 🌞</p>
                  <button className="mi-btn mi-btn--secondary" onClick={resetForm}>Réserver une autre lecture</button>
                </>
              )}
            </div>
          )}

          {step < 4 && (
            <div className="mi-actions">
              {step > 0 && (
                <button type="button" className="mi-btn mi-btn--secondary" onClick={back}>← Retour</button>
              )}
              <div className="mi-actions__right">
                <button
                  type="button"
                  className="mi-btn mi-btn--primary"
                  disabled={!canNext || submitting}
                  onClick={
                    step === 3 ? goToPayment
                    : step === 0 ? () => { submitLead(); next(); }
                    : step === 1 ? () => { submitInfos(); next(); }
                    : next
                  }
                >
                  {step === 3 ? (submitting ? 'Redirection…' : 'Payer 47 € · Stripe →') : 'Continuer →'}
                </button>
              </div>
            </div>
          )}
        </div>
      </div>
    </section>
  );
}

Object.assign(window, { BookingSection });
