// hero-sphere.jsx — Three.js wireframe sphere reacting to mouse + scroll
const { useEffect, useRef } = React;

function HeroSphere() {
  const mountRef = useRef(null);
  const stateRef = useRef({ mouseX: 0, mouseY: 0, scrollY: 0 });

  useEffect(() => {
    if (!window.THREE) return;
    const THREE = window.THREE;
    const mount = mountRef.current;
    if (!mount) return;

    const w = mount.clientWidth;
    const h = mount.clientHeight;

    // Mobile-only: throttle to ~30fps and cap DPR so finger touches don't
    // contend with Three.js for main-thread / GPU time. Same visuals.
    const isMobile = window.matchMedia('(max-width: 760px)').matches
                  || window.matchMedia('(pointer: coarse)').matches;

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 100);
    // pull camera back so the sphere never hits the canvas edges
    camera.position.z = 6.5;

    const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, isMobile ? 1.5 : 2));
    renderer.setSize(w, h);
    renderer.setClearColor(0x000000, 0);
    mount.appendChild(renderer.domElement);

    const getAccentHex = () => {
      try {
        const c = getComputedStyle(document.documentElement).getPropertyValue('--accent').trim();
        // canvas conversion forces oklch/lab/etc. into resolved sRGB
        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);
    };
    let accentColor = getAccentHex();

    // sphere — slightly smaller to leave breathing room
    const geo = new THREE.IcosahedronGeometry(1.4, 3);
    const mat = new THREE.MeshBasicMaterial({
      color: accentColor, wireframe: true, transparent: true, opacity: 0.55
    });
    const sphere = new THREE.Mesh(geo, mat);
    scene.add(sphere);

    // inner solid (depth)
    const innerGeo = new THREE.IcosahedronGeometry(1.35, 2);
    const innerMat = new THREE.MeshBasicMaterial({ color: 0x000000, transparent: true, opacity: 0.85 });
    const inner = new THREE.Mesh(innerGeo, innerMat);
    scene.add(inner);

    // outer ring
    const ringGeo = new THREE.TorusGeometry(2.05, 0.005, 16, 200);
    const ringMat = new THREE.MeshBasicMaterial({ color: accentColor, transparent: true, opacity: 0.4 });
    const ring = new THREE.Mesh(ringGeo, ringMat);
    ring.rotation.x = Math.PI / 2.3;
    scene.add(ring);

    // particles
    const pCount = 220;
    const pGeo = new THREE.BufferGeometry();
    const positions = new Float32Array(pCount * 3);
    for (let i = 0; i < pCount; i++) {
      const r = 2.3 + Math.random() * 1.4;
      const theta = Math.random() * Math.PI * 2;
      const phi = Math.acos(2 * Math.random() - 1);
      positions[i*3]   = r * Math.sin(phi) * Math.cos(theta);
      positions[i*3+1] = r * Math.sin(phi) * Math.sin(theta);
      positions[i*3+2] = r * Math.cos(phi);
    }
    pGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    const pMat = new THREE.PointsMaterial({ color: accentColor, size: 0.025, transparent: true, opacity: 0.7 });
    const particles = new THREE.Points(pGeo, pMat);
    scene.add(particles);

    const onMove = (e) => {
      const rect = mount.getBoundingClientRect();
      stateRef.current.mouseX = ((e.clientX - rect.left) / rect.width - 0.5) * 2;
      stateRef.current.mouseY = ((e.clientY - rect.top) / rect.height - 0.5) * 2;
    };
    const onScroll = () => { stateRef.current.scrollY = window.scrollY; };
    window.addEventListener('mousemove', onMove);
    window.addEventListener('scroll', onScroll, { passive: true });

    // Touch drag-to-rotate, scoped to the sphere area (mount element).
    // Gesture detection: on the first significant movement we decide if the
    // user is rotating (horizontal-dominant) or scrolling (vertical-dominant).
    // Vertical scrolls are handled natively by iOS thanks to touch-action: pan-y.
    let touchActive = false;
    let gesture = null; // null | 'rotate' | 'scroll'
    let dragStartX = 0, dragStartY = 0;
    let dragStartRotX = 0, dragStartRotY = 0;
    const DRAG_SENS = 0.012;

    const onTouchStart = (e) => {
      if (!e.touches || e.touches.length === 0) return;
      // Filter: only react if the touch is inside the sphere area
      const r = mount.getBoundingClientRect();
      const t = e.touches[0];
      if (t.clientX < r.left || t.clientX > r.right || t.clientY < r.top || t.clientY > r.bottom) return;
      touchActive = true;
      gesture = null;
      dragStartX = t.clientX;
      dragStartY = t.clientY;
      dragStartRotX = sphere.rotation.x;
      dragStartRotY = sphere.rotation.y;
      stateRef.current.dragRotX = dragStartRotX;
      stateRef.current.dragRotY = dragStartRotY;
      // Bypass the 30fps throttle from the first touch — even before we know
      // if it's a rotate or scroll, full fps prevents the choppy first frame.
      stateRef.current.touchActive = true;
      stateRef.current.dragging = false;
    };
    const onTouchMove = (e) => {
      if (!touchActive || !e.touches || e.touches.length === 0) return;
      const dx = e.touches[0].clientX - dragStartX;
      const dy = e.touches[0].clientY - dragStartY;
      // Decide intent on first non-trivial movement (small threshold so we
      // commit fast — at the top of page iOS Safari delays event delivery
      // while it's still deciding scroll vs not, so we want to lock asap).
      if (gesture === null) {
        if (dx === 0 && dy === 0) return;
        if (Math.abs(dx) >= Math.abs(dy)) {
          gesture = 'rotate';
          stateRef.current.dragging = true;
        } else {
          gesture = 'scroll';
          touchActive = false;
          return;
        }
      }
      if (gesture === 'rotate') {
        stateRef.current.dragRotX = dragStartRotX + dy * DRAG_SENS;
        stateRef.current.dragRotY = dragStartRotY + dx * DRAG_SENS;
      }
    };
    const onTouchEnd = () => {
      touchActive = false;
      gesture = null;
      stateRef.current.dragging = false;
      stateRef.current.touchActive = false;
    };
    // On mobile, the touch handler itself gates by scrollY so iOS Safari's
    // bottom toolbar gesture zone at the top of the page can't interfere.
    document.addEventListener('touchstart', onTouchStart, { passive: true });
    document.addEventListener('touchmove', onTouchMove, { passive: true });
    document.addEventListener('touchend', onTouchEnd, { passive: true });
    document.addEventListener('touchcancel', onTouchEnd, { passive: true });

    const onResize = () => {
      const w2 = mount.clientWidth, h2 = mount.clientHeight;
      camera.aspect = w2 / h2;
      camera.updateProjectionMatrix();
      renderer.setSize(w2, h2);
    };
    window.addEventListener('resize', onResize);

    const updateAccent = () => {
      const c = getAccentHex();
      mat.color.copy(c);
      ringMat.color.copy(c);
      pMat.color.copy(c);
    };
    updateAccent();
    setTimeout(updateAccent, 50);
    setTimeout(updateAccent, 300);
    const accentInterval = setInterval(updateAccent, 500);

    let raf, t = 0, lastFrame = 0;
    const animate = () => {
      raf = requestAnimationFrame(animate);
      // Mobile: throttle to 30fps when idle (saves CPU during scroll), but
      // render at full 60fps while a finger is actively touching — even before
      // we've decided rotate vs scroll — so first contact is never sacadé.
      const interactive = stateRef.current.dragging || stateRef.current.touchActive;
      if (isMobile && !interactive) {
        const now = performance.now();
        if (now - lastFrame < 32) return;
        lastFrame = now;
      } else {
        lastFrame = performance.now();
      }
      t += 0.005;
      const { mouseX, mouseY, scrollY, dragging, dragRotX, dragRotY } = stateRef.current;
      if (dragging) {
        // Direct drag — chase finger target with strong lerp for responsive feel.
        sphere.rotation.x += (dragRotX - sphere.rotation.x) * 0.32;
        sphere.rotation.y += (dragRotY - sphere.rotation.y) * 0.32;
      } else {
        const targetRotX = mouseY * 0.4 + scrollY * 0.0008;
        const targetRotY = mouseX * 0.6 + t * 0.3;
        sphere.rotation.x += (targetRotX - sphere.rotation.x) * 0.06;
        sphere.rotation.y += (targetRotY - sphere.rotation.y) * 0.06;
      }
      inner.rotation.copy(sphere.rotation);
      ring.rotation.z = t * 0.4;
      particles.rotation.y = t * 0.15;
      particles.rotation.x = mouseY * 0.2;

      const s = 1 + Math.sin(t * 1.2) * 0.015;
      sphere.scale.set(s, s, s);

      renderer.render(scene, camera);
    };
    animate();

    return () => {
      cancelAnimationFrame(raf);
      clearInterval(accentInterval);
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onResize);
      if (!isMobile) {
        document.removeEventListener('touchstart', onTouchStart);
        document.removeEventListener('touchmove', onTouchMove);
        document.removeEventListener('touchend', onTouchEnd);
        document.removeEventListener('touchcancel', onTouchEnd);
      }
      try { mount.removeChild(renderer.domElement); } catch(e) {}
      geo.dispose(); mat.dispose();
      innerGeo.dispose(); innerMat.dispose();
      ringGeo.dispose(); ringMat.dispose();
      pGeo.dispose(); pMat.dispose();
      renderer.dispose();
    };
  }, []);

  return <div ref={mountRef} className="hero-canvas" style={{ width: '100%', height: '100%' }} />;
}

window.HeroSphere = HeroSphere;
