/* ============================================================
   TEND BEARING ON THE LANDING PAGE
   Twelve questions in place. The needle stays still until the twelfth
   answer, then settles once (Revision 5, 7.4), the result appears in
   place, and Read more opens the 30-minute offer in place. The offer
   record is shared with report-bearing-card.html, so the clock never
   restarts. Answers are saved only when the person asks to keep them.
   ============================================================ */
const { useState: useLB, useEffect: useLBE, useRef: useLBR } = React;
const LB = window.TENDBearing;

function lbRead(k) {try {return JSON.parse(localStorage.getItem(k) || 'null');} catch (e) {return null;}}
function lbWrite(k, v) {try {localStorage.setItem(k, JSON.stringify(v));} catch (e) {}}
function lbMakeOffer() {
  const now = Date.now();
  const o = { offer_id: 'bo_' + now.toString(36) + Math.random().toString(36).slice(2, 8), started_at: now, expires_at: now + LB.OFFER_MS };
  lbWrite(LB.OFFER_KEY, o);
  try { window.TENDApi.api('bearing/offer', { offer_id: o.offer_id }, { keepalive: true }).catch(() => {}); } catch (e) {}
  return o;
}
const lbReduce = () => !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
const lbPad = (n) => (n < 10 ? '0' : '') + n;

