/* eslint-disable */
const { useState, useEffect, useLayoutEffect, useRef, useMemo } = React;

const PARAMS = new URLSearchParams(location.search);
const TOKEN = (PARAMS.get('t') || PARAMS.get('token') || '').trim().toLowerCase();
const DEBUG = PARAMS.get('debug') === 'true';

const ALL_EVENTS = [
{
  id: 'tefilines',
  title: 'Mise des Téfilines',
  short: 'Téfilines',
  day: 'Jeudi',
  date: '30 juillet 2026',
  time: '8h30',
  place: 'Yaguel Yaacov',
  addr: '90 rue Gabriel Péri, Montrouge'
},
{
  id: 'shabbat',
  title: 'Shabbat',
  short: 'Shabbat',
  day: 'Vendredi',
  date: '31 juillet 2026',
  time: 'à partir de 16h',
  place: 'Domain du Moulin XII',
  addr: '1 chemin de l’Église, 28500 Sainte-Gemme-Moronval'
},
{
  id: 'brunch',
  title: 'Brunch Royal',
  short: 'Brunch',
  day: 'Dimanche',
  date: '2 août 2026',
  time: 'à partir de 11h',
  place: '',
  addr: ''
}];

const EVENTS_BY_LIST = {
  shabbat: ['tefilines', 'shabbat', 'brunch'],
  brunch:  ['tefilines', 'brunch'],
};

function eventsForList(list) {
  const ids = EVENTS_BY_LIST[list] || [];
  return ALL_EVENTS.filter(e => ids.includes(e.id));
}

function imageForList(list) {
  return list === 'brunch'
    ? 'emmett-invitation-brunch.png'
    : 'emmett-invitation-shabbat.png';
}

/* ------------------------------------------------------------------ */
/* APP                                                                 */
/* ------------------------------------------------------------------ */

