<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>3D Force-Directed Graph</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        #graph-container {
            width: 100%;
            height: 100vh;
        }
    </style>
</head>
<body>
    <div id="graph-container"></div>

    <script>
        // Fetch the graph data from the Flask server
        fetch('/data')
            .then(response => response.json())
            .then(data => {
                const nodes = data.nodes.map(node => ({
                    ...node,
                    x: Math.random() * 1000 - 500,
                    y: Math.random() * 1000 - 500,
                    z: Math.random() * 1000 - 500
                }));
                const links = data.edges;

                // Create the force-directed graph using D3.js
                const container = document.getElementById('graph-container');
                const width = container.clientWidth;
                const height = container.clientHeight;

                const simulation = d3.forceSimulation(nodes)
                    .force("link", d3.forceLink(links).id(d => d.id))
                    .force("charge", d3.forceManyBody().strength(-50))
                    .force("x", d3.forceX(width / 2))
                    .force("y", d3.forceY(height / 2));

                const svg = d3.select(container)
                    .append('svg')
                    .attr('width', width)
                    .attr('height', height);

                const link = svg.append('g')
                    .attr('class', 'links')
                    .selectAll('line')
                    .data(links)
                    .enter().append('line')
                    .style("stroke-width", function(d) { return Math.sqrt(d.value); });

                const node = svg.append('g')
                    .attr('class', 'nodes')
                    .selectAll('circle')
                    .data(nodes)
                    .enter().append('circle')
                    .attr('r', 5)
                    .attr('fill', '#69b3a2')
                    .call(d3.drag()
                        .on("start", dragstarted)
                        .on("drag", dragged)
                        .on("end", dragended));

                node.append('title')
                    .text(d => d.id);

                simulation.on("tick", () => {
                    link
                        .attr("x1", d => d.source.x)
                        .attr("y1", d => d.source.y)
                        .attr("z1", d => d.source.z)
                        .attr("x2", d => d.target.x)
                        .attr("y2", d => d.target.y)
                        .attr("z2", d => d.target.z);

                    node
                        .attr("cx", d => d.x)
                        .attr("cy", d => d.y)
                        .attr("cz", d => d.z);
                });

                function dragstarted(event, d) {
                    if (!event.active) simulation.alphaTarget(0.3).restart();
                    d.fx = d.x;
                    d.fy = d.y;
                    d.fz = d.z;
                }

                function dragged(event, d) {
                    d.fx = event.x;
                    d.fy = event.y;
                    d.fz = event.z;
                }

                function dragended(event, d) {
                    if (!event.active) simulation.alphaTarget(0);
                    d.fx = null;
                    d.fy = null;
                    d.fz = null;
                }
            });
    </script>
</body>
</html>
