/* App shell + rail nav + icons. v2. */
/* global React */

const { useState, useEffect, useLayoutEffect, useCallback, useRef } = React;

/* ---------- Icons (inline SVG) ---------- */
const Icon = {
  Arrow: (p) => (
    <svg width={p.size||14} height={p.size||14} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 5l7 7-7 7"/></svg>
  ),
  Check: (p) => (
    <svg width={p.size||14} height={p.size||14} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12l5 5L20 7"/></svg>
  ),
  Suitcase: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="7" width="18" height="13" rx="2"/><path d="M9 7V4h6v3M3 12h18"/></svg>
  ),
  Document: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6M8 13h8M8 17h5"/></svg>
  ),
  Alert: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2 2 21h20L12 2z"/><path d="M12 9v6M12 18v.01"/></svg>
  ),
  Edit: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M12 20h9M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/></svg>
  ),
  Search: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
  ),
  Question: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.5 9a2.5 2.5 0 0 1 5 0c0 1.7-2.5 2-2.5 4M12 17.5v.01"/></svg>
  ),
  Plane: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M21 16v-2l-8-5V3.5a1.5 1.5 0 0 0-3 0V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z"/></svg>
  ),
  Plug: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M9 2v6M15 2v6M6 8h12v3a6 6 0 0 1-6 6 6 6 0 0 1-6-6V8zM12 17v5"/></svg>
  ),
  Trend: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M3 17l6-6 4 4 8-8"/><path d="M14 7h7v7"/></svg>
  ),
  Handshake: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M3 12l4-4 4 4 6-6 4 4M12 16l4 4 3-3-4-4M9 19l-2 2-3-3 5-5"/></svg>
  ),
  Chart: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M3 3v18h18"/><rect x="7" y="11" width="3" height="7"/><rect x="13" y="7" width="3" height="11"/><rect x="19" y="14" width="2" height="4"/></svg>
  ),
  Star: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2l3 7 7 .8-5.2 4.7L18 22l-6-3.5L6 22l1.2-7.5L2 9.8 9 9z"/></svg>
  ),
  Dashboard: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="9"/><rect x="14" y="3" width="7" height="5"/><rect x="14" y="12" width="7" height="9"/><rect x="3" y="16" width="7" height="5"/></svg>
  ),
  Chat: (p) => (
    <svg width={p.size||18} height={p.size||18} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M4 4h16v12H7l-3 4z"/></svg>
  ),
  External: (p) => (
    <svg width={p.size||14} height={p.size||14} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14 4h6v6M20 4l-9 9M5 5h6v2H7v10h10v-4h2v6H5z"/></svg>
  ),
};

window.Icon = Icon;

/* ---------- useCountUp hook ---------- */
function useCountUp(target, decimals, duration, sep) {
  const ref = useRef(null);
  const [val, setVal] = useState(() => (sep === ',' ? '0,0'.padEnd(decimals + 2, '0') : '0'));
  useEffect(() => {
    let raf, start;
    const node = ref.current;
    if (!node) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          io.disconnect();
          const animate = (t) => {
            if (!start) start = t;
            const p = Math.min((t - start) / duration, 1);
            const eased = 1 - Math.pow(1 - p, 3);
            const v = target * eased;
            const formatted = decimals > 0
              ? v.toFixed(decimals).replace('.', sep)
              : Math.round(v).toLocaleString('en').replace(/,/g, ' ');
            setVal(formatted);
            if (p < 1) raf = requestAnimationFrame(animate);
          };
          raf = requestAnimationFrame(animate);
        }
      });
    }, { threshold: 0.3 });
    io.observe(node);
    return () => { io.disconnect(); if (raf) cancelAnimationFrame(raf); };
  }, [target, decimals, duration, sep]);
  return [ref, val];
}

window.useCountUp = useCountUp;

/* ---------- Brand mark ---------- */
function Brand({ dark, taglineEl }) {
  return (
    <div className="rail-brand">
      <img className="rail-brand-img" src="/fcb-logo.png" alt="FCB.ai" />
      {taglineEl && <div className="tag">{taglineEl}</div>}
    </div>
  );
}

