/**
 * ╔═══════════════════════════════════════════════════════════════════════════╗
 * ║                    INTERACTIVE FORCE GRAPH                                 ║
 * ║═══════════════════════════════════════════════════════════════════════════║
 * ║  Dynamic knowledge graph visualization with force-directed layout         ║
 * ║  Part of the Liquid UI Arsenal                                            ║
 * ╚═══════════════════════════════════════════════════════════════════════════╝
 */

import { useRef, useCallback, useState, useEffect } from 'react';
import ForceGraph2D, { ForceGraphMethods, NodeObject, LinkObject } from 'react-force-graph-2d';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { cn } from '@/lib/utils';
import {
  ZoomIn, ZoomOut, Maximize2, RefreshCw,
  MousePointer2, Move, Info
} from 'lucide-react';

export interface GraphNode {
  id: string;
  label: string;
  type?: string;
  color?: string;
  size?: number;
  metadata?: Record<string, unknown>;
}

export interface GraphLink {
  source: string;
  target: string;
  label?: string;
  type?: string;
  weight?: number;
}

export interface InteractiveForceGraphProps {
  nodes: GraphNode[];
  links: GraphLink[];
  title?: string;
  height?: number;
  onNodeClick?: (node: GraphNode) => void;
  onLinkClick?: (link: GraphLink) => void;
  highlightNodes?: string[];
  highlightLinks?: string[];
}

// Color palette for node types
const nodeColors: Record<string, string> = {
  concept: '#3b82f6',    // Blue
  entity: '#10b981',     // Green
  event: '#f59e0b',      // Amber
  person: '#ec4899',     // Pink
  document: '#8b5cf6',   // Purple
  system: '#06b6d4',     // Cyan
  gap: '#ef4444',        // Red
  idea: '#f97316',       // Orange
  default: '#6b7280',    // Gray
};