function LBCompass({ cv, settle }) {
  const ref = useLBR(null);
  const played = useLBR(false);
  const key = cv ? [cv.aqua, cv.coral, cv.amber, cv.violet].join(',') : 'rest';
  useLBE(() => {
    const el = ref.current;if (!el) return;
    el.innerHTML = window.TENDCompass.render({
      ...(cv || { aqua: 0, coral: 0, amber: 0, violet: 0 }), size: 300, style: 'field', labels: true, needle: !!cv, idSeed: 'lb',
      alt: cv ? undefined : 'The TEND Compass at rest. The needle appears when all twelve questions are answered.'
    });
    if (!cv) {played.current = false;return;}
    if (!settle || played.current || lbReduce()) return;
    played.current = true;
    const g = el.querySelector('g[transform^="rotate"]');
    const m = g && /rotate\(([-\d.]+)/.exec(g.getAttribute('transform'));
    if (!g || !m) return;
    /* A soft damped spring over 1.2s, drawn on the needle's own rotate()
       so the resting state is exactly the rendered one. */
    const d = parseFloat(m[1]),t0 = performance.now(),D = 1200;
    const spring = (p) => 1 - Math.exp(-6 * p) * Math.cos(9 * p);
    let raf = 0;
    const step = (now) => {
      const p = Math.min(1, (now - t0) / D);
      g.setAttribute('transform', 'rotate(' + (p >= 1 ? d : d * spring(p)).toFixed(2) + ' 200 200)');
      if (p < 1) raf = requestAnimationFrame(step);
    };
    g.setAttribute('transform', 'rotate(0 200 200)');
    raf = requestAnimationFrame(step);
    const fin = setTimeout(() => {cancelAnimationFrame(raf);g.setAttribute('transform', 'rotate(' + d + ' 200 200)');}, D + 150);
    return () => {cancelAnimationFrame(raf);clearTimeout(fin);g.setAttribute('transform', 'rotate(' + d + ' 200 200)');};
  }, [key, settle]);
  return <div ref={ref} className="lb-compass" />;
}

function LBOffer({ name, onClose }) {
  const [o] = useLB(() => lbRead(LB.OFFER_KEY) || lbMakeOffer());
  const [now, setNow] = useLB(Date.now());
  useLBE(() => {const id = setInterval(() => setNow(Date.now()), 1000);return () => clearInterval(id);}, []);
  const PX = window.TENDPrices,TT = window.TENDTimes;
  const left = Math.max(0, o.expires_at - now),expired = left <= 0,s = Math.ceil(left / 1000);
  const [ga, setGa] = useLB(false);
  const href = (p, g) => 'checkout.html?product=' + p + (g ? '&genai=1' : '') + (expired ? '' : '&offer_id=' + encodeURIComponent(o.offer_id));
  const T = PX.table, dirPrice = PX.isZA ? 'R' + (T.direction.zar + (ga ? T.genai.zar : 0)).toLocaleString('en-US') : '$' + (T.direction.usd + (ga ? T.genai.usd : 0));
  return (
    <section className="lb-offer" aria-labelledby="lb-offer-h">
      <h3 id="lb-offer-h">There is more to your result</h3>
      {expired ?
      <p className="lb-body">Your 30-minute offer has ended, and both reports are still here whenever you are ready.</p> :
      <p className="lb-body">The rest of your description is waiting in TEND Heading and TEND Direction, along with the current that sits beside {name} and shapes how it lands with the people around you. Because you wanted to keep reading, we would like to add a short conversation to help you make sense of it, so if you choose either one in the next 30 minutes, a free debrief comes with it.</p>}
      {!expired &&
      <div>
          <div className="lb-timer"><span>{left <= 5 * 60 * 1000 ? 'Five minutes left on your offer' : 'Your offer is held for'}</span><b className="tnum">{lbPad(Math.floor(s / 60))}:{lbPad(s % 60)}</b></div>
          <div className="lb-bar" aria-hidden="true"><i style={{ width: (left / LB.OFFER_MS * 100).toFixed(2) + '%' }} /></div>
        </div>
      }
      <div className="lb-offers">
        <div className="lb-o">
          <p className="lb-o-name">TEND Heading</p>
          <p className="lb-o-price tnum">{PX.format('heading')}</p>
          <p className="lb-o-meta">{TT.questions.heading} questions · {TT.heading}</p>
          {!expired && <p className="lb-o-deb">Includes a free 30-minute group debrief</p>}
          <a className="btn btn-outline" href={href('heading')}>Choose TEND Heading</a>
        </div>
        <div className="lb-o is-most">
          <span className="lb-o-tag">Most complete</span>
          <p className="lb-o-name">TEND Direction</p>
          <p className="lb-o-price tnum">{dirPrice}</p>
          <p className="lb-o-meta">{TT.questions.direction} questions · {TT.direction}</p>
          {!expired && <p className="lb-o-deb">Includes a free 30-minute one-to-one debrief</p>}
          <label className="ld-addon"><input type="checkbox" checked={ga} onChange={(e) => setGa(e.target.checked)} /><span><strong>Add the Gen AI chapter</strong> for <span className="tnum">+{PX.format('genai')}</span></span></label>
          <a className="btn btn-ink" href={href('direction', ga)}>Choose TEND Direction</a>
        </div>
      </div>
      {!PX.isZA && <p className="lb-small">{PX.note}</p>}
      {!expired && <p className="lb-small">Your 30 minutes began when you tapped Read more and will not restart. Debriefs are held online and booked after you buy, using the times in your confirmation email, and group debriefs have a limited number of places.</p>}
      <button type="button" className="lb-link" onClick={onClose}>Not now</button>
    </section>);

}

function LBKeep({ answers, res, kept, onKept }) {
  const [email, setEmail] = useLB('');
  const [ok, setOk] = useLB(false);
  const [err, setErr] = useLB('');
  const [busy, setBusy] = useLB(false);
  if (kept) return (
    <div className="lb-keep" role="status">
      <p><strong>Your result is saved.</strong> We have emailed {kept.email} a link that brings you back to it on any device. If you go on to TEND Heading, these twelve answers carry over.</p>
    </div>);

  function submit(e) {
    e.preventDefault();
    if (!/^\S+@\S+\.\S+$/.test(email)) return setErr('Please enter an email address we can send your link to.');
    if (!ok) return setErr('Please tick the box so we can save your answers.');
    const offer = lbRead(LB.OFFER_KEY);
    setBusy(true);
    window.TENDApi.api('bearing/keep', { email: email.trim(), answers, consent: true, offer_id: offer && offer.expires_at > Date.now() ? offer.offer_id : '' })
      .then(() => {
        const rec = { email: email.trim(), answers, cv: res.cv, top: res.top, second: res.second, at: Date.now() };
        lbWrite(LB.KEPT_KEY, rec); onKept(rec);
      })
      .catch(e2 => setErr(e2.message || 'We could not save your result just now. Please try again in a moment.'))
      .finally(() => setBusy(false));
  }
  return (
    <form className="lb-keep" onSubmit={submit} noValidate>
      <h3>Keep your result</h3>
      <p>Enter your email and we will send you a link that brings you back to this result on any device. Your twelve answers are then saved with your consent, and they carry over if you go on to TEND Heading.</p>
      <label className="lb-field"><span>Email</span><input type="email" autoComplete="email" value={email} onChange={(e) => {setEmail(e.target.value);setErr('');}} /></label>
      <label className="lb-check"><input type="checkbox" checked={ok} onChange={(e) => {setOk(e.target.checked);setErr('');}} /><span>Save my answers and email me a link to my result.</span></label>
      {err && <p className="lb-err" role="alert">{err}</p>}
      <button type="submit" className="btn btn-outline" disabled={busy}>{busy ? 'Saving…' : 'Email me a link to my result'}</button>
    </form>);

}

function LandingBearing() {
  const N = LB.N;
  const kept0 = lbRead(LB.KEPT_KEY);
  const keptOk = !!(kept0 && kept0.answers && kept0.cv && kept0.top);
  const [answers, setAnswers] = useLB(keptOk ? kept0.answers : Array(N).fill(null));
  const [i, setI] = useLB(keptOk ? N : 0);
  const [settle, setSettle] = useLB(false);
  const [open, setOpen] = useLB(false);
  const [kept, setKept] = useLB(keptOk ? kept0 : null);
  const [res, setRes] = useLB(keptOk ? { cv: kept0.cv, top: kept0.top, second: kept0.second } : null);
  const [ready, setReady] = useLB(LB.items.length === N);
  const [loadErr, setLoadErr] = useLB('');
  const headRef = useLBR(null);
  const done = i >= N && !!res;
  const scoring = i >= N && !res;

  useLBE(() => {
    if (LB.items.length === N) return;
    LB.load().then(() => setReady(LB.items.length === N)).catch(() => setLoadErr('TEND Bearing could not load just now. Please refresh the page to try again.'));
  }, []);
  useLBE(() => {
    if (!scoring) return;
    LB.scoreRemote(answers).then(r => { setRes({ cv: r.cv, top: r.top, second: r.second }); setSettle(true); setTimeout(() => headRef.current && headRef.current.focus({ preventScroll: true }), 80); })
      .catch(() => setLoadErr('We could not read your answers just now. Please check your connection and tap Answer again.'));
  }, [scoring]);

  useLBE(() => {window.__tendBearingDone = done;window.dispatchEvent(new Event('tend-bearing'));}, [done]);

  /* A ref keeps quick double taps on the same question from being lost. */
  const st = useLBR({ i, answers });
  st.current = { i, answers };
  function pick(v) {
    const cur = st.current;if (cur.i >= N) return;
    const next = cur.answers.slice();next[cur.i] = v;
    st.current = { i: cur.i + 1, answers: next };
    setAnswers(next);
    if (cur.i + 1 >= N) {setI(N);} else
    setI(cur.i + 1);
  }
  function again() {
    try {localStorage.removeItem(LB.KEPT_KEY);} catch (e) {}
    setKept(null);setRes(null);setLoadErr('');setAnswers(Array(N).fill(null));setI(0);setSettle(false);setOpen(false);
  }

  const R = res && LB.read[res.top];
  return (
    <div className="lb" id="bearing" role="region" aria-label="TEND Bearing, twelve questions">
      <div className="lb-side">
        <LBCompass cv={res ? res.cv : null} settle={settle} />
        {!done &&
        <>
            <div className="spec lb-spec" role="progressbar" aria-label="Progress" aria-valuemin={0} aria-valuemax={100} aria-valuenow={Math.round(i / N * 100)} aria-valuetext={Math.round(i / N * 100) + ' percent answered'}>
              <span className="rest" style={{ width: (100 - i / N * 100) + '%' }} />
            </div>
            <p className="lb-note">The needle stays still until you have answered every question, because one answer on its own cannot show a direction.</p>
          </>
        }
      </div>
      <div className="lb-main">
        {loadErr ? <div><p className="lb-stmt">{loadErr}</p>{i >= N && <button type="button" className="lb-link" onClick={again}>Answer again</button>}</div> :
        scoring ? <p className="lb-stmt" role="status">Reading your twelve answers…</p> :
        !done && !ready ? <p className="lb-stmt" role="status">Loading TEND Bearing…</p> :
        !done ?
        <>
            <p className="lb-stmt" key={i}>{LB.items[i].t}</p>
            <div className="lb-scale" role="group" aria-label="Your answer">
              {LB.scale.map(([l, v]) =>
            <button key={v} type="button" className={'lb-opt' + (answers[i] === v ? ' on' : '')} aria-pressed={answers[i] === v} onClick={() => pick(v)}>
                  <span className="lb-dot" aria-hidden="true" />{l}
                </button>
            )}
            </div>
            {i > 0 && <button type="button" className="lb-link" onClick={() => setI(i - 1)}>Back to the last question</button>}
          </> :

        <div className={'lb-result energy-' + res.top}>
            <p className="lb-count">Your TEND Bearing</p>
            <h2 ref={headRef} tabIndex={-1} className="lb-head">You led with {tendify(R.name)} in these answers.</h2>
            <p className="lb-id"><span className="lb-sw" aria-hidden="true" /><span>{R.name}, {R.who}</span></p>
            <div className="lb-desc">
              <p>{R.paras[0]}</p>
              <div className="lb-fade"><p>{R.paras[1]}</p></div>
            </div>
            {!open && <button type="button" className="btn btn-ink lb-more" onClick={() => setOpen(true)}>Read more</button>}
            {open && <LBOffer name={R.name} onClose={() => setOpen(false)} />}
            <p className="lb-small">This first read comes from twelve questions, so it shows the current that came through most strongly in your answers. TEND Heading and TEND Direction show the current beside it, and how the two shape the way you work.</p>
            <LBKeep answers={answers} res={res} kept={kept} onKept={setKept} />
            <button type="button" className="lb-link" onClick={again}>Answer again</button>
          </div>
        }
      </div>
    </div>);

}

Object.assign(window, { LandingBearing });