/* ---------- Rail ---------- */
function Rail({ current, lang, onSelect, onLang, t, dark, progress }) {
  const chapters = [
    { id: 1, label: t.rail.chapter1 },
    { id: 2, label: t.rail.chapter2 },
    { id: 3, label: t.rail.chapter3 },
    { id: 4, label: t.rail.chapter4 },
    { id: 5, label: t.rail.chapter5 },
    { id: 6, label: t.rail.chapter6 },
  ];
  return (
    <aside className={`rail ${dark ? 'dark' : ''}`}>
      <div className="rail-head">
        <Brand dark={dark} taglineEl={t.fcbLogo.tagline} />
        <div className="lang">
          <button className={lang === 'fr' ? 'on' : ''} onClick={() => onLang('fr')}>FR</button>
          <button className={lang === 'en' ? 'on' : ''} onClick={() => onLang('en')}>EN</button>
        </div>
      </div>
      <nav className="rail-nav">
        {chapters.map(({ id, label }) => {
          const status = id === current ? 'active' : id < current ? 'past' : 'future';
          return (
            <button
              key={id}
              className={`rail-btn ${status}`}
              onClick={() => onSelect(id)}
            >
              <div className="num">
                {status === 'past' ? <Icon.Check size={12} /> : String(id).padStart(2, '0')}
              </div>
              <div className="body">
                <div className="kicker">{t.rail.chapterPrefix} {String(id).padStart(2, '0')}</div>
                <div className="label">{label}</div>
              </div>
              {status === 'active' && <span className="pulse" />}
            </button>
          );
        })}
      </nav>

      <div className="rail-progress" aria-hidden="true">
        <div className="fill" style={{ width: `${Math.max(4, progress * 100)}%` }} />
      </div>

      {current >= 3 && (() => {
        // CTA → opens WhatsApp with a pre-filled message.
        // FR routes to Olivier; EN routes to Antoine (CEO).
        // Slack ping fires in parallel (fire-and-forget) so we know who clicked.
        const waMsg = lang === 'fr'
          ? "Bonjour Olivier, j'aimerais réserver 30 min pour discuter de FCB.ai pour mon entreprise."
          : "Hi Antoine, I'd like to book 30 min to discuss FCB.ai for my airline.";
        const waNumber = lang === 'fr' ? '33680962279' : '33781543166';
        const waUrl = `https://wa.me/${waNumber}?text=${encodeURIComponent(waMsg)}`;
        const pingSlack = () => {
          let email = '';
          try { email = sessionStorage.getItem('fcb-airlines-email') || ''; } catch (_) {}
          try {
            fetch('/api/notify', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({
                event: 'book-30min-click',
                email,
                page: 'fcb-airlines-v2',
                referrer: document.referrer || null,
                userAgent: navigator.userAgent,
              }),
              keepalive: true,
            }).catch(() => {});
          } catch (_) {}
        };
        return (
          <a
            className="rail-cta"
            href={waUrl}
            target="_blank"
            rel="noopener noreferrer"
            onClick={pingSlack}
          >
            <span className="dot" />
            {lang === 'fr' ? 'Réserver 30 min' : 'Book a 30-min call'}
            <span className="arrow"><Icon.Arrow size={14} /></span>
          </a>
        );
      })()}

      <div className="rail-foot">
        <div className="label">{t.rail.confidentialTitle}</div>
        <div className="body">{t.rail.confidentialBody}</div>
      </div>
    </aside>
  );
}