function App() {
  const [loading, setLoading] = useState(true);
  const [loadError, setLoadError] = useState(null);
  const [invitee, setInvitee] = useState(null); // { token, name, list }
  const [rsvps, setRsvps] = useState({});

  const [stage, setStage] = useState('envelope');
  const [selected, setSelected] = useState(null);
  const [lastReply, setLastReply] = useState(null);

  /* The "flying" invitation lives at App root; GSAP pins it to whichever
     slot is active.  During envelope + early opening it stays opacity 0
     (the in-envelope <img> is what the viewer sees).  Halfway through
     the opening sequence we cross-fade to this one and fly it home. */
  const invitationRef = useRef(null);
  const cardSlotRef = useRef(null);
  const envLetterRef = useRef(null);
  const envWrapRef = useRef(null);
  const audioRef = useRef(null);
  const musicStartedRef = useRef(false);

  /* Browsers block autoplay until the page receives a user gesture.
     We try on mount anyway (works in PWAs, after back-nav, or when the
     origin already has audio permission). If blocked, the first user
     gesture anywhere on the page (or the envelope click specifically)
     starts it. */
  function startMusic() {
    const a = audioRef.current;
    if (!a || musicStartedRef.current) return;
    a.volume = 0.55;
    const p = a.play();
    if (p && typeof p.then === 'function') {
      p.then(() => { musicStartedRef.current = true; }).catch(() => {/* will retry on gesture */});
    } else {
      musicStartedRef.current = true;
    }
  }
  useEffect(() => {
    startMusic();
    const onGesture = () => {
      startMusic();
      if (musicStartedRef.current) {
        document.removeEventListener('pointerdown', onGesture, true);
        document.removeEventListener('keydown', onGesture, true);
      }
    };
    document.addEventListener('pointerdown', onGesture, true);
    document.addEventListener('keydown', onGesture, true);
    return () => {
      document.removeEventListener('pointerdown', onGesture, true);
      document.removeEventListener('keydown', onGesture, true);
    };
  }, []);

  useEffect(() => {
    if (!TOKEN) {
      setLoadError('missing');
      setLoading(false);
      return;
    }
    let cancelled = false;
    fetch(`/api/invitee?t=${encodeURIComponent(TOKEN)}`)
      .then(async (r) => {
        if (!r.ok) throw new Error(r.status === 404 ? 'not_found' : 'fetch_failed');
        return r.json();
      })
      .then((data) => {
        if (cancelled) return;
        setInvitee({ token: data.token, name: data.name, list: data.list });
        setRsvps(data.rsvps || {});
        setLoading(false);
      })
      .catch((err) => {
        if (cancelled) return;
        setLoadError(err.message === 'not_found' ? 'not_found' : 'fetch_failed');
        setLoading(false);
      });
    return () => { cancelled = true; };
  }, []);

  function openEnvelope() {
    if (stage !== 'envelope') return;
    startMusic();
    setStage('opening');
  }
  function finishOpening() { setStage('card'); }
  function chooseEvent(ev) { setSelected(ev); setStage('form'); }
  async function submitRsvp(payload) {
    const res = await fetch('/api/rsvp', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({
        token: invitee.token,
        eventId: selected.id,
        attending: payload.attending,
        adults: payload.adults,
        children: payload.children,
        message: payload.message,
      }),
    });
    if (!res.ok) {
      const data = await res.json().catch(() => ({}));
      throw new Error(data.error || 'submit_failed');
    }
    const stored = {
      attending: payload.attending,
      adults: payload.attending ? payload.adults : 0,
      children: payload.attending ? payload.children : 0,
      message: payload.message,
      updatedAt: Date.now(),
    };
    setRsvps((prev) => ({ ...prev, [selected.id]: stored }));
    setLastReply({ event: selected, ...stored });
    setStage('thanks');
  }
  function backToCard() { setSelected(null); setStage('card'); }

  /* Stage-driven positioning for the App-root invitation image:
       - 'envelope'             : CSS keeps it width:0 / opacity:0; no work
       - 'opening'              : the GSAP timeline in EnvelopeScene
                                  takes over and flies it to the card slot
       - 'card'                 : pinned to .card-frame, re-pinned on resize
       - 'form' / 'thanks'      : faded out so it doesn't bleed through

     position: absolute is relative to .page, so we feed GSAP
     document-relative coords (rect + scrollY/scrollX) — the image
     then scrolls with the page once it's pinned. */
  useLayoutEffect(() => {
    const img = invitationRef.current;
    if (!img) return;

    if (stage === 'form' || stage === 'thanks') {
      gsap.killTweensOf(img);
      gsap.set(img, { opacity: 0 });
      return;
    }

    if (stage !== 'card') return;  /* envelope / opening : leave alone */

    function placeAtCardSlot() {
      const slot = cardSlotRef.current;
      if (!slot) return;
      const r = slot.getBoundingClientRect();
      if (r.width < 10 || r.height < 10) {
        requestAnimationFrame(placeAtCardSlot);
        return;
      }
      gsap.set(img, {
        x: r.left + window.scrollX,
        y: r.top + window.scrollY,
        width: r.width,
        height: r.height,
        opacity: 1
      });
    }
    placeAtCardSlot();
    window.addEventListener('resize', placeAtCardSlot);
    return () => window.removeEventListener('resize', placeAtCardSlot);
  }, [stage]);

  /* Audio sits at position 0 of the Fragment in every render path,
     so React keeps the SAME audio DOM node mounted from first render
     through every stage change. Unmount/remount kills play state. */
  const audioEl = (
    <audio
      key="bgm"
      ref={audioRef}
      src="musique.mp3"
      loop
      preload="auto"
      playsInline />
  );

  let content;
  if (loading) {
    content = (
      <div className="page page-loading">
        <div className="loading-card">
          <div className="loading-spark">✦</div>
          <div className="loading-text">Préparation de votre invitation…</div>
        </div>
      </div>);
  } else if (loadError || !invitee) {
    content = <ErrorScene kind={loadError} />;
  } else {
    content = renderMain();
  }

  function renderMain() {
    const guestName = invitee.name;
    const events = eventsForList(invitee.list);
    const imgSrc = imageForList(invitee.list);
    const cardVisible = stage === 'card';
    const showForm = stage === 'form';
    const showThanks = stage === 'thanks';

    return (
    <div className="page">
      <img
        ref={invitationRef}
        className="invitation-img"
        src={imgSrc}
        alt="Invitation Bar Mitzvah Emmett Moshé"
        draggable="false" />

      <div className={"card-layer " + (cardVisible ? "is-visible " : "") + (showForm || showThanks ? "is-hidden" : "")}>
        <CardScene name={guestName} events={events} rsvps={rsvps} onPick={chooseEvent} cardSlotRef={cardSlotRef} />
      </div>

      {(stage === 'envelope' || stage === 'opening') &&
      <div className="env-layer">
          <EnvelopeScene
            name={guestName}
            imgSrc={imgSrc}
            opening={stage === 'opening'}
            onOpen={openEnvelope}
            onComplete={finishOpening}
            envLetterRef={envLetterRef}
            envWrapRef={envWrapRef}
            cardSlotRef={cardSlotRef}
            invitationRef={invitationRef} />
        </div>
      }

      {showForm &&
      <FormScene name={guestName} event={selected} existing={rsvps[selected.id]} onCancel={backToCard} onSubmit={submitRsvp} />
      }
      {showThanks && lastReply &&
      <ThanksScene name={guestName} events={events} reply={lastReply} rsvps={rsvps} onBack={backToCard} />
      }
    </div>);
  }

  return (<>{audioEl}{content}</>);
}

