/* ============================================================================
   Animatic "Giới thiệu 60 giây" — 12 cảnh theo §3 Storyboard của
   2026-06-14-promo-video-60s-script.md. Tự nạp ảnh thật ở webinar/promo/<tên>.png
   (cảnh 3–11); chưa có ảnh → placeholder brand-styled. Cảnh 1/2/12 = đồ họa.
   Pure JSX + CSS, không thêm thư viện. Xem webinar/promo/README.md.
   ========================================================================== */

const PROMO_DIR = 'webinar/promo/';
const RING_NODES = ['Tiếp nhận', 'Đo', 'Phân tầng', 'Điều trị', 'Tái khám', 'Báo cáo'];

// 12 cảnh — dur(ms) bám timecode §3b; tổng ~58s.
const SCENES = [
  { k: 'logo', dur: 3000, big: 'Vietcanthi', vo: 'Vietcanthi.', tx: 'fade', node: -1 },
  { k: 'ring', dur: 5000, big: 'Hệ sinh thái kiểm soát cận thị · khép kín', vo: 'Hệ sinh thái kiểm soát cận thị khép kín cho phòng khám nhãn khoa.', tx: 'fade', node: -1 },
  { k: 'shot', img: '03-devices', dur: 4000, big: 'Kết nối máy đo · chuẩn hóa dữ liệu', vo: 'Bắt đầu từ dữ liệu chuẩn hóa — kết nối máy đo,', tx: 'slide', node: 1 },
  { k: 'shot', img: '04-visit', dur: 4000, big: 'Số đo tự chảy vào · không gõ tay', vo: 'số đo tự chảy vào hồ sơ, không gõ tay.', tx: 'match', node: 1 },
  { k: 'shot', img: '05-tier', dur: 4000, big: 'Phân tầng nguy cơ · theo bằng chứng', vo: 'Hệ thống tự động phân tầng nguy cơ theo bằng chứng.', tx: 'zoom', node: 2 },
  { k: 'shot', img: '04-visit', dur: 4000, big: 'Nhập liệu nhanh', vo: 'Khám nhanh, nhập liệu gọn.', tx: 'push', node: 3 },
  { k: 'shot', img: '07-treatment', dur: 6000, big: 'Đề xuất phác đồ · có dẫn chứng', vo: 'Hệ thống đề xuất phương pháp điều trị phù hợp, kèm trích dẫn y văn.', tx: 'cross', node: 3 },
  { k: 'shot', img: '08-schedule', dur: 5000, big: 'Mỗi phương pháp · một lịch riêng', vo: 'Mỗi phương pháp một lịch riêng — tự động phân lịch tái khám,', tx: 'slide', node: 4 },
  { k: 'shot', img: '09-reminders', dur: 5000, big: 'Tự động nhắc · giảm vắng hẹn', vo: 'và nhắc đúng hạn, giảm bỏ theo dõi.', tx: 'cross', node: 4 },
  { k: 'shot', img: '10-alchart', dur: 5000, big: 'Theo dõi tốc độ trục nhãn cầu', vo: 'Theo dõi tốc độ thay đổi trục nhãn cầu,', tx: 'ken', node: 5 },
  { k: 'shot', img: '11-ksct', dur: 7000, big: 'Đánh giá hiệu quả · dự phóng', vo: 'đánh giá hiệu quả từng phương pháp, phân tích xu hướng và dự phóng theo bằng chứng.', tx: 'push', node: 5 },
  { k: 'cta', dur: 6000, big: 'Đặt demo hôm nay · vietcanthi.com', vo: 'Vietcanthi — kiểm soát cận thị khép kín, chuẩn y văn. Đặt demo ngay hôm nay.', tx: 'fade', node: -1 }];

const TOTAL = SCENES.reduce((s, x) => s + x.dur, 0);

