
import React, { useEffect, useRef } from 'react';
import * as d3 from 'd3';
import { Note, NoteCluster, BridgeNote } from '../types';

interface NetworkGraphProps {
  notes: Note[];
  clusters: NoteCluster[];
  bridgeNotes: BridgeNote[];
  onNoteSelect: (id: string) => void;
  selectedClusterId: string | null;
  onClusterSelect: (id: string | null) => void;
}

export const NetworkGraph: React.FC<NetworkGraphProps> = ({ 
  notes, 
  clusters, 
  bridgeNotes,
  onNoteSelect,
  selectedClusterId,
  onClusterSelect
}) => {
  const svgRef = useRef<SVGSVGElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!svgRef.current || !containerRef.current) return;

    const width = containerRef.current.clientWidth;
    const height = containerRef.current.clientHeight;

    const svg = d3.select(svgRef.current);
    svg.selectAll("*").remove();

    const g = svg.append("g");

    const zoom = d3.zoom<SVGSVGElement, unknown>()
      .scaleExtent([0.1, 4])
      .on("zoom", (event) => {
        g.attr("transform", event.transform);
      });

    svg.call(zoom);

    // Filter notes with embeddings and cluster assignments
    const visibleNotes = notes.filter(n => n.embedding && n.clusterId);
    
    interface D3Node extends d3.SimulationNodeDatum {
      id: string;
      title: string;
      clusterId: string;
      color: string;
      isBridge: boolean;
      radius: number;
    }

    interface D3Link extends d3.SimulationLinkDatum<D3Node> {
      source: string;
      target: string;
      strength: number;
    }

    const bridgeSet = new Set(bridgeNotes.map(b => b.noteId));

    const nodes: D3Node[] = visibleNotes.map(n => {
      const cluster = clusters.find(c => c.id === n.clusterId);
      const isBridge = bridgeSet.has(n.id);
      return {
        id: n.id,
        title: n.title,
        clusterId: n.clusterId!,
        color: cluster?.color || '#cbd5e1',
        isBridge,
        radius: isBridge ? 13 : 8
      };
    });

    const links: D3Link[] = [];
    // Only connect strong links
    for (let i = 0; i < visibleNotes.length; i++) {
      for (let j = i + 1; j < visibleNotes.length; j++) {
        const ni = visibleNotes[i];
        const nj = visibleNotes[j];
        
        if (ni.clusterId === nj.clusterId) {
            links.push({ source: ni.id, target: nj.id, strength: 0.5 });
        }
      }
    }

    const simulation = d3.forceSimulation<D3Node>(nodes)
      .force("link", d3.forceLink<D3Node, D3Link>(links).id(d => d.id).distance(110))
      .force("charge", d3.forceManyBody().strength(-220))
      .force("center", d3.forceCenter(width / 2, height / 2))
      .force("collision", d3.forceCollide<D3Node>().radius(d => d.radius + 12));

    // Links
    const link = g.append("g")
      .selectAll("line")
      .data(links)
      .enter()
      .append("line")
      .attr("stroke", "#e2e8f0")
      .attr("stroke-opacity", (d: any) => {
        if (!selectedClusterId) return 0.6;
        const sId = typeof d.source === 'string' ? d.source : (d.source as any).id;
        const tId = typeof d.target === 'string' ? d.target : (d.target as any).id;
        const sourceNote = nodes.find(n => n.id === sId);
        const targetNote = nodes.find(n => n.id === tId);
        return (sourceNote?.clusterId === selectedClusterId && targetNote?.clusterId === selectedClusterId) ? 0.8 : 0.05;
      })
      .attr("stroke-width", 1);

    // Nodes
    const node = g.append("g")
      .selectAll(".node")
      .data(nodes)
      .enter()
      .append("g")
      .attr("class", "node cursor-pointer")
      .on("click", (event, d) => onNoteSelect(d.id))
      .call(d3.drag<SVGGElement, D3Node>()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended) as any);

    // Node opacities based on focus
    node.attr("opacity", d => {
      if (!selectedClusterId) return 1;
      return d.clusterId === selectedClusterId ? 1 : 0.15;
    });

    node.append("circle")
      .attr("r", d => d.radius)
      .attr("fill", d => d.color)
      .attr("stroke", d => d.isBridge ? "#D4AF37" : "#fff")
      .attr("stroke-width", d => d.isBridge ? 3.5 : 2)
      .style("filter", d => d.isBridge ? "drop-shadow(0 0 6px rgba(212, 175, 55, 0.6))" : "none");

    node.append("text")
      .attr("dy", d => d.radius + 14)
      .attr("text-anchor", "middle")
      .attr("class", "text-[10px] fill-concrete dark:fill-concrete/60 font-medium pointer-events-none")
      .text(d => d.title.length > 20 ? d.title.substring(0, 20) + "..." : d.title);

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

      node
        .attr("transform", d => `translate(${d.x},${d.y})`);
    });

    // Zoom transition on cluster highlight
    if (selectedClusterId && width && height) {
      const clusterNodes = nodes.filter(n => n.clusterId === selectedClusterId);
      if (clusterNodes.length > 0) {
        // Run a small tick count synchronously to find coordinates quickly if layout is starting
        for (let i = 0; i < 50; ++i) simulation.tick();

        const xCoords = clusterNodes.map(cn => cn.x).filter((x): x is number => x !== undefined);
        const yCoords = clusterNodes.map(cn => cn.y).filter((y): y is number => y !== undefined);
        
        if (xCoords.length > 0 && yCoords.length > 0) {
          const avgX = d3.mean(xCoords) || width / 2;
          const avgY = d3.mean(yCoords) || height / 2;
          
          svg.transition()
            .duration(800)
            .call(
              zoom.transform,
              d3.zoomIdentity.translate(width / 2, height / 2).scale(1.4).translate(-avgX, -avgY)
            );
        }
      }
    } else {
      svg.transition()
        .duration(800)
        .call(zoom.transform, d3.zoomIdentity);
    }

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

    function dragged(event: any, d: D3Node) {
      d.fx = event.x;
      d.fy = event.y;
    }

    function dragended(event: any, d: D3Node) {
      if (!event.active) simulation.alphaTarget(0);
      d.fx = null;
      d.fy = null;
    }

    return () => simulation.stop();
  }, [notes, clusters, bridgeNotes, onNoteSelect, selectedClusterId]);

  return (
    <div ref={containerRef} className="w-full h-full bg-paper dark:bg-[#121212] rounded-3xl overflow-hidden border border-border/40 relative">
        <div className="absolute top-6 left-6 z-10 flex flex-wrap gap-2 max-w-[90%] sm:max-w-[450px]">
             {clusters.map(c => {
                 const isSelected = selectedClusterId === c.id;
                 return (
                  <button 
                    key={c.id} 
                    onClick={() => onClusterSelect?.(isSelected ? null : c.id)}
                    className={`flex items-center gap-1.5 px-3 py-1.5 rounded-full border shadow-sm transition-all text-[9px] font-bold uppercase tracking-wider
                      ${isSelected 
                        ? 'bg-ink text-white dark:bg-white dark:text-black border-ink dark:border-white scale-105 shadow-md' 
                        : 'bg-white/90 dark:bg-black/80 text-concrete hover:text-ink hover:border-concrete/40 border-border'
                      }`}
                  >
                      <div className="w-1.5 h-1.5 rounded-full" style={{ backgroundColor: c.color }} />
                      <span>{c.name}</span>
                  </button>
                 );
             })}
             {selectedClusterId && (
               <button 
                 onClick={() => onClusterSelect?.(null)}
                 className="px-3 py-1.5 rounded-full border border-rose-200 bg-rose-50 dark:bg-rose-950/20 dark:border-rose-900/40 text-rose-500 text-[9px] font-bold uppercase tracking-wider hover:bg-rose-100 transition-all shadow-sm"
               >
                 Réinitialiser focus
               </button>
             )}
        </div>
        <svg ref={svgRef} className="w-full h-full" />
    </div>
  );
};