function ErrorScene({ kind }) {
  const msg = kind === 'not_found'
    ? "Nous ne reconnaissons pas ce lien. Vérifiez le lien que vous avez reçu, ou contactez la famille Jochimek."
    : kind === 'missing'
    ? "Ce lien semble incomplet. Veuillez utiliser le lien d'invitation que vous avez reçu."
    : "Une erreur est survenue. Merci de réessayer dans un instant.";
  return (
    <div className="page page-loading">
      <div className="loading-card">
        <div className="loading-spark">✦</div>
        <h2 style={{margin: '0 0 8px', fontWeight: 500}}>Invitation introuvable</h2>
        <div className="loading-text">{msg}</div>
      </div>
    </div>);
}

/* ==================================================================
   MOLECULE — FlipCard
   A generic 3D flip card.  Renders two faces and rotates 180° around
   Y when `flipped` is true.  Knows nothing about envelopes; the
   caller passes whatever JSX it wants for the front and back faces.
   Atomic design: this is a "molecule" — a reusable composite of the
   flip-wrap / flip-inner / flip-face atoms.
   ================================================================== */
function FlipCard({ flipped, onClick, disabled, ariaLabel, front, back, wrapRef }) {
  return (
    <div
      className={"flip-wrap " + (flipped ? "is-opening" : "")}
      ref={wrapRef}>
      <button
        className="flip-inner"
        onClick={onClick}
        aria-label={ariaLabel}
        disabled={disabled}>
        <div className="flip-face flip-face-front" aria-hidden="true">{front}</div>
        <div className="flip-face flip-face-back"  aria-hidden="true">{back}</div>
      </button>
    </div>);
}

/* Small helper for the addressed front face content. */
function AddressedFront({ name }) {
  return (
    <React.Fragment>
      <div className="flip-front-stamp">
        <div className="flip-front-stamp-inner">
          <div>BAR</div>
          <div>MITZVAH</div>
          <div className="dot">✦</div>
          <div>2026</div>
        </div>
      </div>
      <div className="flip-front-to-label">à</div>
      <div className="flip-front-to-name">{name}</div>
      <div className="flip-front-rule"></div>
      <div className="flip-front-tagline">Une invitation vous attend</div>
    </React.Fragment>);
}

/* ==================================================================
   MOLECULE — Envelope
   A self-contained sealed 4-flap envelope (CodePen pattern).  Drop
   it into any positioned container of any aspect and it fills it.
   The caller passes refs so an external timeline can animate the
   card / top flap / seal / shadow.  Knows nothing about flips.
   ================================================================== */
