// ============ SHARED HOOKS ============
const { useState, useEffect, useRef, useMemo } = React;

function useReveal(deps = []) {
  useEffect(() => {
    const obs = new IntersectionObserver(
      (entries) => entries.forEach((e) => e.isIntersecting && e.target.classList.add("in")),
      { threshold: 0.12 }
    );
    document.querySelectorAll(".reveal:not(.in)").forEach((el) => obs.observe(el));
    return () => obs.disconnect();
  }, deps);
}

function useInView(ref, threshold = 0.25) {
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const obs = new IntersectionObserver(
      (entries) => entries.forEach((e) => { if (e.isIntersecting) setSeen(true); }),
      { threshold }
    );
    obs.observe(ref.current);
    return () => obs.disconnect();
  }, []);
  return seen;
}

function useCount(target, trigger, dur = 1600) {
  const [v, setV] = useState(0);
  useEffect(() => {
    if (!trigger) return;
    const start = performance.now();
    const step = (t) => {
      const p = Math.min(1, (t - start) / dur);
      const eased = 1 - Math.pow(1 - p, 3);
      setV(Math.floor(eased * target));
      if (p < 1) requestAnimationFrame(step);
    };
    requestAnimationFrame(step);
  }, [trigger, target]);
  return v;
}

// ============ SCROLL PROGRESS (0 → 1 as section passes through viewport) ============
// mode "through": 0 when top of el hits bottom of viewport, 1 when bottom hits top
// mode "pin":     0 when top hits top of viewport, 1 when pin-distance scrolled past
function useScrollProgress(ref, { mode = "through", pinDistance = 0 } = {}) {
  const [p, setP] = useState(0);
  useEffect(() => {
    if (!ref.current) return;
    let raf = 0;
    const compute = () => {
      const el = ref.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      let prog = 0;
      if (mode === "through") {
        const total = r.height + vh;
        const scrolled = vh - r.top;
        prog = scrolled / total;
      } else if (mode === "pin") {
        // progress from 0 (top of el at top of viewport) to 1 (scrolled pinDistance past)
        const top = r.top;
        prog = (-top) / Math.max(1, pinDistance);
      }
      setP(Math.max(0, Math.min(1, prog)));
    };
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => { raf = 0; compute(); });
    };
    compute();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [mode, pinDistance]);
  return p;
}

// ease a 0-1 segment; returns 0 before start, 1 after end, eased in between
function seg(p, start, end, easing = (x) => x) {
  if (p <= start) return 0;
  if (p >= end) return 1;
  const t = (p - start) / (end - start);
  return easing(t);
}
const easeOut = (t) => 1 - Math.pow(1 - t, 3);
const easeInOut = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
const lerp = (a, b, t) => a + (b - a) * t;

// ============ PARALLAX BLOCK — generic scroll-lift wrapper ============
// Wraps children in a div that rises + rotates gently into place as it
// scrolls through the viewport. Used for Signal/Stack/Cinema sections.
function ParallaxBlock({ children, intensity = 1, className = "", id }) {
  const ref = useRef(null);
  const p = useScrollProgress(ref, { mode: "through" });
  // entrance window: 0.02 → 0.28 of the "through" progress (element entering from bottom).
  // Kept tight so reveals finish well before the section is fully in view.
  const enter = seg(p, 0.02, 0.28, easeOut);
  // subtle continued drift 0.28 → 0.9
  const drift = seg(p, 0.28, 0.9, (x) => x);
  const ty = lerp(40, 0, enter) * intensity + lerp(0, -14, drift) * intensity;
  const rx = lerp(8, 0, enter) * intensity;
  // Opacity starts from 0.2 so content is never fully invisible while entering —
  // ramps to 1 quickly. Prevents the "coming really late" feel.
  const op = lerp(0.2, 1, enter);
  return (
    <div
      ref={ref}
      id={id}
      className={"px-block " + className}
      style={{
        transform: `perspective(1800px) translate3d(0, ${ty}px, 0) rotateX(${rx}deg)`,
        opacity: op,
        transformOrigin: "50% 100%",
      }}
    >
      {children}
    </div>
  );
}

// ============ SPINE (left edge timeline) ============
const MILESTONES = [
  { yr: "2014", lbl: "CognitiveScale · Start" },
  { yr: "2019", lbl: "Tech Lead · Marketplace" },
  { yr: "2022", lbl: "Austin · Certifai" },
  { yr: "2023", lbl: "TecnoTree · Staff" },
  { yr: "2024", lbl: "Trustwise · Founding" },
  { yr: "Now",  lbl: "Series A · Principal" },
];

function Spine() {
  const [active, setActive] = useState(MILESTONES.length - 2);
  useEffect(() => {
    const onScroll = () => {
      const sections = ["hero", "sig", "stack", "graph", "ir", "cine", "contact"];
      const y = window.scrollY + window.innerHeight * 0.3;
      let idx = 0;
      sections.forEach((id, i) => {
        const el = document.getElementById(id);
        if (el && el.offsetTop < y) idx = Math.min(MILESTONES.length - 1, Math.round((i / (sections.length - 1)) * (MILESTONES.length - 1)));
      });
      setActive(idx);
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  return (
    <aside className="spine" aria-hidden="true">
      <div className="spine-head">MC</div>
      <div className="timeline">
        {MILESTONES.map((m, i) => (
          <div key={m.yr} className={"node" + (i === active ? " active" : "")}>
            <span className="lbl">{m.lbl}</span>
            <span className="dot" />
            <span className="yr">{m.yr}</span>
          </div>
        ))}
      </div>
      <div className="spine-foot">30.27°N · 97.74°W</div>
    </aside>
  );
}

// ============ TOP BAR + MODE TOGGLE ============
function TopBar({ mode, setMode }) {
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {
    const onS = () => setScrolled(window.scrollY > 30);
    window.addEventListener("scroll", onS, { passive: true });
    return () => window.removeEventListener("scroll", onS);
  }, []);
  return (
    <div className={"topbar" + (scrolled ? " scrolled" : "")}>
      <div className="who">
        <span className="sig">MC</span>
        <span className="name">Mayank Chutani</span>
        <span className="sep">·</span>
        <span className="role">{mode === "eng" ? "Principal Engineer, AI Platform" : "Cinematographer · Field"}</span>
      </div>
      <div className="mode-toggle" role="tablist" aria-label="View mode">
        <div className="slider" />
        <button className={mode === "eng" ? "on" : ""} onClick={() => setMode("eng")}>Engineer</button>
        <button className={mode === "cine" ? "on" : ""} onClick={() => setMode("cine")}>Cinematographer</button>
      </div>
    </div>
  );
}

// ============ COMMIT TICKER ============
function Ticker() {
  const duplicated = [...COMMITS, ...COMMITS];
  return (
    <div className="ticker">
      <span className="tag"><span className="pulse" />Live — origin/main</span>
      <div className="stream">
        <div className="stream-track">
          {duplicated.map((c, i) => (
            <span className="commit" key={i}>
              <span className="sha">{c.sha}</span>{" "}
              <span className="msg">{c.msg}</span>
              <span className="t">· {c.t}</span>
            </span>
          ))}
        </div>
      </div>
      <span className="total"><span className="n">768+</span> commits · #1 · 43%</span>
    </div>
  );
}

Object.assign(window, { Spine, TopBar, Ticker, useReveal, useInView, useCount, useScrollProgress, seg, easeOut, easeInOut, lerp, ParallaxBlock, MILESTONES });