/* ---- Vòng 6 mắt xích (Tiếp nhận→Đo→Phân tầng→Điều trị→Tái khám→Báo cáo) ---- */
function Ring6({ active = -1, size = 120, labels = false, closed = false }) {
  const cx = size / 2, cy = size / 2, r = size * 0.36;
  const node = RING_NODES.map((name, i) => {
    const a = (-90 + i * 60) * Math.PI / 180;
    return {
      name,
      x: cx + r * Math.cos(a), y: cy + r * Math.sin(a),
      lx: cx + (r + size * 0.16) * Math.cos(a), ly: cy + (r + size * 0.16) * Math.sin(a) };
  });
  return (
    <svg viewBox={'0 0 ' + size + ' ' + size} width={size} height={size} className={'ring6' + (closed ? ' closed' : '')} aria-hidden="true">
      <circle cx={cx} cy={cy} r={r} className="ring-track" style={{ strokeDasharray: 2 * Math.PI * r, strokeDashoffset: 2 * Math.PI * r }} />
      <g className="ring-flow-g" style={{ transformOrigin: cx + 'px ' + cy + 'px' }}>
        <circle cx={cx} cy={cy - r} r={size * 0.03} className="ring-flow" />
      </g>
      {node.map((p, i) =>
        <g key={i} className={'ring-node' + (i === active ? ' on' : '') + (active < 0 ? ' all' : '')} style={{ '--d': (i * 0.12) + 's' }}>
          <circle cx={p.x} cy={p.y} r={size * 0.058} />
          {labels && <text x={p.lx} y={p.ly} textAnchor="middle" dominantBaseline="middle" fontSize={size * 0.052}>{p.name}</text>}
        </g>)}
    </svg>);
}

/* ---- Cảnh đồ họa: logo sting / vòng / CTA ---- */
function LogoScene() {
  return (
    <div className="anim-graphic">
      <img className="sting-mark" src="brand-assets/mark-sun-amber.svg" alt="" width="120" height="120" />
      <img className="sting-word" src="brand-assets/wordmark-white.svg" alt="VietCanThi" />
    </div>);
}

function RingScene({ on }) {
  return (
    <div className="anim-graphic ring-scene">
      {on && <Ring6 active={-1} size={250} labels />}
    </div>);
}

function CtaScene() {
  return (
    <div className="anim-graphic cta-scene">
      <div className="cta-ring"><Ring6 active={-1} size={250} closed /></div>
      <img className="cta-mark" src="brand-assets/mark-sun-amber.svg" alt="" width="76" height="76" />
      <img className="cta-word" src="brand-assets/wordmark-white.svg" alt="VietCanThi" />
      <p className="cta-tag">See the world clearly.</p>
      <div className="cta-row">
        <span className="cta-qr" aria-hidden="true">
          {Array.from({ length: 49 }).map((_, i) =>
            <i key={i} className={((i * 7 + (i % 5) + (i % 3) * 4) % 3 === 0) ? 'on' : ''} />)}
        </span>
        <span className="cta-site">vietcanthi.com<em>Đặt demo cho phòng khám của bạn</em></span>
      </div>
    </div>);
}

function ShotScene({ scene, active }) {
  const { useState } = React;
  const [err, setErr] = useState(false);
  if (err) {
    return (
      <div className="anim-ph">
        <span className="anim-ph-tag">Cảnh · ảnh thật</span>
        <b>{scene.big}</b>
        <span className="anim-ph-hint">Thả ảnh <code>{PROMO_DIR}{scene.img}.png</code> để thay placeholder</span>
      </div>);
  }
  return (
    <img className={'anim-shot' + (scene.tx === 'ken' ? ' ken' : '') + (active ? ' run' : '')}
      src={PROMO_DIR + scene.img + '.png'} alt={scene.big} onError={() => setErr(true)} />);
}

