// Hero — custom canvas knowledge graph backdrop + headline.

const buildGraphData = (W, H, isMob) => {
  const links = [];
  const seen = new Set();
  const addLink = (a, b) => {
    const key = [a, b].sort().join('|');
    if (!seen.has(key)) { seen.add(key); links.push({ s: a, t: b }); }
  };

  let seed = 42;
  const rand = () => { seed = (seed * 16807 + 0) % 2147483647; return (seed - 1) / 2147483646; };
  const j = (n) => (rand() - 0.5) * 2 * n;

  // Mobile: hug edges + corners, away from the centered text block (y≈12%–75%).
  // Desktop: wide C on the right 50–90%.
  const hubPos = isMob ? [
    { x: W * 0.08, y: H * 0.05 },  // top-left corner
    { x: W * 0.92, y: H * 0.06 },  // top-right corner
    { x: W * 0.05, y: H * 0.32 },  // left rail, high
    { x: W * 0.95, y: H * 0.50 },  // right rail, mid
    { x: W * 0.06, y: H * 0.70 },  // left rail, low
    { x: W * 0.94, y: H * 0.82 },  // right rail, low
    { x: W * 0.48, y: H * 0.93 },  // bottom center
  ] : [
    { x: W * 0.50, y: H * 0.12 },  // upper-left
    { x: W * 0.72, y: H * 0.18 },  // upper-right
    { x: W * 0.88, y: H * 0.36 },  // far-right top
    { x: W * 0.90, y: H * 0.62 },  // far-right bottom
    { x: W * 0.72, y: H * 0.80 },  // lower-right
    { x: W * 0.50, y: H * 0.88 },  // lower-left
    { x: W * 0.78, y: H * 0.50 },  // center-right (fills arc interior)
  ];

  // Node radii — smaller on mobile so they don't crowd a 375px canvas
  const rHub  = isMob ? 5   : 9;
  const rMid  = isMob ? 3   : 5.5;
  const rLeaf = isMob ? 1.8 : 3.5;

  const hubs = hubPos.map((p, i) => ({
    id: `h${i}`, r: rHub,
    x: p.x + j(20), y: p.y + j(20),
    vx: 0, vy: 0, _cx: p.x, _cy: p.y, _born: Infinity,
    _tx: p.x, _ty: p.y, _ak: 0.003,
  }));

  // Fewer, tighter nodes on mobile to avoid crowding a narrow canvas
  const leafCount = isMob ? 4 : 10;
  const midCount  = isMob ? 1 : 3;
  const leafJit   = isMob ? 28 : 60;
  const midJit    = isMob ? 20 : 45;

  const leaves = hubs.flatMap((h, hi) =>
    Array.from({ length: leafCount }, (_, li) => ({
      id: `l${hi}_${li}`, r: rLeaf,
      x: h._cx + j(leafJit), y: h._cy + j(leafJit),
      vx: 0, vy: 0, _born: Infinity,
      _tx: h._cx, _ty: h._cy, _ak: 0.0008,
    }))
  );

  const mids = hubs.flatMap((h, hi) =>
    Array.from({ length: midCount }, (_, mi) => ({
      id: `m${hi}_${mi}`, r: rMid,
      x: h._cx + j(midJit), y: h._cy + j(midJit),
      vx: 0, vy: 0, _born: Infinity,
      _tx: h._cx, _ty: h._cy, _ak: 0.0015,
    }))
  );

  const nodes = [...hubs, ...mids, ...leaves];

  hubs.forEach((h, hi) => {
    Array.from({ length: midCount }, (_, mi) => addLink(h.id, `m${hi}_${mi}`));
    for (let li = 0; li < leafCount; li++) addLink(h.id, `l${hi}_${li}`);
  });

  hubs.forEach((_, hi) => {
    addLink(`m${hi}_0`, `l${hi}_0`); addLink(`m${hi}_0`, `l${hi}_1`);
    if (midCount > 1) {
      addLink(`m${hi}_1`, `l${hi}_${Math.min(4, leafCount - 1)}`);
      addLink(`m${hi}_1`, `l${hi}_${Math.min(5, leafCount - 1)}`);
    }
    if (midCount > 2) {
      addLink(`m${hi}_2`, `l${hi}_${Math.min(8, leafCount - 1)}`);
      addLink(`m${hi}_2`, `l${hi}_${Math.min(9, leafCount - 1)}`);
    }
  });

  hubs.forEach((_, hi) => {
    addLink(`l${hi}_0`, `l${hi}_${Math.min(2, leafCount - 1)}`);
    if (leafCount > 6) {
      addLink(`l${hi}_3`, `l${hi}_${Math.min(6, leafCount - 1)}`);
      addLink(`l${hi}_7`, `l${hi}_${Math.min(9, leafCount - 1)}`);
    }
  });

  // Spine + cross-braces between hubs
  addLink('h0', 'h1'); addLink('h1', 'h2'); addLink('h2', 'h3');
  addLink('h3', 'h4'); addLink('h4', 'h5');
  addLink('h0', 'h6'); addLink('h6', 'h5');
  addLink('h1', 'h6'); addLink('h6', 'h4');
  addLink('h2', 'h3');

  return { nodes, links };
};

