// neural-crystal.jsx — Three.js neural network wireframe layer.
// Subtle network nodes and connections; never a solid hero foreground object.
// Props: accent (hex), intensity (0..1), power (0..1)

const NeuralCrystal = ({ accent = "#39ff88", intensity = 0.6, power = 0 }) => {
  const mountRef = React.useRef(null);
  const stateRef = React.useRef({ intensity, power, accent });

  React.useEffect(() => {
    stateRef.current = { intensity, power, accent };
  }, [intensity, power, accent]);

  React.useEffect(() => {
    const mount = mountRef.current;
    if (!mount || typeof THREE === "undefined") return;

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(50, mount.offsetWidth / mount.offsetHeight, 0.1, 100);
    camera.position.z = 8;

    let renderer;
    try {
      renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
    } catch {
      return undefined;
    }
    renderer.setSize(mount.offsetWidth, mount.offsetHeight);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 1.5));
    mount.appendChild(renderer.domElement);

    // Neural network sphere — nodes + edges
    const nodeCount = 40;
    const nodes = [];
    const nodeGroup = new THREE.Group();
    const nodeGeo = new THREE.SphereGeometry(0.05, 8, 8);
    const nodeMat = new THREE.MeshBasicMaterial({
      color: new THREE.Color(accent),
      transparent: true,
      opacity: 0.28,
    });

    for (let i = 0; i < nodeCount; i++) {
      const phi = Math.acos(-1 + (2 * i) / nodeCount);
      const theta = Math.sqrt(nodeCount * Math.PI) * phi;
      const x = 2.5 * Math.cos(theta) * Math.sin(phi);
      const y = 2.5 * Math.sin(theta) * Math.sin(phi);
      const z = 2.5 * Math.cos(phi);
      const node = new THREE.Mesh(nodeGeo, nodeMat);
      node.position.set(x, y, z);
      node.userData.basePos = { x, y, z };
      nodeGroup.add(node);
      nodes.push(node);
    }
    scene.add(nodeGroup);

    // Edges — connect nearest neighbors
    const edgeMat = new THREE.LineBasicMaterial({
      color: new THREE.Color(accent),
      transparent: true,
      opacity: 0.3,
    });
    const edgePositions = [];
    for (let i = 0; i < nodeCount; i++) {
      const dists = [];
      for (let j = 0; j < nodeCount; j++) {
        if (i === j) continue;
        const a = nodes[i].position, b = nodes[j].position;
        const d = a.distanceTo(b);
        dists.push([d, j]);
      }
      dists.sort((a, b) => a[0] - b[0]);
      for (let k = 0; k < Math.min(3, dists.length); k++) {
        const j = dists[k][1];
        if (i < j) {
          edgePositions.push(nodes[i].position.x, nodes[i].position.y, nodes[i].position.z);
          edgePositions.push(nodes[j].position.x, nodes[j].position.y, nodes[j].position.z);
        }
      }
    }
    const edgeGeo = new THREE.BufferGeometry();
    edgeGeo.setAttribute("position", new THREE.Float32BufferAttribute(edgePositions, 3));
    const edges = new THREE.LineSegments(edgeGeo, edgeMat);
    nodeGroup.add(edges);

    // Animation state
    const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;
    let raf, time = 0, appliedAccent = accent;

    const fpsScale = () => (window.__fpsScale == null ? 1 : window.__fpsScale);
    const frame = () => (window.__sceneFrame || {});
    const phase = () => (window.Cinematic ? window.Cinematic.phase : "idle");

    const phaseState = () => {
      const current = phase();
      if (current === "void") return { opacity: 0, speed: 0 };
      if (current === "seed") return { opacity: 0.16, speed: 0.16 };
      if (current === "propagate") return { opacity: 0.6, speed: 0.86 };
      if (current === "decode") return { opacity: 1, speed: 0.58 };
      return { opacity: 0.82, speed: 0.26 };
    };

    const animate = () => {
      raf = requestAnimationFrame(animate);
      const sceneState = phaseState();
      const motionSpeed = reduced ? 0 : sceneState.speed;
      const opacity = sceneState.opacity;

      time += 0.008 * motionSpeed;
      const st = stateRef.current;
      const currentFrame = frame();
      const fs = fpsScale();

      if (appliedAccent !== st.accent) {
        appliedAccent = st.accent;
        const accentColor = new THREE.Color(st.accent);
        nodeMat.color.copy(accentColor);
        edgeMat.color.copy(accentColor);
      }

      // Neural sphere orbit + node pulse
      nodeGroup.rotation.y += 0.002 * motionSpeed;
      nodeGroup.rotation.x += 0.0005 * motionSpeed;
      const activeNodes = fs < 1 ? Math.floor(nodeCount * fs) : nodeCount;
      nodes.forEach((node, i) => {
        node.visible = i < activeNodes;
        if (node.visible) {
          const nPulse = 0.6 + 0.4 * Math.sin(time * 3 + i * 0.5);
          node.material.opacity = (0.16 + 0.12 * nPulse) * opacity;
          node.position.y = node.userData.basePos.y + Math.sin(time + i) * 0.1;
        }
      });
      edgeMat.opacity = (0.1 + 0.06 * Math.sin(time * 2)) * opacity;

      renderer.render(scene, camera);
    };

    animate();

    const onResize = () => {
      camera.aspect = mount.offsetWidth / mount.offsetHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(mount.offsetWidth, mount.offsetHeight);
    };
    window.addEventListener("resize", onResize);

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("resize", onResize);
      mount.removeChild(renderer.domElement);
      nodeGeo.dispose(); nodeMat.dispose();
      edgeGeo.dispose(); edgeMat.dispose();
      renderer.dispose();
    };
  }, []);

  return <div ref={mountRef} className="layer layer-crystal" aria-hidden="true" />;
};

window.NeuralCrystal = NeuralCrystal;