export function InteractiveForceGraph({
  nodes,
  links,
  title = 'Knowledge Graph',
  height = 400,
  onNodeClick,
  onLinkClick,
  highlightNodes = [],
  highlightLinks = [],
}: InteractiveForceGraphProps) {
  const graphRef = useRef<ForceGraphMethods>();
  const containerRef = useRef<HTMLDivElement>(null);
  const [hoveredNode, setHoveredNode] = useState<GraphNode | null>(null);
  const [selectedNode, setSelectedNode] = useState<GraphNode | null>(null);
  const [dimensions, setDimensions] = useState({ width: 600, height });

  // Update dimensions on resize
  useEffect(() => {
    const updateDimensions = () => {
      if (containerRef.current) {
        setDimensions({
          width: containerRef.current.offsetWidth,
          height: height,
        });
      }
    };

    updateDimensions();
    window.addEventListener('resize', updateDimensions);
    return () => window.removeEventListener('resize', updateDimensions);
  }, [height]);

  // Transform data for react-force-graph
  const graphData = {
    nodes: nodes.map(n => ({
      ...n,
      color: n.color || nodeColors[n.type || 'default'] || nodeColors.default,
      val: n.size || 1,
    })),
    links: links.map(l => ({
      ...l,
      color: highlightLinks.includes(`${l.source}-${l.target}`) ? '#fbbf24' : 'rgba(255,255,255,0.2)',
    })),
  };

  // Node rendering
  const paintNode = useCallback((node: NodeObject, ctx: CanvasRenderingContext2D, globalScale: number) => {
    const gNode = node as NodeObject & GraphNode;
    const label = gNode.label || gNode.id;
    const fontSize = 12 / globalScale;
    const size = (gNode.size || 1) * 4;

    const isHighlighted = highlightNodes.includes(gNode.id as string);
    const isHovered = hoveredNode?.id === gNode.id;
    const isSelected = selectedNode?.id === gNode.id;

    // Node circle
    ctx.beginPath();
    ctx.arc(node.x!, node.y!, size, 0, 2 * Math.PI);
    ctx.fillStyle = gNode.color || nodeColors.default;

    if (isHighlighted || isHovered || isSelected) {
      ctx.shadowColor = gNode.color || '#fff';
      ctx.shadowBlur = 15;
    }

    ctx.fill();
    ctx.shadowBlur = 0;

    // Border for selected/hovered
    if (isSelected) {
      ctx.strokeStyle = '#fff';
      ctx.lineWidth = 2 / globalScale;
      ctx.stroke();
    } else if (isHovered) {
      ctx.strokeStyle = 'rgba(255,255,255,0.5)';
      ctx.lineWidth = 1 / globalScale;
      ctx.stroke();
    }

    // Label
    if (globalScale > 0.5 || isHovered || isSelected) {
      ctx.font = `${fontSize}px ui-monospace, monospace`;
      ctx.textAlign = 'center';
      ctx.textBaseline = 'top';
      ctx.fillStyle = 'rgba(255,255,255,0.9)';
      ctx.fillText(label.substring(0, 20), node.x!, node.y! + size + 2);
    }

    // Type badge
    if ((isHovered || isSelected) && gNode.type) {
      ctx.font = `${fontSize * 0.7}px ui-monospace, monospace`;
      ctx.fillStyle = 'rgba(255,255,255,0.6)';
      ctx.fillText(`[${gNode.type}]`, node.x!, node.y! + size + fontSize + 4);
    }
  }, [highlightNodes, hoveredNode, selectedNode]);

  // Link rendering
  const paintLink = useCallback((link: LinkObject, ctx: CanvasRenderingContext2D, globalScale: number) => {
    const gLink = link as LinkObject & GraphLink;
    const start = link.source as NodeObject;
    const end = link.target as NodeObject;

    if (!start.x || !start.y || !end.x || !end.y) return;

    // Draw line
    ctx.beginPath();
    ctx.moveTo(start.x, start.y);
    ctx.lineTo(end.x, end.y);
    ctx.strokeStyle = gLink.color || 'rgba(255,255,255,0.2)';
    ctx.lineWidth = (gLink.weight || 1) / globalScale;
    ctx.stroke();

    // Draw label if zoomed in
    if (globalScale > 1 && gLink.label) {
      const midX = (start.x + end.x) / 2;
      const midY = (start.y + end.y) / 2;
      const fontSize = 8 / globalScale;

      ctx.font = `${fontSize}px ui-monospace, monospace`;
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillStyle = 'rgba(255,255,255,0.5)';
      ctx.fillText(gLink.label, midX, midY);
    }
  }, []);

  // Handlers
  const handleNodeClick = useCallback((node: NodeObject) => {
    const gNode = node as NodeObject & GraphNode;
    setSelectedNode(gNode);
    onNodeClick?.(gNode);

    // Center on node
    graphRef.current?.centerAt(node.x, node.y, 500);
    graphRef.current?.zoom(2, 500);
  }, [onNodeClick]);

  const handleNodeHover = useCallback((node: NodeObject | null) => {
    setHoveredNode(node as GraphNode | null);
    if (containerRef.current) {
      containerRef.current.style.cursor = node ? 'pointer' : 'grab';
    }
  }, []);

  const handleLinkClick = useCallback((link: LinkObject) => {
    const gLink = link as LinkObject & GraphLink;
    onLinkClick?.(gLink);
  }, [onLinkClick]);

  // Controls
  const zoomIn = () => graphRef.current?.zoom(graphRef.current?.zoom() * 1.5, 300);
  const zoomOut = () => graphRef.current?.zoom(graphRef.current?.zoom() / 1.5, 300);
  const resetView = () => {
    graphRef.current?.zoomToFit(400);
    setSelectedNode(null);
  };

  return (
    <div className="rounded-lg border border-border/30 bg-background/50 overflow-hidden">
      {/* Header */}
      <div className="flex items-center justify-between px-4 py-2 bg-muted/30 border-b border-border/30">
        <div className="flex items-center gap-3">
          <span className="text-sm font-medium">{title}</span>
          <Badge variant="outline" className="text-[10px] font-mono">
            {nodes.length} nodes / {links.length} links
          </Badge>
        </div>
        <div className="flex items-center gap-1">
          <Button variant="ghost" size="sm" onClick={zoomIn} className="h-7 w-7 p-0">
            <ZoomIn className="w-3 h-3" />
          </Button>
          <Button variant="ghost" size="sm" onClick={zoomOut} className="h-7 w-7 p-0">
            <ZoomOut className="w-3 h-3" />
          </Button>
          <Button variant="ghost" size="sm" onClick={resetView} className="h-7 w-7 p-0">
            <Maximize2 className="w-3 h-3" />
          </Button>
        </div>
      </div>

      {/* Graph container */}
      <div ref={containerRef} className="relative bg-black/30" style={{ height }}>
        <ForceGraph2D
          ref={graphRef}
          graphData={graphData}
          width={dimensions.width}
          height={dimensions.height}
          backgroundColor="transparent"
          nodeCanvasObject={paintNode}
          linkCanvasObject={paintLink}
          onNodeClick={handleNodeClick}
          onNodeHover={handleNodeHover}
          onLinkClick={handleLinkClick}
          nodePointerAreaPaint={(node, color, ctx) => {
            const size = ((node as GraphNode).size || 1) * 4 + 4;
            ctx.fillStyle = color;
            ctx.beginPath();
            ctx.arc(node.x!, node.y!, size, 0, 2 * Math.PI);
            ctx.fill();
          }}
          linkDirectionalArrowLength={4}
          linkDirectionalArrowRelPos={1}
          d3AlphaDecay={0.02}
          d3VelocityDecay={0.3}
          warmupTicks={50}
          cooldownTicks={100}
        />

        {/* Node info tooltip */}
        {(hoveredNode || selectedNode) && (
          <div className="absolute bottom-3 left-3 right-3 p-2 bg-black/80 rounded border border-border/30 text-xs">
            <div className="flex items-center gap-2 mb-1">
              <div
                className="w-2 h-2 rounded-full"
                style={{ backgroundColor: (hoveredNode || selectedNode)?.color }}
              />
              <span className="font-medium">{(hoveredNode || selectedNode)?.label}</span>
              {(hoveredNode || selectedNode)?.type && (
                <Badge variant="outline" className="text-[8px]">
                  {(hoveredNode || selectedNode)?.type}
                </Badge>
              )}
            </div>
            <span className="text-muted-foreground font-mono text-[10px]">
              ID: {(hoveredNode || selectedNode)?.id}
            </span>
          </div>
        )}
      </div>

      {/* Legend */}
      <div className="px-4 py-2 bg-muted/20 border-t border-border/30 flex items-center gap-4 flex-wrap text-[10px]">
        {Object.entries(nodeColors).slice(0, 6).map(([type, color]) => (
          <div key={type} className="flex items-center gap-1">
            <div className="w-2 h-2 rounded-full" style={{ backgroundColor: color }} />
            <span className="text-muted-foreground capitalize">{type}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

export default InteractiveForceGraph;