function Envelope({ cardRef, topRef, sealRef, shadowRef, imgSrc, seal }) {
  return (
    <div className="envelope">
      <div className="env-card" ref={cardRef}>
        <img className="env-card-img" src={imgSrc} alt="" draggable="false" />
      </div>
      <div className="env-right"></div>
      <div className="env-bottom"></div>
      <div className="env-left"></div>
      <div className="env-seam"></div>
      <div className="env-top" ref={topRef}></div>
      <div className="env-seal" ref={sealRef}>
        <div className="env-seal-inner"><span>{seal}</span></div>
      </div>
      <div className="env-shadow" ref={shadowRef} aria-hidden="true"></div>
    </div>);
}

/* ==================================================================
   ORGANISM — EnvelopeScene
   Composes <FlipCard/> with <Envelope/> on its back face, owns the
   phase state, drives the GSAP opening timeline, exposes debug
   prev/next controls.
   ================================================================== */
function EnvelopeScene({ name, imgSrc, opening, onOpen, onComplete, envLetterRef, envWrapRef, cardSlotRef, invitationRef }) {
  const hintRef = useRef(null);
  const topRef = useRef(null);
  const sealRef = useRef(null);
  const shadowRef = useRef(null);
  const tlRef = useRef(null);

  /* Debug phase machine (5 phases, each independently triggerable).
       0 = idle, front face visible
       1 = flipped, sealed envelope visible
       2 = envelope opens: seal cracks + top flap rotates
       3 = invitation slides up out of the envelope
       4 = invitation scales/flies to the card-frame slot

     "Next" plays the ONE animation that transitions from prev → next.
     "Previous" instantly snaps state back to the target phase. */
  const [phase, setPhase] = useState(0);
  const prevPhaseRef = useRef(0);

  /* Reset every animated element to its CSS baseline. */
  function resetAnimatedState() {
    const card = envLetterRef.current;
    const top = topRef.current;
    const seal = sealRef.current;
    const shadow = shadowRef.current;
    const img = invitationRef.current;
    const wrap = envWrapRef.current;
    if (tlRef.current) { tlRef.current.kill(); tlRef.current = null; }
    if (card)   gsap.set(card,   { clearProps: 'all' });
    if (top)    gsap.set(top,    { clearProps: 'all' });
    if (seal)   gsap.set(seal,   { clearProps: 'all' });
    if (shadow) gsap.set(shadow, { clearProps: 'all' });
    if (img) {
      gsap.killTweensOf(img);
      gsap.set(img, { opacity: 0, width: 0, height: 0, x: 0, y: 0 });
    }
    if (wrap) wrap.classList.remove('is-leaving');
  }

  /* Instantly snap every element to the cumulative end state of phase p.
     Used when the viewer hits "Previous" — no transition, just place. */
  function snapToPhase(p) {
    const card = envLetterRef.current;
    const top = topRef.current;
    const seal = sealRef.current;
    resetAnimatedState();
    if (p >= 2 && top)  gsap.set(top,  { rotateX: 180, zIndex: 1 });
    if (p >= 2 && seal) gsap.set(seal, { yPercent: 80, rotation: 22, opacity: 0 });
    if (p >= 3 && card) gsap.set(card, { yPercent: -120, zIndex: 20 });
    /* Phase 4 is the in-flight handoff; we don't try to freeze that. */
  }

  /* ---- One mini-timeline per forward phase transition ---- */

  /* Phase 2: open the envelope (top flap rotates, seal rotates with it).
     Both elements share duration + easing so the seal feels glued to
     the flap as it folds open.  The seal also fades and drifts down a
     touch so it visibly "breaks" rather than just turning in place. */
  function playPhase2() {
    const top = topRef.current;
    const seal = sealRef.current;
    if (!top || !seal) return;
    if (tlRef.current) tlRef.current.kill();
    const tl = gsap.timeline();
    tl.to(top, {
      duration: 0.80,
      ease: "back.out(1.7)",
      rotateX: 180,
      zIndex: 1
    }, 0);
    tl.to(seal, {
      duration: 0.80,
      ease: "power2.in",   /* accelerating — reads as gravity */
      yPercent: 220,       /* falls past the envelope */
      rotation: 35,        /* slight in-plane tumble */
      opacity: 0           /* fades out as it falls */
    }, 0);
    tlRef.current = tl;
  }

  /* Phase 3: the invitation card slides up and out of the envelope. */
  function playPhase3() {
    const card = envLetterRef.current;
    if (!card) return;
    if (tlRef.current) tlRef.current.kill();
    const tl = gsap.timeline();
    tl.set(card, { zIndex: 2 });
    tl.to(card, { duration: 0.6, yPercent: -120, ease: "power2.out" });
    tl.set(card, { zIndex: 20 });
    tlRef.current = tl;
  }

  /* Phase 4: hand off to the App-root flying image, fly + scale into
     the card-frame slot, fade the envelope away.  Fires onComplete to
     transition the parent into the card scene. */
  function playPhase4() {
    const img = invitationRef.current;
    const wrap = envWrapRef.current;
    const card = envLetterRef.current;
    const shadow = shadowRef.current;
    const cardSlot = cardSlotRef.current;
    if (!img || !wrap || !card || !cardSlot) return;
    if (tlRef.current) tlRef.current.kill();

    const tl = gsap.timeline({
      onComplete: () => onComplete && onComplete()
    });

    tl.call(() => {
      const r = card.getBoundingClientRect();
      gsap.set(img, {
        x: r.left + window.scrollX,
        y: r.top + window.scrollY,
        width: r.width,
        height: r.height,
        opacity: 0
      });
    });
    tl.to(img,  { opacity: 1, duration: 0.2, ease: "power1.out" });
    tl.to(card, { opacity: 0, duration: 0.18 }, "<+0.04");
    tl.call(() => {
      const r = cardSlot.getBoundingClientRect();
      gsap.to(img, {
        x: r.left + window.scrollX,
        y: r.top + window.scrollY,
        width: r.width,
        height: r.height,
        duration: 1.1,
        ease: "power2.inOut"
      });
    }, [], ">-0.05");
    if (shadow) tl.to(shadow, { scaleX: 1.6, opacity: 0, duration: 0.9 }, 0);
    tl.add(() => wrap.classList.add('is-leaving'), ">-0.4");
    tl.to({}, { duration: 1.1 });

    tlRef.current = tl;
  }

  /* React to phase transitions. */
  useEffect(() => {
    const prev = prevPhaseRef.current;
    prevPhaseRef.current = phase;
    if (phase === prev) return;
    if (phase < prev) {
      /* Going back: instant snap to the target phase. */
      snapToPhase(phase);
      return;
    }
    /* Going forward: snap to the previous phase first (so the start
       state is exact), then play the one transition. */
    snapToPhase(prev);
    if (phase === 2) playPhase2();
    else if (phase === 3) playPhase3();
    else if (phase === 4) playPhase4();
    return () => { if (tlRef.current) tlRef.current.kill(); };
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [phase]);

  /* Legacy: clicking the envelope auto-runs all four forward phases.
     In debug mode (?debug=true) we skip the auto-advance so the
     viewer can drive every transition manually with the buttons. */
  useEffect(() => {
    if (!opening || DEBUG) return;
    const timers = [];
    setPhase(1);
    timers.push(setTimeout(() => setPhase(2),  950));   /* 0.85s flip + 0.10s hold */
    timers.push(setTimeout(() => setPhase(3), 1850));   /* + 0.80s open */
    timers.push(setTimeout(() => setPhase(4), 2500));   /* + 0.60s slide */
    return () => timers.forEach(clearTimeout);
  }, [opening]);

  /* Hide the hint as soon as the user advances past idle. */
  useEffect(() => {
    if (phase > 0 && hintRef.current) hintRef.current.classList.add('is-gone');
  }, [phase]);

  return (
    <div className="scene env-scene">

      {/* Compose the two molecules: a FlipCard whose back face holds an Envelope. */}
      <FlipCard
        flipped={phase >= 1}
        onClick={onOpen}
        disabled={opening}
        ariaLabel="Ouvrir l'enveloppe"
        wrapRef={envWrapRef}
        front={<AddressedFront name={name} />}
        back={
          <Envelope
            cardRef={envLetterRef}
            topRef={topRef}
            sealRef={sealRef}
            shadowRef={shadowRef}
            imgSrc={imgSrc}
            seal="Famille Jochimek" />
        } />

      {/* Debug controls — only shown when ?debug=true in the URL.
          Lets the viewer step through the five animation phases. */}
      {DEBUG && (
      <div className="env-debug">
        <button
          type="button"
          className="env-debug-btn"
          onClick={() => setPhase((p) => Math.max(p - 1, 0))}
          disabled={phase === 0}>
          ← Previous
        </button>
        <span className="env-debug-phase">
          {phase === 0 && "Phase 0 — front: addressee + stamp"}
          {phase === 1 && "Phase 1 — flipped: sealed envelope"}
          {phase === 2 && "Phase 2 — opening: seal + flap"}
          {phase === 3 && "Phase 3 — invitation out of envelope"}
          {phase === 4 && "Phase 4 — invitation scales / flies"}
        </span>
        <button
          type="button"
          className="env-debug-btn"
          onClick={() => setPhase((p) => Math.min(p + 1, 4))}
          disabled={phase === 4}>
          Next →
        </button>
      </div>
      )}

      <div className="env-hint" ref={hintRef}>
        <span className="env-hint-arrow">↑</span>
        <span>Cliquez sur l'enveloppe pour l'ouvrir</span>
      </div>
    </div>);
}

/* ------------------------------------------------------------------ */
/* CARD                                                                */
/* ------------------------------------------------------------------ */

function CardScene({ name, events, rsvps, onPick, cardSlotRef }) {
  const intro = events.length === 1
    ? "Veuillez répondre à l'invitation."
    : events.length === 2
    ? "Veuillez répondre individuellement aux deux temps forts. Vous pouvez participer à l'un ou aux deux."
    : "Veuillez répondre individuellement à chacun des trois temps forts. Vous pouvez participer à un, deux, ou aux trois.";

  return (
    <div className="scene card-scene">
      <div className="card-hello">
        <span className="hello-line"></span>
        <span className="hello-text">Pour {name}</span>
        <span className="hello-line"></span>
      </div>

      {/* size & position target only — the real invitation img lives at App root */}
      <div className="card-frame" ref={cardSlotRef}></div>

      <div className="rsvp-block">
        <div className="rsvp-eyebrow">— Confirmation de présence —</div>
        <h2 className="rsvp-title">Nous serions honorés de votre présence</h2>
        <p className="rsvp-sub">{intro}</p>

        <div className="rsvp-grid">
          {events.map((ev, i) => {
            const r = rsvps[ev.id];
            const total = r ? (r.adults || 0) + (r.children || 0) : 0;
            return (
              <button
                key={ev.id}
                className={"rsvp-card " + (r ? r.attending ? "is-yes" : "is-no" : "")}
                onClick={() => onPick(ev)}
                style={{ animationDelay: i * 120 + 600 + 'ms' }}>

                <div className="rc-status">
                  {r ?
                  r.attending ?
                  <><span className="dot dot-yes"></span> Présent · {total} {total > 1 ? 'personnes' : 'personne'}</> :
                  <><span className="dot dot-no"></span> Excusé</> :
                  <><span className="dot"></span> En attente de réponse</>}
                </div>
                <div className="rc-day">{ev.day}</div>
                <div className="rc-title">{ev.title}</div>
                <div className="rc-date">{ev.date}</div>
                <div className="rc-time">{ev.time}</div>
                {ev.place && <div className="rc-place">{ev.place}</div>}
                {ev.addr && <div className="rc-addr">{ev.addr}</div>}
                <div className="rc-cta">
                  {r ? 'Modifier ma réponse' : 'Répondre'} <span>→</span>
                </div>
              </button>);
          })}
        </div>
      </div>

      <div className="card-foot">
        <span>♦</span> Avec joie · 5786 / 2026 · Famille Jochimek <span>♦</span>
      </div>
    </div>);
}

/* ------------------------------------------------------------------ */
/* FORM                                                                */
/* ------------------------------------------------------------------ */

function FormScene({ name, event, existing, onCancel, onSubmit }) {
  const [attending, setAttending] = useState(existing?.attending ?? true);
  const [adults, setAdults] = useState(existing?.attending ? existing.adults : 1);
  const [children, setChildren] = useState(existing?.attending ? existing.children : 0);
  const [message, setMessage] = useState(existing?.message ?? '');
  const [submitting, setSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState(null);

  async function handleSubmit(e) {
    e.preventDefault();
    if (submitting) return;
    if (attending && adults + children === 0) {
      setSubmitError('Indiquez au moins un adulte ou un enfant.');
      return;
    }
    setSubmitError(null);
    setSubmitting(true);
    try {
      await onSubmit({
        attending,
        adults: Math.max(0, Math.min(50, Number(adults) || 0)),
        children: Math.max(0, Math.min(50, Number(children) || 0)),
        message: message.trim(),
      });
    } catch (err) {
      setSubmitError(err.message || "Erreur d'envoi. Réessayez.");
      setSubmitting(false);
    }
  }

  return (
    <div className="scene form-scene form-enter">
      <button className="form-back" onClick={onCancel} aria-label="Retour" disabled={submitting}>
        ← Retour à l'invitation
      </button>

      <div className="form-card">
        <div className="form-eyebrow">{event.day} {event.date.replace(' 2026', '')} · {event.time}</div>
        <h2 className="form-title">{event.title}</h2>
        {event.place && <div className="form-place">{event.place} — {event.addr}</div>}

        <form onSubmit={handleSubmit} className="form-body">
          <div className="seg">
            <button
              type="button"
              className={"seg-btn " + (attending ? "active" : "")}
              onClick={() => setAttending(true)}>
              <span className="seg-mark">✓</span> Je serai présent(e)
            </button>
            <button
              type="button"
              className={"seg-btn " + (!attending ? "active" : "")}
              onClick={() => setAttending(false)}>
              <span className="seg-mark">✕</span> Je ne pourrai pas venir
            </button>
          </div>

          {attending && <>
            <div className="field">
              <span className="field-label">Adultes</span>
              <div className="counter">
                <button type="button" onClick={() => setAdults((c) => Math.max(0, c - 1))} aria-label="Moins d'adultes">−</button>
                <div className="counter-value">{adults}</div>
                <button type="button" onClick={() => setAdults((c) => Math.min(20, c + 1))} aria-label="Plus d'adultes">+</button>
              </div>
            </div>
            <div className="field">
              <span className="field-label">Enfants</span>
              <div className="counter">
                <button type="button" onClick={() => setChildren((c) => Math.max(0, c - 1))} aria-label="Moins d'enfants">−</button>
                <div className="counter-value">{children}</div>
                <button type="button" onClick={() => setChildren((c) => Math.min(20, c + 1))} aria-label="Plus d'enfants">+</button>
              </div>
              <span className="field-hint">
                {adults + children > 0
                  ? `${adults + children} personne${adults + children > 1 ? 's' : ''} au total.`
                  : 'Indiquez au moins une personne.'}
              </span>
            </div>
          </>}

          <label className="field">
            <span className="field-label">Un mot pour Emmett <em>(facultatif)</em></span>
            <textarea
              rows="3"
              value={message}
              onChange={(e) => setMessage(e.target.value)}
              placeholder={attending ? "Hâte d'y être…" : "Désolé(e), je serai pensée."} />
          </label>

          {submitError && <div className="form-error">{submitError}</div>}

          <div className="form-actions">
            <button type="button" className="btn ghost" onClick={onCancel} disabled={submitting}>Annuler</button>
            <button type="submit" className="btn primary" disabled={submitting}>
              {submitting ? 'Envoi…' : attending ? 'Confirmer ma présence' : 'Envoyer mes excuses'}
            </button>
          </div>
        </form>
      </div>
    </div>);
}

/* ------------------------------------------------------------------ */
/* THANKS                                                              */
/* ------------------------------------------------------------------ */

function ThanksScene({ name, events, reply, rsvps, onBack }) {
  const remaining = events.filter((e) => !rsvps[e.id]);
  const attending = reply.attending;
  const total = (reply.adults || 0) + (reply.children || 0);
  const headline = attending ?
  "Merci, votre place est réservée." :
  "Merci pour votre réponse.";
  const sub = attending ?
  `Nous avons le plaisir de vous compter parmi nous pour ${reply.event.title}. ${total > 1 ? `Vous serez ${total}.` : ''} La famille Jochimek vous accueillera avec joie.` :
  `Nous regrettons que vous ne puissiez pas vous joindre à nous pour ${reply.event.title}, mais nous garderons une pensée pour vous ce jour-là.`;

  return (
    <div className="scene thanks-scene thanks-enter">
      <Confetti show={attending} />

      <div className="thanks-card">
        <div className="thanks-mark">
          {attending ? <CheckMark /> : <HeartMark />}
        </div>

        <div className="thanks-eyebrow">— {reply.event.title} —</div>
        <h1 className="thanks-headline">{headline}</h1>
        <p className="thanks-sub">{sub}</p>

        <div className="thanks-recap">
          <div className="recap-row"><span>Invité</span><strong>{name}</strong></div>
          {attending && reply.adults > 0 && <div className="recap-row"><span>Adultes</span><strong>{reply.adults}</strong></div>}
          {attending && reply.children > 0 && <div className="recap-row"><span>Enfants</span><strong>{reply.children}</strong></div>}
          <div className="recap-row"><span>Date</span><strong>{reply.event.day} {reply.event.date}</strong></div>
          {reply.event.place && <div className="recap-row"><span>Lieu</span><strong>{reply.event.place}</strong></div>}
        </div>

        {remaining.length > 0 ?
        <div className="thanks-next">
            <div className="next-label">Il vous reste {remaining.length === 1 ? '1 temps fort' : `${remaining.length} temps forts`} à confirmer :</div>
            <div className="next-list">
              {remaining.map((ev) =>
            <button key={ev.id} className="next-pill" onClick={onBack}>
                  {ev.title} <span>→</span>
                </button>
            )}
            </div>
          </div> :

        <div className="thanks-next done">
            <div className="next-label">Vous avez répondu à toutes les invitations. Mille mercis ✦</div>
          </div>
        }

        <div className="thanks-actions">
          <button className="btn ghost" onClick={onBack}>Revenir à l'invitation</button>
        </div>
      </div>
    </div>);
}

function CheckMark() {
  return (
    <svg viewBox="0 0 64 64" width="64" height="64" aria-hidden="true">
      <circle cx="32" cy="32" r="30" fill="none" stroke="currentColor" strokeWidth="1.5" opacity=".4" />
      <circle cx="32" cy="32" r="22" fill="none" stroke="currentColor" strokeWidth="1" />
      <path className="check-path" d="M20 33 L29 42 L46 23" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
    </svg>);
}
function HeartMark() {
  return (
    <svg viewBox="0 0 64 64" width="64" height="64" aria-hidden="true">
      <circle cx="32" cy="32" r="30" fill="none" stroke="currentColor" strokeWidth="1.5" opacity=".4" />
      <path d="M32 46 C18 36, 14 28, 20 23 C25 19, 30 22, 32 26 C34 22, 39 19, 44 23 C50 28, 46 36, 32 46 Z"
      fill="none" stroke="currentColor" strokeWidth="2" />
    </svg>);
}

function Confetti({ show }) {
  if (!show) return null;
  const bits = useMemo(() => {
    return Array.from({ length: 28 }, (_, i) => ({
      id: i,
      left: Math.random() * 100,
      delay: Math.random() * 0.6,
      dur: 2.4 + Math.random() * 1.6,
      size: 4 + Math.random() * 6,
      kind: Math.random() < 0.5 ? 'gold' : 'cream',
      rot: Math.random() * 360
    }));
  }, []);
  return (
    <div className="confetti" aria-hidden="true">
      {bits.map((b) =>
      <span
        key={b.id}
        className={"bit " + b.kind}
        style={{
          left: b.left + '%',
          width: b.size + 'px',
          height: b.size * 1.4 + 'px',
          animationDelay: b.delay + 's',
          animationDuration: b.dur + 's',
          transform: `rotate(${b.rot}deg)`
        }} />
      )}
    </div>);
}

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