const GraphBackdrop = ({ parallaxY = 0 }) => {
  const wrapRef = React.useRef(null);
  const mouseRef = React.useRef(null);
  const isMob = useIsMobile();

  React.useEffect(() => {
    const wrap = wrapRef.current;
    if (!wrap) return;

    const W = wrap.clientWidth  || (isMob ? 390 : 1440);
    const H = wrap.clientHeight || (isMob ? 844 : 940);
    const { nodes, links } = buildGraphData(W, H, isMob);

    // Build neighbour lookup
    const nodeById = {};
    nodes.forEach(n => { nodeById[n.id] = n; });
    const adj = {};
    nodes.forEach(n => { adj[n.id] = new Set(); });
    links.forEach(l => { adj[l.s].add(l.t); adj[l.t].add(l.s); });

    // Pre-compute degree
    nodes.forEach(n => { n._degree = adj[n.id].size; });

    // Canvas setup
    const canvas = document.createElement('canvas');
    canvas.width = W; canvas.height = H;
    canvas.style.cssText = `position:absolute;inset:0;width:100%;height:100%;`;
    wrap.appendChild(canvas);
    const ctx = canvas.getContext('2d');

    // Physics constants
    const REPULSION = 1600;
    const LINK_DIST = 58;
    const LINK_K = 0.038;
    const DAMPING = 0.80;
    const MIN_DIST = 28;

    const simulate = () => {
      for (let i = 0; i < nodes.length; i++) {
        const a = nodes[i];
        // Repulsion from nearby nodes only
        for (let j2 = i + 1; j2 < nodes.length; j2++) {
          const b = nodes[j2];
          const dx = b.x - a.x, dy = b.y - a.y;
          const d2 = dx * dx + dy * dy;
          const d = Math.sqrt(d2) || 0.01;
          if (d > 280) continue;
          const f = REPULSION / (d2 < MIN_DIST * MIN_DIST ? MIN_DIST * MIN_DIST : d2);
          const fx = (dx / d) * f, fy = (dy / d) * f;
          a.vx -= fx; a.vy -= fy;
          b.vx += fx; b.vy += fy;
        }
        // Per-node anchor spring (keeps clusters in C-zone)
        if (a._tx !== undefined) {
          a.vx += (a._tx - a.x) * a._ak;
          a.vy += (a._ty - a.y) * a._ak;
        }
        // Damping
        a.vx *= DAMPING; a.vy *= DAMPING;
      }
      // Link spring
      links.forEach(l => {
        const a = nodeById[l.s], b = nodeById[l.t];
        if (!a || !b) return;
        const dx = b.x - a.x, dy = b.y - a.y;
        const d = Math.sqrt(dx * dx + dy * dy) || 0.01;
        const f = (d - LINK_DIST) * LINK_K;
        const fx = (dx / d) * f, fy = (dy / d) * f;
        a.vx += fx; a.vy += fy;
        b.vx -= fx; b.vy -= fy;
      });
      // Integrate, clamp to canvas
      nodes.forEach(n => {
        n.x = Math.max(n.r, Math.min(W - n.r, n.x + n.vx));
        n.y = Math.max(n.r, Math.min(H - n.r, n.y + n.vy));
      });
    };

    // Staggered reveal
    let revealTimer = null;
    const triggerReveal = () => {
      if (revealTimer) clearTimeout(revealTimer);
      nodes.forEach(n => { n._born = Infinity; });
      const schedule = [
        { batch: 1, delay: 180 }, { batch: 1, delay: 160 },
        { batch: 1, delay: 160 }, { batch: 1, delay: 160 },
        ...Array.from({ length: 15 }, () => ({ batch: 1, delay: 55 })),
        ...Array.from({ length: 20 }, () => ({ batch: 2, delay: 28 })),
      ];
      let idx = 0, si = 0;
      const step = () => {
        if (idx >= nodes.length) return;
        const { batch, delay } = schedule[si++] || { batch: 2, delay: 30 };
        nodes.slice(idx, idx + batch).forEach(n => { n._born = Date.now(); });
        idx += batch;
        revealTimer = setTimeout(step, delay);
      };
      step();
    };

    // Hover state
    let hovered = null, hovNeighbors = new Set();
    const onMouseMove = (e) => {
      const rect = canvas.getBoundingClientRect();
      const mx = (e.clientX - rect.left) * (W / rect.width);
      const my = (e.clientY - rect.top) * (H / rect.height);
      mouseRef.current = { x: mx, y: my };
      let found = null;
      for (const n of nodes) {
        const dx = mx - n.x, dy = my - n.y;
        if (Math.sqrt(dx*dx+dy*dy) < n.r * 4 && n._degree >= 3) { found = n; break; }
      }
      hovered = found;
      hovNeighbors = found ? adj[found.id] : new Set();
      canvas.style.cursor = found ? 'pointer' : 'default';
    };
    const onMouseLeave = () => { hovered = null; hovNeighbors = new Set(); mouseRef.current = null; canvas.style.cursor = 'default'; };
    canvas.addEventListener('mousemove', onMouseMove);
    canvas.addEventListener('mouseleave', onMouseLeave);

    // Draw
    const draw = () => {
      ctx.clearRect(0, 0, W, H);
      const now = Date.now();
      const hasHov = !!hovered;

      // Links
      links.forEach(l => {
        const a = nodeById[l.s], b = nodeById[l.t];
        if (!a || !b) return;
        const aAge = now - a._born, bAge = now - b._born;
        if (aAge <= 0 || bAge <= 0) return;
        const fade = Math.min(1, Math.min(aAge, bAge) / 600);
        const isActive = hasHov && (l.s === hovered.id || l.t === hovered.id);
        ctx.beginPath();
        ctx.moveTo(a.x, a.y);
        ctx.lineTo(b.x, b.y);
        ctx.strokeStyle = isActive
          ? `rgba(200,135,74,${0.80 * fade})`
          : `rgba(238,232,223,${(hasHov ? 0.05 : 0.18) * fade})`;
        ctx.lineWidth = isActive ? 1.5 : 0.5;
        ctx.stroke();
      });

      // Nodes
      nodes.forEach(n => {
        const age = now - n._born;
        if (age <= 0) return;
        const fade = Math.min(1, age / 600);
        const ease = 1 - Math.pow(1 - fade, 3);
        const isHub = n._degree >= 8;
        const isHov = hovered?.id === n.id;
        const isNeighbor = !isHov && hasHov && hovNeighbors.has(n.id);

        if (isHov) {
          ctx.shadowColor = 'rgba(200,135,74,0.9)';
          ctx.shadowBlur = 22;
          ctx.beginPath();
          ctx.arc(n.x, n.y, n.r * ease * 2.2, 0, Math.PI * 2);
          ctx.fillStyle = 'rgba(200,135,74,0.15)';
          ctx.fill();
          ctx.shadowBlur = 0;
          ctx.beginPath();
          ctx.arc(n.x, n.y, n.r * ease * 1.4, 0, Math.PI * 2);
          ctx.fillStyle = `rgba(200,135,74,${ease})`;
          ctx.fill();
        } else if (isNeighbor) {
          ctx.shadowColor = 'rgba(200,135,74,0.4)';
          ctx.shadowBlur = 8;
          ctx.beginPath();
          ctx.arc(n.x, n.y, n.r * ease * 1.25, 0, Math.PI * 2);
          ctx.fillStyle = `rgba(200,135,74,${0.6 * ease})`;
          ctx.fill();
          ctx.shadowBlur = 0;
        } else {
          ctx.shadowColor = 'rgba(238,232,223,0.35)';
          ctx.shadowBlur = isHub ? 7 : 2;
          ctx.beginPath();
          ctx.arc(n.x, n.y, n.r * ease, 0, Math.PI * 2);
          ctx.fillStyle = hasHov
            ? `rgba(238,232,223,${0.10 * ease})`
            : `rgba(238,232,223,${(isHub ? 0.58 : 0.38) * ease})`;
          ctx.fill();
          ctx.shadowBlur = 0;
        }
      });
    };

    // RAF loop: simulate + draw
    let raf;
    const tick = () => {
      simulate();
      // Ambient drift
      nodes.forEach(n => {
        n.x += (Math.random() - 0.5) * 0.12;
        n.y += (Math.random() - 0.5) * 0.12;
      });
      draw();
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);

    // Reveal after initial physics settle
    const settleTimer = setTimeout(triggerReveal, 900);

    // Scroll re-animation
    const hero = wrap.closest('section') || wrap.parentElement;
    let wasHidden = false;
    const io = new IntersectionObserver(([entry]) => {
      if (!entry.isIntersecting) { wasHidden = true; }
      else if (wasHidden) { wasHidden = false; setTimeout(triggerReveal, 150); }
    }, { threshold: 0.15 });
    io.observe(hero);

    // Resize
    const ro = new ResizeObserver(entries => {
      const { width, height } = entries[0].contentRect;
      canvas.width = width; canvas.height = height;
    });
    ro.observe(wrap);

    return () => {
      clearTimeout(settleTimer);
      if (revealTimer) clearTimeout(revealTimer);
      cancelAnimationFrame(raf);
      canvas.removeEventListener('mousemove', onMouseMove);
      canvas.removeEventListener('mouseleave', onMouseLeave);
      io.disconnect(); ro.disconnect();
      if (canvas.parentNode) canvas.parentNode.removeChild(canvas);
    };
  }, []);

  return (
    <div ref={wrapRef} aria-hidden style={{
      position: 'absolute', inset: 0,
      overflow: 'hidden', zIndex: 0,
      transform: `translate3d(0, ${parallaxY}px, 0)`,
      willChange: 'transform',
    }} />
  );
};

