import { useEffect, useState, useRef, useCallback } from "react";
import ForceGraph3D from "react-force-graph-3d";
import graphData from "../data.json";
import SpriteText from "https://esm.sh/three-spritetext";

export default function GraphComponent({ selectedCategory }) {
  const fgRef = useRef(null);
  const [selectedNode, setSelectedNode] = useState(null);
  const [graphDataState, setGraphDataState] = useState({ nodes: [], links: [] });
  const nodePositions = useRef({}); // Store node positions across re-renders

  useEffect(() => {
    const filteredNodes = selectedCategory
      ? graphData.nodes.filter((node) => node.category === selectedCategory)
      : graphData.nodes;

    const filteredNodeIds = new Set(filteredNodes.map((node) => node.id));
    const filteredLinks = graphData.links.filter(
      (link) => filteredNodeIds.has(link.source) && filteredNodeIds.has(link.target)
    );

    // Preserve node positions when switching categories
    filteredNodes.forEach((node) => {
      if (nodePositions.current[node.id]) {
        node.x = nodePositions.current[node.id].x;
        node.y = nodePositions.current[node.id].y;
        node.z = nodePositions.current[node.id].z;
      }
    });

    setGraphDataState({ nodes: filteredNodes, links: filteredLinks });
  }, [selectedCategory]);

  useEffect(() => {
    if (!fgRef.current) return; // ✅ Prevent errors if ref isn't available

    // Wait until ForceGraph3D is fully initialized
    const timeout = setTimeout(() => {
      if (!fgRef.current) return;

      fgRef.current.d3Force("charge").strength(-120); // Adjust node clustering

      // Save node positions when the graph stabilizes
      const handleEngineStop = () => {
        const nodes = fgRef.current.graphData().nodes;
        nodes.forEach((node) => {
          nodePositions.current[node.id] = { x: node.x, y: node.y, z: node.z };
        });
      };

      fgRef.current.onEngineStop(handleEngineStop); // ✅ Correct way to use engineStop event
    }, 500); // Slight delay ensures ref is available

    return () => clearTimeout(timeout); // Cleanup timeout on unmount
  }, [graphDataState]); // Runs when graph data changes

  const handleNodeClick = useCallback((node) => {
    setSelectedNode(node);
  }, []);

  const handleBackgroundClick = useCallback(() => {
    setSelectedNode(null);
  }, []);

  return (
    <div className="relative">
      {selectedNode && (
        <div
          className="bg-[rgba(255,255,255,0.1)] rounded-lg shadow-md z-10 
          absolute left-[50%] top-[50%] transform-gpu -translate-x-1/2 -translate-y-1/2 max-h-[70vh] p-4 pb-13"
        >
          <h3 className="text-lg font-bold mb-2">{selectedNode.name}</h3>
          <button
            className="absolute top-0 right-0 p-2 pr-4 text-[rgba(255,255,255,0.5)] cursor-pointer"
            onClick={() => setSelectedNode(null)}
          >
            Close
          </button>
          <div className="overflow-y-scroll max-h-[100%] w-[65ch] rounded-sm">
            {selectedNode.image && (
              <img
                className="rounded-sm w-full object-contain mb-4"
                src={selectedNode.image}
                alt={selectedNode.name}
              />
            )}
            <p className="text-sm">{selectedNode.description}</p>
          </div>
        </div>
      )}

      <ForceGraph3D
        ref={fgRef}
        graphData={graphDataState}
        nodeAutoColorBy="group"
        nodeThreeObject={(node) => {
          const sprite = new SpriteText(node.name);
          sprite.color = node.color;
          sprite.textHeight = 8;
          return sprite;
        }}
        onNodeClick={handleNodeClick}
        onBackgroundClick={handleBackgroundClick}
      />
    </div>
  );
}
