// rocket-scroll.jsx — Rocket orbits each card in 3D (front + behind passes)
const { useEffect: _reffect, useRef: _rref } = React;

function RocketScroll() {
  const frontRef = _rref(null);
  const backRef = _rref(null);
  const stateRef = _rref({ scrollY: 0, mouseX: 0, mouseY: 0 });

  _reffect(() => {
    if (!window.THREE) return;
    const THREE = window.THREE;
    const frontMount = frontRef.current;
    const backMount = backRef.current;
    if (!frontMount || !backMount) return;

    let w = window.innerWidth, h = window.innerHeight;

    // Two scenes (front + back) sharing the same camera so we can layer the rocket above/below cards
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(50, w / h, 0.1, 100);
    camera.position.z = 7;

    const makeRenderer = (mount) => {
      const r = new THREE.WebGLRenderer({ alpha: true, antialias: true });
      r.setPixelRatio(Math.min(window.devicePixelRatio, 2));
      r.setSize(w, h);
      r.setClearColor(0x000000, 0);
      mount.appendChild(r.domElement);
      return r;
    };
    const rendererFront = makeRenderer(frontMount);
    const rendererBack = makeRenderer(backMount);

    // CRITICAL: the portfolio cards live inside .portfolio-track which has
    // transform (creates a stacking context). To render BEHIND the cards we
    // must move our back layer INTO that same stacking context.
    const portfolioPin = document.querySelector('.portfolio-pin-inner');
    if (portfolioPin) {
      backMount.style.position = 'absolute';
      backMount.style.zIndex = '0';
      backMount.style.top = '0';
      backMount.style.left = '0';
      backMount.style.width = '100%';
      backMount.style.height = '100%';
      // ensure pin-inner can host absolute children
      const cs = getComputedStyle(portfolioPin);
      if (cs.position === 'static') portfolioPin.style.position = 'relative';
      portfolioPin.appendChild(backMount);
      // Also: the .portfolio-track has transform → its own stacking context.
      // We need the back canvas BELOW the track. Forcing track to position:relative
      // with z-index:1 makes its stacking context paint above our z:0 back canvas,
      // while individual cards (z:10) inside track still beat the front layer? No:
      // front layer is z:50 at body level (outside any portfolio context) so front wins.
      const track = portfolioPin.querySelector('.portfolio-track');
      if (track) {
        track.style.position = 'relative';
        track.style.zIndex = '1';
      }
    }

    const getAccentHex = () => {
      try {
        const c = getComputedStyle(document.documentElement).getPropertyValue('--accent').trim();
        const cv = document.createElement('canvas'); cv.width = cv.height = 1;
        const cx = cv.getContext('2d');
        cx.fillStyle = '#000'; cx.fillStyle = c;
        cx.fillRect(0, 0, 1, 1);
        const [r, g, b] = cx.getImageData(0, 0, 1, 1).data;
        return new THREE.Color(r/255, g/255, b/255);
      } catch (e) {}
      return new THREE.Color(0xf5f5f5);
    };

    // BUTTERFLY
    const rocket = new THREE.Group(); // kept variable name for downstream code

    // body — slim ellipsoid
    const bodyGeo = new THREE.SphereGeometry(0.08, 16, 12);
    const bodyMat = new THREE.MeshBasicMaterial({ color: 0x111418 });
    const body = new THREE.Mesh(bodyGeo, bodyMat);
    body.scale.set(0.6, 1.6, 0.6);
    rocket.add(body);
    // body wire (so accent shows on body)
    const bodyWire = new THREE.Mesh(
      bodyGeo,
      new THREE.MeshBasicMaterial({ color: getAccentHex(), wireframe: true, transparent: true, opacity: 0.55 })
    );
    bodyWire.scale.set(0.6, 1.6, 0.6);
    rocket.add(bodyWire);

    // antennae
    const stripeMat = new THREE.MeshBasicMaterial({ color: getAccentHex() }); // reused name
    const antennaGeo = new THREE.CylinderGeometry(0.005, 0.005, 0.18, 6);
    const antL = new THREE.Mesh(antennaGeo, stripeMat);
    antL.position.set(-0.03, 0.18, 0); antL.rotation.z = 0.4; rocket.add(antL);
    const antR = new THREE.Mesh(antennaGeo, stripeMat);
    antR.position.set(0.03, 0.18, 0); antR.rotation.z = -0.4; rocket.add(antR);

    // wing shape — top wing (large) and bottom wing (small)
    const makeWingShape = (big) => {
      const s = new THREE.Shape();
      if (big) {
        // upper wing: rounded leaf
        s.moveTo(0, 0);
        s.bezierCurveTo(0.05, 0.55, 0.7, 0.65, 0.75, 0.25);
        s.bezierCurveTo(0.78, 0.05, 0.55, -0.05, 0.3, -0.05);
        s.bezierCurveTo(0.15, -0.05, 0.05, -0.02, 0, 0);
      } else {
        // lower wing: smaller drop
        s.moveTo(0, 0);
        s.bezierCurveTo(0.05, -0.3, 0.5, -0.5, 0.55, -0.2);
        s.bezierCurveTo(0.58, -0.05, 0.4, 0, 0.2, 0);
        s.bezierCurveTo(0.1, 0, 0.04, 0, 0, 0);
      }
      return s;
    };

    const wingFillMat = new THREE.MeshBasicMaterial({
      color: getAccentHex(), side: THREE.DoubleSide, transparent: true, opacity: 0.85
    });
    const wingEdgeMat = new THREE.MeshBasicMaterial({
      color: 0xffffff, side: THREE.DoubleSide, transparent: true, opacity: 0.6, wireframe: true
    });
    // expose for accent updates
    const portMat = wingFillMat; // alias for accent updater
    const finMat = wingFillMat; // alias for accent updater

    const wings = new THREE.Group();
    rocket.add(wings);

    const buildSide = (sign) => {
      const sideGroup = new THREE.Group();
      const upperGeo = new THREE.ShapeGeometry(makeWingShape(true));
      const lowerGeo = new THREE.ShapeGeometry(makeWingShape(false));
      const upper = new THREE.Mesh(upperGeo, wingFillMat);
      const upperEdge = new THREE.Mesh(upperGeo, wingEdgeMat);
      const lower = new THREE.Mesh(lowerGeo, wingFillMat);
      const lowerEdge = new THREE.Mesh(lowerGeo, wingEdgeMat);
      sideGroup.add(upper); sideGroup.add(upperEdge);
      sideGroup.add(lower); sideGroup.add(lowerEdge);
      // mirror for left side
      sideGroup.scale.x = sign;
      // pivot at body — wings hinge on x=0
      return sideGroup;
    };
    const wingR = buildSide(1);
    const wingL = buildSide(-1);
    wings.add(wingR);
    wings.add(wingL);

    // dummy refs for old code (flame trail still flickers behind butterfly subtly)
    const flameMat = new THREE.MeshBasicMaterial({
      color: getAccentHex(), transparent: true, opacity: 0.0, side: THREE.DoubleSide
    });
    const flame = new THREE.Mesh(new THREE.ConeGeometry(0.001, 0.001, 4), flameMat);
    rocket.add(flame);
    const flameInner = new THREE.Mesh(
      new THREE.ConeGeometry(0.001, 0.001, 4),
      new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0 })
    );
    rocket.add(flameInner);

    rocket.scale.set(1.6, 1.6, 1.6);
    rocket.visible = false;
    scene.add(rocket);

    // TRAIL
    const TRAIL_MAX = 220;
    const trailGeo = new THREE.BufferGeometry();
    const trailPos = new Float32Array(TRAIL_MAX * 3);
    const trailLife = new Float32Array(TRAIL_MAX);
    for (let i = 0; i < TRAIL_MAX; i++) { trailPos[i*3]=trailPos[i*3+1]=trailPos[i*3+2]=1000; trailLife[i]=0; }
    trailGeo.setAttribute('position', new THREE.BufferAttribute(trailPos, 3));
    const trailMat = new THREE.PointsMaterial({
      color: getAccentHex(), size: 0.05, transparent: true, opacity: 0.55, sizeAttenuation: true
    });
    const trail = new THREE.Points(trailGeo, trailMat);
    scene.add(trail);
    let trailIdx = 0;

    const onMove = (e) => {
      stateRef.current.mouseX = (e.clientX / window.innerWidth - 0.5) * 2;
      stateRef.current.mouseY = (e.clientY / window.innerHeight - 0.5) * 2;
    };
    const onScroll = () => { stateRef.current.scrollY = window.scrollY; };
    const onResize = () => {
      w = window.innerWidth; h = window.innerHeight;
      camera.aspect = w / h; camera.updateProjectionMatrix();
      rendererFront.setSize(w, h);
      rendererBack.setSize(w, h);
    };
    window.addEventListener('mousemove', onMove);
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onResize);

    const updateAccent = () => {
      const c = getAccentHex();
      bodyWire.material.color.copy(c);
      stripeMat.color.copy(c);
      wingFillMat.color.copy(c);
      portMat.color.copy(c);
      finMat.color.copy(c);
      flameMat.color.copy(c);
      trailMat.color.copy(c);
    };
    // run once now in case CSS variable was applied between init and this point
    updateAccent();
    // and again after a tick (covers React effect order during first paint)
    setTimeout(updateAccent, 50);
    setTimeout(updateAccent, 300);
    const accentInterval = setInterval(updateAccent, 500);

    const screenToWorld = (sx, sy) => {
      const ndcX = (sx / w) * 2 - 1;
      const ndcY = -((sy / h) * 2 - 1);
      const v = new THREE.Vector3(ndcX, ndcY, 0.5);
      v.unproject(camera);
      const dir = v.sub(camera.position).normalize();
      const dist = -camera.position.z / dir.z;
      return camera.position.clone().add(dir.multiplyScalar(dist));
    };

    let raf, t = 0;
    const animate = () => {
      t += 0.016;
      const { mouseX, mouseY } = stateRef.current;

      const pin = document.querySelector('.portfolio-pin');
      const cards = Array.from(document.querySelectorAll('.portfolio-card'));
      let active = false;
      let progress = 0;

      if (pin) {
        const rect = pin.getBoundingClientRect();
        const totalScroll = pin.offsetHeight - h;
        if (rect.top <= 0 && rect.bottom >= h && totalScroll > 0) {
          progress = Math.min(Math.max(-rect.top / totalScroll, 0), 1);
          active = true;
        } else if (rect.top > 0 && rect.top < h * 0.8) {
          progress = 0; active = true;
        } else if (rect.bottom < h && rect.bottom > 0) {
          progress = 1; active = true;
        }
      }

      const fadeTarget = active ? 1 : 0;
      bodyWire.material.opacity += (fadeTarget * 0.5 - bodyWire.material.opacity) * 0.1;
      flameMat.opacity += (fadeTarget * (0.7 + Math.sin(t * 20) * 0.15) - flameMat.opacity) * 0.15;
      trailMat.opacity += (fadeTarget * 0.75 - trailMat.opacity) * 0.1;
      rocket.visible = bodyWire.material.opacity > 0.02 || fadeTarget > 0;

      const cardCount = cards.length || 3;
      let target = new THREE.Vector3(0, 0, 0);

      if (cards.length > 0) {
        const seg = 1 / cardCount;
        const cardIdx = Math.min(Math.floor(progress / seg), cardCount - 1);
        const localProg = (progress - cardIdx * seg) / seg;

        const card = cards[cardIdx];
        const rect = card.getBoundingClientRect();
        const cx = rect.left + rect.width / 2;
        const cy = rect.top + rect.height / 2;
        const center = screenToWorld(cx, cy);

        const edgeRight = screenToWorld(rect.right + 60, cy);
        const radius = Math.abs(edgeRight.x - center.x);

        // VERTICAL motion: butterfly sweeps top → bottom around each card,
        // staying in FRONT of the card the whole time (no behind/front swap).
        const edgeBottom = screenToWorld(cx, rect.bottom + 60);
        const vRadius = Math.abs(edgeBottom.y - center.y);
        const angle = -Math.PI / 2 + localProg * Math.PI * 6;
        const verticalSweep = (localProg - 0.5) * vRadius * 2.0;
        target.y = center.y - verticalSweep + Math.sin(angle * 0.5) * vRadius * 0.25;
        target.x = center.x + Math.cos(angle) * vRadius * 0.55;
        // Z stays positive → always in front of the card
        target.z = 1.5 + Math.abs(Math.sin(angle)) * 0.5;

        if (localProg > 0.85 && cardIdx < cardCount - 1) {
          const nextRect = cards[cardIdx + 1].getBoundingClientRect();
          const nextCenter = screenToWorld(
            nextRect.left + nextRect.width / 2,
            nextRect.top + nextRect.height / 2
          );
          const blend = (localProg - 0.85) / 0.15;
          target.x = target.x * (1 - blend) + nextCenter.x * blend;
          target.y = target.y * (1 - blend) + nextCenter.y * blend;
          target.z = target.z * (1 - blend) + 1.5 * blend;
        }

        target.x += mouseX * 0.2;
        target.y += mouseY * -0.15;
      }

      rocket.position.x += (target.x - rocket.position.x) * 0.12;
      rocket.position.y += (target.y - rocket.position.y) * 0.12;
      rocket.position.z += (target.z - rocket.position.z) * 0.12;

      const vx = target.x - rocket.position.x;
      const vy = target.y - rocket.position.y;
      const vz = target.z - rocket.position.z;
      const dir = new THREE.Vector3(vx, vy, vz);
      if (dir.lengthSq() > 0.0001) {
        dir.normalize();
        const targetQuat = new THREE.Quaternion();
        // butterfly: body forward = +Y axis; align body with motion direction
        targetQuat.setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir);
        rocket.quaternion.slerp(targetQuat, 0.15);
        // gentle banking roll based on horizontal velocity
        const bank = new THREE.Quaternion();
        bank.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.sin(t * 0.8) * 0.3);
        rocket.quaternion.multiply(bank);
      }

      // wing flap — fast oscillation
      const flap = Math.sin(t * 18);
      // wings rotate around Y axis (folding toward viewer / away)
      // right wing positive sign, left wing negative
      wingR.rotation.y = -flap * 0.9;
      wingL.rotation.y = flap * 0.9;
      // tiny scale pulse so wings feel alive
      const wingPulse = 1 + Math.sin(t * 18) * 0.04;
      wings.scale.set(wingPulse, wingPulse, wingPulse);
      // hide unused flame meshes
      flame.scale.set(0.001, 0.001, 0.001);
      flameInner.scale.set(0.001, 0.001, 0.001);

      if (rocket.visible && fadeTarget > 0.5) {
        const localTail = new THREE.Vector3(0, -0.95, 0).applyQuaternion(rocket.quaternion).add(rocket.position);
        trailPos[trailIdx*3]   = localTail.x + (Math.random()-0.5)*0.1;
        trailPos[trailIdx*3+1] = localTail.y + (Math.random()-0.5)*0.1;
        trailPos[trailIdx*3+2] = localTail.z + (Math.random()-0.5)*0.1;
        trailLife[trailIdx] = 1.0;
        trailIdx = (trailIdx + 1) % TRAIL_MAX;
      }
      for (let i = 0; i < TRAIL_MAX; i++) {
        if (trailLife[i] > 0) {
          trailLife[i] -= 0.012;
          if (trailLife[i] <= 0) { trailPos[i*3]=trailPos[i*3+1]=trailPos[i*3+2]=1000; }
        }
      }
      trailGeo.attributes.position.needsUpdate = true;

      // ── Render twice: once in front layer (z-index above cards),
      //    once in back layer (z-index behind cards). The z position
      //    determines which one we actually show via opacity.
      // Rocket Z > 0  → in front of the card plane
      // Rocket Z < 0  → behind the card plane
      const inFront = rocket.position.z >= 0;
      frontMount.style.opacity = inFront ? '1' : '0';
      backMount.style.opacity = inFront ? '0' : '1';
      // Slight scale by depth for realism
      const depthScale = 1.2 + rocket.position.z * 0.05;
      rocket.scale.set(depthScale, depthScale, depthScale);

      rendererFront.render(scene, camera);
      rendererBack.render(scene, camera);
      raf = requestAnimationFrame(animate);
    };
    animate();

    return () => {
      cancelAnimationFrame(raf);
      clearInterval(accentInterval);
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onResize);
      try { frontMount.removeChild(rendererFront.domElement); } catch(e) {}
      try { backMount.removeChild(rendererBack.domElement); } catch(e) {}
      rendererFront.dispose();
      rendererBack.dispose();
    };
  }, []);

  return (
    <>
      {/* Behind cards: zIndex 2 (lower than cards' z-index 10) */}
      <div
        ref={backRef}
        style={{
          position: 'fixed', inset: 0, pointerEvents: 'none', zIndex: 2,
          transition: 'opacity 0.1s'
        }}
      />
      {/* In front of cards */}
      <div
        ref={frontRef}
        style={{
          position: 'fixed', inset: 0, pointerEvents: 'none', zIndex: 50,
          transition: 'opacity 0.1s'
        }}
      />
    </>
  );
}

window.RocketScroll = RocketScroll;