const Hero = () => {
  const t = useTweaks();
  const mob = useIsMobile();
  const scrollY = useParallax(1);
  const amp = (t.parallax || 32) / 100;
  const bgY = -scrollY * amp * 0.5;
  const wordY = -scrollY * amp * 0.35;
  const headY = -scrollY * amp * 0.18;
  const wordmarkOpacity = (t.wordmark ?? 5) / 100;
  const isLeft = t.heroLayout === 'left';

  return (
    <section
      id="top"
      data-screen-label="01 Hero"
      style={{
        position: 'relative', minHeight: '100vh',
        background: '#2C365A', color: '#FFFFFF',
        overflow: 'hidden', display: 'flex', flexDirection: 'column',
      }}
    >
      <GraphBackdrop parallaxY={bgY} />

      <div aria-hidden style={{
        position: 'absolute', inset: 0, pointerEvents: 'none',
        background: mob
          ? 'radial-gradient(ellipse at center, transparent 20%, rgba(28,37,64,0.30) 100%)'
          : 'radial-gradient(ellipse at center, transparent 40%, rgba(28,37,64,0.55) 100%)',
        zIndex: 1,
      }} />

      <div aria-hidden style={{
        position: 'absolute', left: 0, right: 0, top: '50%',
        transform: `translate3d(0, calc(-50% + ${wordY}px), 0)`,
        willChange: 'transform', zIndex: 2, textAlign: 'center',
        fontFamily: 'Clash Display, General Sans, system-ui',
        fontWeight: 700,
        fontSize: 'clamp(140px, 22vw, 360px)',
        lineHeight: 0.86, letterSpacing: '-0.04em',
        textTransform: 'uppercase', color: '#EEE8DF',
        opacity: wordmarkOpacity, whiteSpace: 'nowrap',
        pointerEvents: 'none', userSelect: 'none',
      }}>ANDRI RIVERA</div>

      <div style={{
        position: 'relative', zIndex: 3, flex: 1,
        display: 'flex', flexDirection: 'column', justifyContent: 'center',
        alignItems: isLeft ? 'flex-start' : 'center',
        padding: mob ? '100px 24px 56px' : '160px 32px 120px',
        textAlign: isLeft ? 'left' : 'center',
        maxWidth: 1240, margin: '0 auto', width: '100%',
        transform: `translate3d(0, ${headY}px, 0)`, willChange: 'transform',
        pointerEvents: 'none',
      }}>
        <div style={{
          fontSize: 11, fontWeight: 700, letterSpacing: '0.22em',
          textTransform: 'uppercase', color: '#C4BCB0', marginBottom: mob ? 24 : 36,
          display: 'inline-flex', alignItems: 'center', gap: 12,
        }}>
          <span style={{ width: 28, height: 1, background: 'rgba(196,188,176,0.5)' }} />
          Operations · AI · Systems
        </div>
        <h1 style={{
          margin: 0, fontFamily: 'Clash Display, General Sans, system-ui',
          fontSize: mob ? 'clamp(42px, 12vw, 68px)' : 'clamp(44px, 6.6vw, 104px)',
          fontWeight: 700, lineHeight: 0.98, letterSpacing: '-0.035em',
          color: '#FFFFFF', maxWidth: isLeft ? 900 : 1100, textWrap: 'balance',
        }}>
          I build the systems.<br/>
          <span style={{ color: '#EEE8DF', opacity: 0.72 }}>You run the business.</span>
        </h1>
        <p style={{
          marginTop: mob ? 24 : 36, marginBottom: mob ? 32 : 44,
          fontSize: mob ? '16px' : 'clamp(17px, 1.4vw, 20px)',
          fontWeight: 400, lineHeight: 1.55, maxWidth: 680,
          color: 'rgba(255,255,255,0.78)', textWrap: 'pretty',
        }}>
          Most business owners are losing time and money to work that should run itself.
          I build the back office around your operation and run it monthly so you never
          have to think about it again.
        </p>
        <div style={{
          display: 'flex', gap: 10, flexWrap: 'wrap',
          justifyContent: isLeft ? 'flex-start' : 'center',
          pointerEvents: 'auto',
        }}>
          <CTA size={mob ? 'md' : 'lg'} href="#final-cta">See if it's a fit</CTA>
          <CTA size={mob ? 'md' : 'lg'} variant="ghost-dark" href="#services" withArrow={false}>See what I build</CTA>
        </div>
        {!mob && (
          <div style={{
            marginTop: 48, display: 'flex', gap: 32, flexWrap: 'wrap',
            justifyContent: isLeft ? 'flex-start' : 'center',
            fontSize: 12, fontWeight: 600, letterSpacing: '0.14em',
            textTransform: 'uppercase', color: 'rgba(196,188,176,0.72)',
          }}>
            <span>Free 30-min call</span>
            <span style={{ opacity: 0.3 }}>·</span>
            <span>No commitment</span>
          </div>
        )}
      </div>

      <div aria-hidden style={{
        position: 'absolute', bottom: 28, left: '50%',
        transform: 'translateX(-50%)', zIndex: 3,
        pointerEvents: 'none',
        fontSize: 10, letterSpacing: '0.28em', textTransform: 'uppercase',
        color: 'rgba(238,232,223,0.5)', fontWeight: 700,
        display: mob ? 'none' : 'flex', flexDirection: 'column', alignItems: 'center', gap: 10,
      }}>
        Scroll
        <span style={{
          width: 1, height: 36, background: 'rgba(238,232,223,0.35)',
          animation: 'scrollhint 2.2s cubic-bezier(0.4,0,0.2,1) infinite',
          transformOrigin: 'top',
        }} />
        <style>{`@keyframes scrollhint { 0% { transform: scaleY(0); transform-origin: top; } 50% { transform: scaleY(1); transform-origin: top; } 51% { transform: scaleY(1); transform-origin: bottom; } 100% { transform: scaleY(0); transform-origin: bottom; } }`}</style>
      </div>
    </section>
  );
};

window.Hero = Hero;
