// ==== Career as a signal trace — scrub the comet through time ========
// Awwwards-grade vertical interaction, on-brand with the site's "decision
// flight recorder" thesis. A luminous spine runs top→bottom; a comet head
// is dragged (or clicked / arrow-keyed) along it to travel 2019 → 2026.
// On first view the trace "records" itself — the comet plays down the spine,
// igniting each milestone — then settles on the latest and hands you control.

function tlEaseOut(k) { return 1 - Math.pow(1 - k, 3); }
function tlClamp(v, a, b) { return Math.min(b, Math.max(a, v)); }

function CareerTimeline() {
  const events = AYUSH.TIMELINE_EVENTS;
  const n = events.length;
  const [active, setActive] = useState(n - 1);
  const [seenRef, seen] = useInView({ threshold: 0.25 });

  const stageRef = useRef(null);
  const spineRef = useRef(null);
  const cometRef = useRef(null);
  const fillRef = useRef(null);
  const canvasRef = useRef(null);
  const tickRefs = useRef([]);

  const tRef = useRef(1);          // continuous position 0..1
  const lockRef = useRef(false);   // user has taken control
  const activeRef = useRef(n - 1);
  const tweenRef = useRef(null);

  // tie the scene ref + inview ref together (both are ref objects)
  function setRoot(el) { stageRef.current = el; if (seenRef) seenRef.current = el; }

  // ---- entrance "recording" playback ----
  useEffect(() => {
    if (!seen || lockRef.current) return;
    let raf, start = performance.now(); const dur = 1700;
    tRef.current = 0;
    function step(now) {
      if (lockRef.current) return;
      const k = Math.min(1, (now - start) / dur);
      tRef.current = tlEaseOut(k);
      if (k < 1) raf = requestAnimationFrame(step);
    }
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [seen]);

  // ---- render + particle loop ----
  useEffect(() => {
    const canvas = canvasRef.current, stage = stageRef.current, spine = spineRef.current;
    if (!canvas || !stage || !spine) return;
    const ctx = canvas.getContext("2d");
    const dpr = Math.min(window.devicePixelRatio || 1, 1.8);
    let W = 0, H = 0;
    function resize() {
      const r = stage.getBoundingClientRect();
      W = r.width; H = r.height;
      canvas.width = W * dpr; canvas.height = H * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    }
    resize();
    const ro = new ResizeObserver(resize); ro.observe(stage);

    const GOLD = "212,174,116";
    const parts = [];
    let raf, prevY = null;

    function loop(now) {
      ctx.clearRect(0, 0, W, H);
      const t = tRef.current;
      const sr = spine.getBoundingClientRect();
      const str = stage.getBoundingClientRect();
      const spineX = sr.left - str.left;
      const spineTop = sr.top - str.top;
      const spineH = sr.height;
      const cy = spineTop + t * spineH;

      // comet + fill DOM
      if (cometRef.current) cometRef.current.style.top = (t * spineH) + "px";
      if (fillRef.current) fillRef.current.style.height = (t * spineH) + "px";

      // active milestone
      const idx = Math.round(t * (n - 1));
      if (idx !== activeRef.current) { activeRef.current = idx; setActive(idx); }
      // light ticks the comet has passed
      tickRefs.current.forEach((el, i) => {
        if (!el) return;
        const tp = i / (n - 1);
        el.classList.toggle("passed", tp <= t + 0.001);
        el.classList.toggle("here", i === idx);
      });

      // horizontal connector from comet to the detail panel
      const panelX = Math.min(W - 8, spineX + Math.max(150, W * 0.30));
      const grad = ctx.createLinearGradient(spineX, 0, panelX, 0);
      grad.addColorStop(0, `rgba(${GOLD},0.5)`);
      grad.addColorStop(1, `rgba(${GOLD},0)`);
      ctx.strokeStyle = grad; ctx.lineWidth = 1;
      ctx.beginPath(); ctx.moveTo(spineX, cy); ctx.lineTo(panelX, cy); ctx.stroke();

      // spawn trail particles as the comet moves
      const moving = prevY === null ? 0 : Math.abs(cy - prevY);
      const spawn = 1 + Math.min(4, moving * 0.4);
      for (let s = 0; s < spawn; s++) {
        parts.push({
          x: spineX + (Math.random() - 0.5) * 3,
          y: cy + (Math.random() - 0.5) * 6,
          vx: (Math.random() - 0.2) * 0.5,
          vy: (Math.random() - 0.5) * 0.6 + 0.1,
          life: 0, max: 700 + Math.random() * 700,
          r: 0.6 + Math.random() * 1.4,
        });
      }
      prevY = cy;

      for (let i = parts.length - 1; i >= 0; i--) {
        const p = parts[i];
        p.life += 16; p.x += p.vx; p.y += p.vy;
        const a = Math.max(0, 1 - p.life / p.max);
        if (a <= 0) { parts.splice(i, 1); continue; }
        ctx.fillStyle = `rgba(${GOLD},${a * 0.6})`;
        ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, 6.28); ctx.fill();
      }
      if (parts.length > 240) parts.splice(0, parts.length - 240);

      // comet glow halo
      const halo = ctx.createRadialGradient(spineX, cy, 1, spineX, cy, 26);
      halo.addColorStop(0, `rgba(${GOLD},0.5)`);
      halo.addColorStop(1, `rgba(${GOLD},0)`);
      ctx.fillStyle = halo;
      ctx.beginPath(); ctx.arc(spineX, cy, 26, 0, 6.28); ctx.fill();

      raf = requestAnimationFrame(loop);
    }
    raf = requestAnimationFrame(loop);
    return () => { cancelAnimationFrame(raf); ro.disconnect(); };
  }, []);

  // ---- tween helper (clicks / keys) ----
  function tweenTo(target) {
    lockRef.current = true;
    if (tweenRef.current) cancelAnimationFrame(tweenRef.current);
    const from = tRef.current, start = performance.now(), dur = 480;
    function step(now) {
      const k = Math.min(1, (now - start) / dur);
      tRef.current = from + (target - from) * tlEaseOut(k);
      if (k < 1) tweenRef.current = requestAnimationFrame(step);
    }
    tweenRef.current = requestAnimationFrame(step);
  }

  // ---- drag scrubbing ----
  function setFromPointer(clientY) {
    const r = spineRef.current.getBoundingClientRect();
    tRef.current = tlClamp((clientY - r.top) / r.height, 0, 1);
  }
  function onScrubDown(e) {
    e.preventDefault();
    lockRef.current = true;
    if (tweenRef.current) cancelAnimationFrame(tweenRef.current);
    setFromPointer(e.clientY);
    function move(ev) { ev.preventDefault(); setFromPointer(ev.clientY); }
    function up() { window.removeEventListener("pointermove", move); window.removeEventListener("pointerup", up); }
    window.addEventListener("pointermove", move);
    window.addEventListener("pointerup", up);
  }
  function onKey(e) {
    if (e.key === "ArrowDown" || e.key === "ArrowRight") { e.preventDefault(); tweenTo(Math.min(1, (activeRef.current + 1) / (n - 1))); }
    if (e.key === "ArrowUp" || e.key === "ArrowLeft") { e.preventDefault(); tweenTo(Math.max(0, (activeRef.current - 1) / (n - 1))); }
  }

  const cur = events[active];

  return (
    <section id="timeline" className="scene" ref={setRoot} data-screen-label="10 Timeline">
      <div className="scene-head">
        <div className="eyebrow">Timeline · scrub the trace</div>
        <h2>Four years.<br />Several frameworks shipped. <em>One paper of the year.</em></h2>
        <p className="lede">
          Four years, measured in what shipped: a run of production frameworks, a stack of open-source builds, four research papers — and one named <b style={{color:"var(--ink)"}}>Paper of the Year</b>. Recorded like a flight trace. Drag the comet down the spine — or arrow-key through it — and watch each inflection point ignite.
        </p>
      </div>

      <div className="tline" role="group" aria-label="Career timeline scrubber" tabIndex={0} onKeyDown={onKey}>
        <canvas className="tline-fx" ref={canvasRef} aria-hidden="true" />

        <div className="tline-rail">
          <div className="tline-spine" ref={spineRef} onPointerDown={onScrubDown}>
            <div className="tline-fill" ref={fillRef} />
            {events.map((e, i) => (
              <button key={i}
                ref={el => tickRefs.current[i] = el}
                className="tline-tick"
                style={{ top: `${(i / (n - 1)) * 100}%` }}
                aria-label={`${e.yr} · ${e.title}`}
                onPointerDown={(ev) => { ev.stopPropagation(); }}
                onClick={() => tweenTo(i / (n - 1))}>
                <span className="ty">{e.yr}</span>
              </button>
            ))}
            <div className="tline-comet" ref={cometRef}>
              <span className="comet-core" />
              <span className="comet-grab">↕</span>
            </div>
          </div>
        </div>

        <div className="tline-detail">
          <div className="td-index">epoch {pad2(active + 1)} <span>/ {pad2(n)}</span></div>
          <div className="td-year" key={"y" + active}>{cur.yr}</div>
          <h4 className="td-title" key={"t" + active}>{cur.title}</h4>
          <div className="td-org" key={"o" + active}>{cur.org}</div>
          <p className="td-blurb" key={"b" + active}>{cur.blurb}</p>
          <div className="td-hint">drag the comet ↕ · or use ← → keys</div>
        </div>
      </div>
    </section>
  );
}

Object.assign(window, { CareerTimeline });