function Animatic() {
  const { useState, useEffect, useRef } = React;
  const [idx, setIdx] = useState(0);
  const [playing, setPlaying] = useState(true);
  const wrapRef = useRef(null);
  const timerRef = useRef(null);
  const startRef = useRef(0);
  const leftRef = useRef(SCENES[0].dur);
  const resumeRef = useRef(false);

  useEffect(() => {
    clearTimeout(timerRef.current);
    if (!playing) return undefined;
    const m = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)');
    if (m && m.matches) return undefined;
    const dur = resumeRef.current ? leftRef.current : SCENES[idx].dur;
    resumeRef.current = false;
    leftRef.current = dur;
    startRef.current = (window.performance || Date).now();
    timerRef.current = setTimeout(() => setIdx((p) => (p + 1) % SCENES.length), dur);
    return () => clearTimeout(timerRef.current);
  }, [idx, playing]);

  const pause = () => {
    if (!playing) return;
    const elapsed = (window.performance || Date).now() - startRef.current;
    leftRef.current = Math.max(400, leftRef.current - elapsed);
    resumeRef.current = true;
    setPlaying(false);
  };
  const toggle = () => { if (playing) pause(); else setPlaying(true); };
  const goTo = (i) => { resumeRef.current = false; leftRef.current = SCENES[i].dur; setIdx(i); };
  const replay = () => { resumeRef.current = false; leftRef.current = SCENES[0].dur; setIdx(0); setPlaying(true); };

  const fullscreen = () => {
    const el = wrapRef.current;
    if (!el) return;
    if (document.fullscreenElement) document.exitFullscreen();
    else if (el.requestFullscreen) el.requestFullscreen();
  };

  const cur = SCENES[idx];

  return (
    <div className="animatic" ref={wrapRef}>
      <div className="anim-stage">
        {SCENES.map((s, k) => {
          const on = k === idx;
          return (
            <div key={k} className={'anim-layer tx-' + s.tx + (on ? ' on' : '') + (' kind-' + s.k)} aria-hidden={!on}>
              {s.k === 'logo' && <LogoScene />}
              {s.k === 'ring' && <RingScene on={on} />}
              {s.k === 'cta' && <CtaScene />}
              {s.k === 'shot' && <ShotScene scene={s} active={on} />}
            </div>);
        })}

        {/* badge vòng 6 mắt xích góc trên — xuất hiện từ cảnh 2 trở đi trong các cảnh app */}
        {cur.k === 'shot' &&
          <div className="anim-ringbadge">
            <Ring6 active={cur.node} size={96} />
            <span className="rb-cap">{cur.node >= 0 ? RING_NODES[cur.node] : ''}</span>
          </div>}

        {/* overlay text + phụ đề VO (cảnh logo/cta có chữ riêng nên ẩn) */}
        {cur.k === 'shot' && <div className="anim-scrim" />}
        {(cur.k === 'shot' || cur.k === 'ring') &&
          <div className={'anim-ovl' + (cur.k === 'ring' ? ' on-dark' : '')}>
            {cur.k === 'shot' && <span className="anim-eyebrow">Vietcanthi · Kiểm soát cận thị khép kín</span>}
            <p key={'big' + idx} className="anim-big">{cur.big}</p>
            <p key={'vo' + idx} className="anim-vo">{cur.vo}</p>
          </div>}
      </div>

      {/* controls + progress */}
      <div className="anim-bar">
        <button type="button" className="anim-btn" onClick={toggle} aria-label={playing ? 'Tạm dừng' : 'Phát'}>
          {playing ? '❚❚' : '►'}
        </button>
        <button type="button" className="anim-btn" onClick={replay} aria-label="Phát lại">↻</button>
        <div className="anim-prog">
          {SCENES.map((s, k) =>
            <button key={k} type="button" className={'pseg' + (k < idx ? ' done' : '') + (k === idx ? ' active' : '')}
              style={{ flexGrow: s.dur }} onClick={() => goTo(k)} aria-label={'Cảnh ' + (k + 1) + ': ' + s.big}>
              {k === idx &&
                <i key={'f' + idx} style={{ animationDuration: SCENES[idx].dur + 'ms', animationPlayState: playing ? 'running' : 'paused' }} />}
            </button>)}
        </div>
        <span className="anim-count">{String(idx + 1).padStart(2, '0')}<em>/{SCENES.length}</em></span>
        <button type="button" className="anim-btn" onClick={fullscreen} aria-label="Toàn màn hình">⛶</button>
      </div>
    </div>);
}

window.Animatic = Animatic;