function MobileRail({ current, dark, lang, onLang, t }) {
  return (
    <div className={`mobile-rail ${dark ? 'dark' : ''}`}>
      <img className="mobile-rail-img" src="/fcb-logo.png" alt="FCB.ai" />
      <div className="crumbs" aria-hidden="true">
        {[1,2,3,4,5,6].map(n => (
          <span key={n} className={`dot ${n < current ? 'done' : ''} ${n === current ? 'active' : ''}`} />
        ))}
        <span style={{ marginLeft: 'auto', color: 'var(--ink-55)' }}>
          {String(current).padStart(2,'0')} / 06
        </span>
      </div>
      <div className="lang" style={{ marginLeft: 'auto' }}>
        <button className={lang==='fr'?'on':''} onClick={() => onLang('fr')}>FR</button>
        <button className={lang==='en'?'on':''} onClick={() => onLang('en')}>EN</button>
      </div>
    </div>
  );
}

window.Rail = Rail;
window.MobileRail = MobileRail;

/* ---------- App ---------- */
function App() {
  const [current, setCurrent] = useState(1);
  const [lang, setLang] = useState(() => {
    const url = new URLSearchParams(window.location.search).get('lang');
    return url === 'en' ? 'en' : 'fr';
  });
  const [progress, setProgress] = useState(0);
  const canvasRef = useRef(null);

  const t = window.DICT[lang];

  const goTo = useCallback((n) => {
    const next = Math.max(1, Math.min(6, n));
    setCurrent(next);
    // Scroll reset is handled by the useLayoutEffect below — running it here
    // would race with React's commit on mobile (Safari's overflow scroll
    // settles after the new chapter mounts, undoing our scrollTop=0).
  }, []);

  // After React commits the new chapter (but before paint), reset both
  // the canvas scroll AND the window scroll. The window reset matters on
  // iOS Safari where the body can scroll independently of the .canvas.
  useLayoutEffect(() => {
    if (canvasRef.current) canvasRef.current.scrollTop = 0;
    window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
  }, [current]);

  useEffect(() => {
    const onKey = (e) => {
      const tag = (e.target && e.target.tagName) || '';
      if (tag === 'INPUT' || tag === 'TEXTAREA') return;
      if (e.key === 'ArrowRight' || e.key === 'ArrowDown') goTo(current + 1);
      else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') goTo(current - 1);
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [current, goTo]);

  // URL lang sync
  useEffect(() => {
    const url = new URL(window.location.href);
    url.searchParams.set('lang', lang);
    window.history.replaceState({}, '', url);
  }, [lang]);

  // Per-chapter scroll progress
  useEffect(() => {
    const node = canvasRef.current;
    if (!node) return;
    const onScroll = () => {
      const max = node.scrollHeight - node.clientHeight;
      const p = max > 0 ? node.scrollTop / max : 0;
      setProgress(Math.max(0, Math.min(1, p)));
    };
    onScroll();
    node.addEventListener('scroll', onScroll, { passive: true });
    return () => node.removeEventListener('scroll', onScroll);
  }, [current]);

  const dark = current >= 5;
  const next = () => goTo(current + 1);

  const ChapterComp =
    current === 1 ? window.Chapter1 :
    current === 2 ? window.Chapter2 :
    current === 3 ? window.Chapter3 :
    current === 4 ? window.Chapter4 :
    current === 5 ? window.Chapter5 :
                    window.Chapter6;

  // Mobile "Book 30 min" floating CTA — same wa.me link + Slack ping as the
  // desktop rail CTA, so visitors can convert on phones too.
  // FR routes to Olivier; EN routes to Antoine (CEO).
  const waMsg = lang === 'fr'
    ? "Bonjour Olivier, j'aimerais réserver 30 min pour discuter de FCB.ai pour mon entreprise."
    : "Hi Antoine, I'd like to book 30 min to discuss FCB.ai for my airline.";
  const waNumber = lang === 'fr' ? '33680962279' : '33781543166';
  const waUrl = `https://wa.me/${waNumber}?text=${encodeURIComponent(waMsg)}`;
  const pingBook = () => {
    let email = '';
    try { email = sessionStorage.getItem('fcb-airlines-email') || ''; } catch (_) {}
    try {
      fetch('/api/notify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          event: 'book-30min-click',
          email,
          page: 'fcb-airlines-v2 (mobile-fab)',
          referrer: document.referrer || null,
          userAgent: navigator.userAgent,
        }),
        keepalive: true,
      }).catch(() => {});
    } catch (_) {}
  };

  return (
    <div className={`app ${dark ? 'dark' : ''}`}>
      <Rail
        current={current}
        lang={lang}
        onLang={setLang}
        onSelect={goTo}
        t={t}
        dark={dark}
        progress={progress}
      />
      <MobileRail current={current} dark={dark} lang={lang} onLang={setLang} t={t} />
      <main
        ref={canvasRef}
        className={`canvas ${dark ? 'dark' : ''}`}
      >
        <div key={`${current}-${lang}`} className="page-fade">
          {ChapterComp ? (
            <ChapterComp t={t} lang={lang} onNext={next} current={current} />
          ) : (
            <div style={{padding: 64}}>Chapter {current} not found.</div>
          )}
        </div>
      </main>
      {current >= 3 && (
        <a
          className="mobile-cta"
          href={waUrl}
          target="_blank"
          rel="noopener noreferrer"
          onClick={pingBook}
        >
          <span className="dot" />
          {lang === 'fr' ? 'Réserver 30 min' : 'Book 30 min'}
        </a>
      )}
    </div>
  );
}

