// ============ DEPENDENCY GRAPH — projects (scroll-scrubbed + pinned) ============

function Graph({ mode }) {
  const [userTouched, setUserTouched] = useState(false);
  const [hoveredId, setHoveredId] = useState(null);
  const wrapRef = useRef(null);
  const secRef = useRef(null);
  const pinRef = useRef(null);
  const [size, setSize] = useState({ w: 1000, h: 620 });

  // Short pin — just enough scroll budget for the graph build animation (edges drawing, nodes popping)
  // without feeling sticky. Once built, user scrolls straight past.
  const pinDistance = typeof window !== "undefined" ? window.innerHeight * 0.8 : 700;
  const p = useScrollProgress(secRef, { mode: "pin", pinDistance });
  // "entry" progress — drives reveal start while section is still entering viewport
  const pEnter = useScrollProgress(secRef, { mode: "through" });
  const seen = p > 0.02;

  useEffect(() => {
    const onResize = () => {
      if (!wrapRef.current) return;
      const r = wrapRef.current.getBoundingClientRect();
      setSize({ w: r.width, h: r.height });
    };
    onResize();
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, []);

  // Auto-tour removed — user-driven only (hover/click). Default selection highlights core.
  const sel = userTouched && hoveredId ? hoveredId : "core";

  const touch = (id) => { setUserTouched(true); setHoveredId(id); };

  const activeNodes = useMemo(
    () => mode === "cine" ? NODES : NODES.filter((n) => n.kind !== "cine"),
    [mode]
  );
  const activeEdges = useMemo(() => {
    const allowed = new Set(activeNodes.map((n) => n.id));
    return EDGES.filter(([a, b]) => allowed.has(a) && allowed.has(b));
  }, [activeNodes]);

  const hot = useMemo(() => {
    return activeEdges.filter(([a, b]) => a === sel || b === sel)
      .map(([a, b]) => `${a}-${b}`);
  }, [sel, activeEdges]);

  const nodeById = useMemo(() => {
    const m = {};
    activeNodes.forEach((n) => { m[n.id] = n; });
    return m;
  }, [activeNodes]);

  // Head + graph-wrap are always visible once section is in DOM; motion comes from tilt/zoom below.
  const headP = 1;
  const wrapP = 1;

  // 3D tilt that eases back as you scroll through
  const tiltY = lerp(-8, 0, seg(p, 0.02, 0.28, easeOut));
  const tiltX = lerp(14, 0, seg(p, 0.02, 0.28, easeOut));
  const zoom = lerp(0.94, 1.0, seg(p, 0.02, 0.28, easeOut));

  // Edge reveal window — driven mostly by entry (section entering viewport),
  // completes within the first half of the short pin
  const edgeGlobalP = Math.max(seg(pEnter, 0.15, 0.55, (x) => x), seg(p, 0.00, 0.35, (x) => x));
  // Node reveal window — slightly later than edges
  const nodeGlobalP = Math.max(seg(pEnter, 0.20, 0.60, (x) => x), seg(p, 0.00, 0.40, (x) => x));

  return (
    <section id="graph" className="graph-section" ref={secRef}
      style={{ height: `calc(100vh + ${pinDistance}px)` }}>
      <div className="graph-pin" ref={pinRef}>
        <div className="graph-pin-inner">
          <div className="shead"
            style={{
              opacity: headP,
              transform: `translate3d(0, ${lerp(30, 0, headP)}px, 0)`,
            }}>
            <span className="idx">§ 01 · SYSTEMS</span>
            <span className="t">Work Graphified</span>
            <span className="meta">{activeNodes.length} NODES · {activeEdges.length} EDGES{mode === "cine" ? " · 1 OUTLIER" : ""}</span>
          </div>

          <div className="graph-wrap"
            style={{
              opacity: wrapP,
              transform: `perspective(1600px) translate3d(0, ${lerp(40, 0, wrapP)}px, 0) rotateX(${tiltX}deg) rotateY(${tiltY}deg) scale(${zoom})`,
              transformOrigin: "50% 50%",
            }}>
            <div className="graph" ref={wrapRef}>
              <svg className="edges" viewBox={`0 0 ${size.w} ${size.h}`} preserveAspectRatio="none">
                {activeEdges.map(([a, b], i) => {
                  const na = nodeById[a], nb = nodeById[b];
                  if (!na || !nb) return null;
                  const x1 = (na.x / 100) * size.w;
                  const y1 = (na.y / 100) * size.h;
                  const x2 = (nb.x / 100) * size.w;
                  const y2 = (nb.y / 100) * size.h;
                  const mx = (x1 + x2) / 2;
                  const my = (y1 + y2) / 2 - 20;
                  const isHot = hot.includes(`${a}-${b}`);
                  // stagger: each edge starts at a slightly later global progress
                  const localStart = i / activeEdges.length;
                  const draw = Math.max(0, Math.min(1, (edgeGlobalP - localStart * 0.5) * 2));
                  return (
                    <path key={`${a}-${b}`}
                      d={`M ${x1} ${y1} Q ${mx} ${my} ${x2} ${y2}`}
                      className={isHot ? "hot" : ""}
                      style={{
                        strokeDasharray: 600,
                        strokeDashoffset: 600 - draw * 600,
                        opacity: draw > 0 ? 1 : 0,
                      }} />
                  );
                })}
              </svg>

              {[
                { txt: "↳ PLATFORM", style: { left: "4%", top: "6%" }, start: 0.1 },
                { txt: "↳ SAFETY", style: { left: "4%", top: "54%" }, start: 0.14 },
                { txt: "↳ DEV / AGENTS", style: { left: "68%", top: "54%" }, start: 0.18 },
                { txt: "↳ OUTLIER / FILM", style: { right: "4%", top: "6%", textAlign: "right" }, start: 0.22 },
              ].map((c, i) => {
                const cp = seg(p, c.start, c.start + 0.1, easeOut);
                return (
                  <span className="cluster-lbl" key={i}
                    style={{ ...c.style, opacity: cp, transform: `translate3d(0, ${lerp(-10, 0, cp)}px, 0)` }}>
                    {c.txt}
                  </span>
                );
              })}

              {activeNodes.map((n, i) => {
                // each node emerges at slightly different scroll pos
                const localStart = i / activeNodes.length;
                const np = Math.max(0, Math.min(1, (nodeGlobalP - localStart * 0.4) * 2.5));
                const scale = lerp(0.4, 1, easeOut(np));
                const opacity = np;
                return (
                  <button
                    key={n.id}
                    className={"node kind-" + n.kind + (sel === n.id ? " active" : "")}
                    style={{
                      left: `${n.x}%`, top: `${n.y}%`,
                      opacity,
                      transform: `translate(-50%, -50%) scale(${scale})`,
                    }}
                    onMouseEnter={() => touch(n.id)}
                    onMouseLeave={() => setHoveredId(null)}
                    onClick={() => touch(n.id)}
                  >
                    <span className="dot" />
                    <span>{n.label}</span>
                    <span className="id">· {n.id.toUpperCase()}</span>
                  </button>
                );
              })}
            </div>

            <div className="graph-legend">
              <div className="keys">
                <span className="key"><i style={{ background: "var(--accent)" }} />PLATFORM</span>
                <span className="key"><i style={{ background: "oklch(0.7 0.18 28)" }} />SAFETY</span>
                <span className="key"><i style={{ background: "oklch(0.72 0.15 145)" }} />DEV / AGENTS</span>
                {mode === "cine" && <span className="key"><i style={{ background: "oklch(0.68 0.18 300)" }} />CINE</span>}
              </div>
              <span>{userTouched ? "node pinned · hover elsewhere to switch" : "hover a node to trace dependencies · click to pin"}</span>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============ FEED: public-work / LinkedIn-style timeline ============
function Incident() {
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current) return;
    const steps = ref.current.querySelectorAll(".f-post");
    const obs = new IntersectionObserver(
      (entries) => entries.forEach((e) => e.isIntersecting && e.target.classList.add("in")),
      { threshold: 0.18, rootMargin: "0px 0px -8% 0px" }
    );
    steps.forEach((s) => obs.observe(s));
    return () => obs.disconnect();
  }, []);

  return (
    <section className="feed reveal" id="feed" ref={ref}>
      <div className="shead">
        <span className="idx">§ 04 · IN PUBLIC</span>
        <span className="t">Building in public</span>
        <span className="meta">LINKEDIN · ONGOING</span>
      </div>

      <div className="feed-wrap">
        <div className="feed-rail" aria-hidden="true" />

        {FEED.map((p, i) => (
          <article className="f-post" key={i}>
            <div className="f-date">
              <span className="dot" />
              <span className="rel">{p.rel}</span>
              <span className="tag">{p.tag}</span>
            </div>

            <div className="f-card">
              {p.headline && <h4 className="f-h">{p.headline}</h4>}

              {p.diff && (
                <div className="f-diff">
                  <span className="plus">✅ +{p.diff.add}</span>
                  <span className="minus">🔻 −{p.diff.rem}</span>
                  <span className="f-diff-sub">{p.diff.sub}</span>
                </div>
              )}

              <div className="f-body">
                {p.body.map((para, j) => <p key={j}>{para}</p>)}
              </div>

              {p.pull && (
                <blockquote className="f-pull">{p.pull}</blockquote>
              )}

              {p.code && (
                <pre className="f-code">{p.code}</pre>
              )}

              <div className="f-foot">
                <div className="f-reacts">
                  <span>▲ {p.likes}</span>
                  <span>◼ {p.comments}</span>
                  <span>↗ {p.reposts}</span>
                </div>
                <a className="f-link" href="https://www.linkedin.com/in/mayankchutani/" target="_blank" rel="noopener noreferrer">
                  View on LinkedIn →
                </a>
              </div>
            </div>
          </article>
        ))}

        <div className="f-end">
          <span>↳ FEED CONTINUES · follow along</span>
          <a href="https://www.linkedin.com/in/mayankchutani/" target="_blank" rel="noopener noreferrer">linkedin.com/in/mayankchutani →</a>
        </div>
      </div>
    </section>
  );
}

Object.assign(window, { Graph, Incident });