window.App = App;

/* ---------- Gate (homepage email capture → Slack notify → pitch) ---------- */

const GATE_COPY = {
  fr: {
    kicker: 'FCB.ai · Compagnies aériennes · 2026',
    h1Pre: 'L’IA conversationnelle',
    h1It: 'au service de l’expérience passager.',
    sub: 'Document confidentiel destiné aux compagnies aériennes. Entrez votre email professionnel pour accéder à la présentation.',
    placeholder: 'jean@compagnie.com',
    cta: 'Accéder à la présentation',
    submitting: 'Envoi…',
    done: 'Ouverture…',
    errInvalid: 'Email invalide',
    metaA: 'Confidentiel',
    metaB: '~5 min de lecture',
    metaC: 'Démo intégrée',
  },
  en: {
    kicker: 'FCB.ai · Airlines · 2026',
    h1Pre: 'Conversational AI',
    h1It: 'for the passenger experience.',
    sub: 'Confidential document for airlines. Enter your work email to access the deck.',
    placeholder: 'jane@airline.com',
    cta: 'Open the deck',
    submitting: 'Sending…',
    done: 'Opening…',
    errInvalid: 'Invalid email',
    metaA: 'Confidential',
    metaB: '~5 min read',
    metaC: 'Live demos inside',
  },
};

function Gate({ onUnlock }) {
  const initialLang = (() => {
    const u = new URLSearchParams(window.location.search).get('lang');
    return u === 'en' ? 'en' : 'fr';
  })();
  const [lang, setLang] = useState(initialLang);
  const [email, setEmail] = useState('');
  const [status, setStatus] = useState('idle'); // idle | submitting | done | error
  const [errMsg, setErrMsg] = useState('');
  const copy = GATE_COPY[lang];

  // Sync the gate's lang choice into the URL so the App (which reads ?lang on
  // mount) picks up the user's choice when the gate unlocks.
  useEffect(() => {
    try {
      const url = new URL(window.location.href);
      url.searchParams.set('lang', lang);
      window.history.replaceState({}, '', url);
    } catch (_) {}
  }, [lang]);

  const submit = async (e) => {
    e.preventDefault();
    const v = email.trim();
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) {
      setErrMsg(copy.errInvalid);
      setStatus('error');
      return;
    }
    setStatus('submitting');
    setErrMsg('');
    try {
      await fetch('/api/notify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          email: v,
          page: 'fcb-airlines-v2',
          referrer: document.referrer || null,
          userAgent: navigator.userAgent,
        }),
      });
    } catch (_) {
      // Slack errors shouldn't block the gate UX.
    }
    try { sessionStorage.setItem('fcb-airlines-granted', '1'); } catch (_) {}
    try { sessionStorage.setItem('fcb-airlines-email', v); } catch (_) {}
    setStatus('done');
    // Blur the focused input + reset scroll *before* the App mounts.
    // Without this, iOS Safari can keep its on-focus zoom level even after
    // the input is removed from the DOM — the whole deck stays zoomed in.
    setTimeout(() => {
      try { document.activeElement && document.activeElement.blur(); } catch (_) {}
      try { window.scrollTo({ top: 0, left: 0, behavior: 'auto' }); } catch (_) {}
      onUnlock();
    }, 220);
  };

  return (
    <div className="gate">
      {/* Decorative flight-path arc */}
      <svg className="gate-arc" viewBox="0 0 1200 800" aria-hidden="true">
        <defs>
          <linearGradient id="gateArcGrad" x1="0" y1="1" x2="1" y2="0">
            <stop offset="0%" stopColor="rgba(214,154,31,.35)" />
            <stop offset="60%" stopColor="rgba(11,59,82,.18)" />
            <stop offset="100%" stopColor="rgba(11,145,214,.40)" />
          </linearGradient>
          <radialGradient id="gateArcOrigin" cx="0.5" cy="0.5" r="0.5">
            <stop offset="0%" stopColor="rgba(214,154,31,.5)" />
            <stop offset="100%" stopColor="rgba(214,154,31,0)" />
          </radialGradient>
          <radialGradient id="gateArcDest" cx="0.5" cy="0.5" r="0.5">
            <stop offset="0%" stopColor="rgba(11,145,214,.5)" />
            <stop offset="100%" stopColor="rgba(11,145,214,0)" />
          </radialGradient>
        </defs>
        <path d="M 30 720 Q 400 80 1170 130" stroke="url(#gateArcGrad)" strokeWidth="1.6" fill="none" strokeDasharray="2 9" strokeLinecap="round" />
        <circle cx="30" cy="720" r="42" fill="url(#gateArcOrigin)" />
        <circle cx="30" cy="720" r="6" fill="#D69A1F" />
        <circle cx="1170" cy="130" r="44" fill="url(#gateArcDest)" />
        <circle cx="1170" cy="130" r="6" fill="#0B91D6" />
      </svg>

      <div className="gate-shell">
        <div className="gate-rail-top">
          <div className="gate-brand">
            <img className="gate-brand-img" src="/fcb-logo.png" alt="FCB.ai" />
          </div>
          <div className="lang gate-lang">
            <button className={lang === 'fr' ? 'on' : ''} onClick={() => setLang('fr')}>FR</button>
            <button className={lang === 'en' ? 'on' : ''} onClick={() => setLang('en')}>EN</button>
          </div>
        </div>

        <div className="gate-card">
          <div className="gate-kicker">{copy.kicker}</div>
          <h1 className="gate-h1">
            {copy.h1Pre}<br />
            <span className="it">{copy.h1It}</span>
          </h1>
          <p className="gate-sub">{copy.sub}</p>

          <form className="gate-form" onSubmit={submit}>
            <input
              type="email"
              placeholder={copy.placeholder}
              value={email}
              onChange={(e) => { setEmail(e.target.value); if (status === 'error') setStatus('idle'); }}
              required
              autoFocus
              disabled={status === 'submitting' || status === 'done'}
            />
            <button type="submit" disabled={status === 'submitting' || status === 'done'}>
              {status === 'submitting' ? copy.submitting : status === 'done' ? copy.done : copy.cta}
              <Icon.Arrow size={14} />
            </button>
          </form>
          {errMsg && <div className="gate-err">{errMsg}</div>}

          <div className="gate-meta">
            <span><span className="dot" />{copy.metaA}</span>
            <span>{copy.metaB}</span>
            <span>{copy.metaC}</span>
          </div>
        </div>
      </div>
    </div>
  );
}

function Root() {
  const [granted, setGranted] = useState(() => {
    try { return sessionStorage.getItem('fcb-airlines-granted') === '1'; }
    catch (_) { return false; }
  });
  if (!granted) return <Gate onUnlock={() => setGranted(true)} />;
  return <App />;
}

window.Gate = Gate;
window.Root = Root;
