// dashboard/src/components/CitationGalaxyCanvas.tsx
import { useEffect, useRef } from 'react';
import type { CitationNetwork } from '../types';

interface CanvasProps {
  networks: CitationNetwork[];
  activeNetwork: CitationNetwork;
  nodeLimit: number;
  onNodeClick: (bibcode: string) => void;
}

interface Node {
  id: string;
  label: string;
  x: number;
  y: number;
  vx: number;
  vy: number;
  radius: number;
  color: string;
  type: 'center' | 'reference' | 'citation';
  inDb: boolean;
}

interface Link {
  source: string;
  target: string;
}

export function CitationGalaxyCanvas({ networks, activeNetwork, nodeLimit, onNodeClick }: CanvasProps) {
  const canvasRef = useRef<HTMLCanvasElement | null>(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    const dpr = window.devicePixelRatio || 1;
    const rect = canvas.getBoundingClientRect();
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    ctx.scale(dpr, dpr);

    const MAX_NODES = nodeLimit;
    const allIds = new Set<string>();
    const nodes: Node[] = [];
    const links: Link[] = [];

    networks.forEach((net, netIdx) => {
      const isActive = net.bibcode === activeNetwork.bibcode;
      if (!allIds.has(net.bibcode) && nodes.length < MAX_NODES) {
        allIds.add(net.bibcode);
        // 根据被引用数量决定中心节点大小 (起步半径 16，按被引量上限 32 比例缩放)
        const citeCount = net.citation_count || 0;
        const radius = isActive 
          ? Math.min(32, Math.max(18, 16 + citeCount / 50)) 
          : Math.min(24, Math.max(12, 11 + citeCount / 100));
        nodes.push({
          id: net.bibcode,
          label: net.bibcode,
          x: rect.width / 2 + (netIdx === 0 ? 0 : (Math.random() - 0.5) * 200),
          y: rect.height / 2 + (netIdx === 0 ? 0 : (Math.random() - 0.5) * 200),
          vx: 0,
          vy: 0,
          radius,
          color: isActive ? '#0284c7' : '#475569',
          type: 'center',
          inDb: true,
        });
      }

      net.references.forEach((ref, idx) => {
        if (nodes.length >= MAX_NODES) return;
        if (!allIds.has(ref)) {
          allIds.add(ref);
          const angle = (idx / Math.max(1, net.references.length)) * Math.PI * 2;
          const dist = 120 + Math.random() * 30;
          const centerNode = nodes.find(n => n.id === net.bibcode);
          const inDb = activeNetwork.citation_counts ? Object.prototype.hasOwnProperty.call(activeNetwork.citation_counts, ref) : false;
          nodes.push({
            id: ref,
            label: ref,
            x: (centerNode?.x ?? rect.width / 2) + Math.cos(angle) * dist,
            y: (centerNode?.y ?? rect.height / 2) + Math.sin(angle) * dist,
            vx: 0,
            vy: 0,
            radius: 8, // 初始值，稍后按连线数重算
            color: '#d97706',
            type: 'reference',
            inDb,
          });
        }
        if (allIds.has(ref)) {
          links.push({ source: ref, target: net.bibcode });
        }
      });

      net.citations.forEach((cit, idx) => {
        if (nodes.length >= MAX_NODES) return;
        if (!allIds.has(cit)) {
          allIds.add(cit);
          const angle = (idx / Math.max(1, net.citations.length)) * Math.PI * 2 + Math.PI;
          const dist = 140 + Math.random() * 40;
          const centerNode = nodes.find(n => n.id === net.bibcode);
          const inDb = activeNetwork.citation_counts ? Object.prototype.hasOwnProperty.call(activeNetwork.citation_counts, cit) : false;
          nodes.push({
            id: cit,
            label: cit,
            x: (centerNode?.x ?? rect.width / 2) + Math.cos(angle) * dist,
            y: (centerNode?.y ?? rect.height / 2) + Math.sin(angle) * dist,
            vx: 0,
            vy: 0,
            radius: 8, // 初始值，稍后按连线数重算
            color: '#0891b2',
            type: 'citation',
            inDb,
          });
        }
        if (allIds.has(cit)) {
          links.push({ source: net.bibcode, target: cit });
        }
      });
    });

    // 计算外围节点在当前渲染网络中的度数（连线数），动态微调其半径大小，凸显网络枢纽节点
    const degrees: Record<string, number> = {};
    links.forEach(l => {
      degrees[l.source] = (degrees[l.source] || 0) + 1;
      degrees[l.target] = (degrees[l.target] || 0) + 1;
    });

    nodes.forEach(n => {
      if (n.type !== 'center') {
        const deg = degrees[n.id] || 0;
        const citeCount = activeNetwork.citation_counts?.[n.id] || 0;
        // 融合数据库中该文献的被引数量 (起步+citeCount/40) 与当前渲染网格连线度数，动态决定半径 (最大限制 18)
        n.radius = Math.min(18, Math.max(6, 6 + citeCount / 40 + Math.min(6, deg * 1.5)));
      }
    });

    let offsetX = 0;
    let offsetY = 0;
    let scale = 1.0;
    let isDragging = false;
    let dragStartX = 0;
    let dragStartY = 0;
    let hasDragged = false;

    let animationFrameId: number;
    let hoveredNode: Node | null = null;
    let frameCount = 0;

    const updatePhysics = () => {
      for (let i = 0; i < nodes.length; i++) {
        for (let j = i + 1; j < nodes.length; j++) {
          let dx = nodes[j].x - nodes[i].x;
          let dy = nodes[j].y - nodes[i].y;
          let dist = Math.sqrt(dx * dx + dy * dy) || 1;
          let minDist = nodes[i].radius + nodes[j].radius + 60;
          if (dist < minDist) {
            let force = (minDist - dist) * 0.08;
            let fx = (dx / dist) * force;
            let fy = (dy / dist) * force;

            if (nodes[i].type !== 'center' || nodes[i].id !== activeNetwork.bibcode) {
              nodes[i].vx -= fx;
              nodes[i].vy -= fy;
            }
            if (nodes[j].type !== 'center' || nodes[j].id !== activeNetwork.bibcode) {
              nodes[j].vx += fx;
              nodes[j].vy += fy;
            }
          }
        }
      }

      links.forEach(link => {
        const sourceNode = nodes.find(n => n.id === link.source);
        const targetNode = nodes.find(n => n.id === link.target);
        if (sourceNode && targetNode) {
          let dx = targetNode.x - sourceNode.x;
          let dy = targetNode.y - sourceNode.y;
          let dist = Math.sqrt(dx * dx + dy * dy) || 1;
          let force = dist * 0.003;
          let fx = (dx / dist) * force;
          let fy = (dy / dist) * force;

          if (sourceNode.type !== 'center' || sourceNode.id !== activeNetwork.bibcode) {
            sourceNode.vx += fx;
            sourceNode.vy += fy;
          }
          if (targetNode.type !== 'center' || targetNode.id !== activeNetwork.bibcode) {
            targetNode.vx -= fx;
            targetNode.vy -= fy;
          }
        }
      });

      nodes.forEach(node => {
        if (node.id !== activeNetwork.bibcode) {
          node.x += node.vx;
          node.y += node.vy;
          node.vx *= 0.85;
          node.vy *= 0.85;
        }
      });
    };

    const render = () => {
      frameCount++;
      updatePhysics();

      ctx.clearRect(0, 0, rect.width, rect.height);

      ctx.save();
      // 应用拖拽和缩放的坐标变换
      const cx = rect.width / 2;
      const cy = rect.height / 2;
      ctx.translate(cx + offsetX, cy + offsetY);
      ctx.scale(scale, scale);
      ctx.translate(-cx, -cy);

      // 绘制背景宇宙引力线 & 刻度圈
      const centerNode = nodes.find(n => n.id === activeNetwork.bibcode);
      if (centerNode) {
        ctx.strokeStyle = 'rgba(148, 163, 184, 0.15)';
        ctx.lineWidth = 1;
        ctx.setLineDash([4, 6]);
        
        ctx.beginPath();
        ctx.arc(centerNode.x, centerNode.y, 130, 0, Math.PI * 2);
        ctx.stroke();

        ctx.beginPath();
        ctx.arc(centerNode.x, centerNode.y, 180, 0, Math.PI * 2);
        ctx.stroke();

        // 动态圈
        ctx.strokeStyle = 'rgba(2, 132, 199, 0.06)';
        ctx.setLineDash([]);
        const pulseRadius = 130 + (frameCount % 120) * 0.4;
        ctx.beginPath();
        ctx.arc(centerNode.x, centerNode.y, pulseRadius, 0, Math.PI * 2);
        ctx.stroke();
      }

      // 绘制连线
      ctx.lineWidth = 1;
      links.forEach(link => {
        const sourceNode = nodes.find(n => n.id === link.source);
        const targetNode = nodes.find(n => n.id === link.target);
        if (sourceNode && targetNode) {
          ctx.beginPath();
          ctx.moveTo(sourceNode.x, sourceNode.y);
          ctx.lineTo(targetNode.x, targetNode.y);
          ctx.strokeStyle = 'rgba(148, 163, 184, 0.25)';
          ctx.stroke();
        }
      });

      // 绘制节点
      nodes.forEach(node => {
        const isHovered = hoveredNode?.id === node.id;
        
        ctx.save();
        ctx.globalAlpha = node.inDb ? 1.0 : 0.35; // 未入库文献透明度降为 0.35

        ctx.beginPath();
        ctx.arc(node.x, node.y, node.radius + (isHovered ? 6 : 3), 0, Math.PI * 2);
        ctx.fillStyle = node.color + (isHovered ? '25' : '0f');
        ctx.fill();

        ctx.beginPath();
        ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
        ctx.fillStyle = node.color;
        ctx.fill();

        ctx.beginPath();
        ctx.arc(node.x, node.y, node.radius + (isHovered ? 4 : 2), 0, Math.PI * 2);
        ctx.strokeStyle = node.color + '40'; 
        ctx.lineWidth = 1;
        ctx.stroke();

        ctx.fillStyle = isHovered ? '#0284c7' : '#334155';
        ctx.font = isHovered ? 'bold 10px sans-serif' : '9px sans-serif';
        ctx.textAlign = 'center';
        ctx.fillText(node.label, node.x, node.y + node.radius + (isHovered ? 16 : 12));

        ctx.restore();
      });

      ctx.restore();

      animationFrameId = requestAnimationFrame(render);
    };

    render();

    const handleMouseDown = (e: MouseEvent) => {
      isDragging = true;
      dragStartX = e.clientX - offsetX;
      dragStartY = e.clientY - offsetY;
      hasDragged = false;
    };

    const handleMouseMove = (e: MouseEvent) => {
      const rect = canvas.getBoundingClientRect();
      const mouseX = e.clientX - rect.left;
      const mouseY = e.clientY - rect.top;

      if (isDragging) {
        const dx = e.clientX - dragStartX;
        const dy = e.clientY - dragStartY;
        if (Math.sqrt((dx - offsetX) ** 2 + (dy - offsetY) ** 2) > 3) {
          hasDragged = true;
        }
        offsetX = dx;
        offsetY = dy;
      }

      const cx = rect.width / 2;
      const cy = rect.height / 2;
      const gx = (mouseX - cx - offsetX) / scale + cx;
      const gy = (mouseY - cy - offsetY) / scale + cy;

      let found: Node | null = null;
      for (const node of nodes) {
        let dx = node.x - gx;
        let dy = node.y - gy;
        let dist = Math.sqrt(dx * dx + dy * dy);
        if (dist < node.radius + 6) {
          found = node;
          break;
        }
      }
      hoveredNode = found;
      
      if (isDragging) {
        canvas.style.cursor = 'grabbing';
      } else {
        canvas.style.cursor = found ? 'pointer' : 'grab';
      }
    };

    const handleMouseUp = () => {
      isDragging = false;
    };

    const handleMouseLeave = () => {
      isDragging = false;
    };

    const handleCanvasClick = () => {
      if (hasDragged) return;
      if (hoveredNode && hoveredNode.id !== activeNetwork.bibcode) {
        onNodeClick(hoveredNode.id);
      }
    };

    const handleWheel = (e: WheelEvent) => {
      e.preventDefault();
      const rect = canvas.getBoundingClientRect();
      const mouseX = e.clientX - rect.left;
      const mouseY = e.clientY - rect.top;
      const cx = rect.width / 2;
      const cy = rect.height / 2;
      const gx = (mouseX - cx - offsetX) / scale + cx;
      const gy = (mouseY - cy - offsetY) / scale + cy;

      if (e.deltaY < 0) {
        scale = Math.min(5.0, scale * 1.1);
      } else {
        scale = Math.max(0.15, scale / 1.1);
      }

      offsetX = mouseX - cx - (gx - cx) * scale;
      offsetY = mouseY - cy - (gy - cy) * scale;
    };

    canvas.addEventListener('mousedown', handleMouseDown);
    canvas.addEventListener('mousemove', handleMouseMove);
    canvas.addEventListener('mouseup', handleMouseUp);
    canvas.addEventListener('mouseleave', handleMouseLeave);
    canvas.addEventListener('click', handleCanvasClick);
    canvas.addEventListener('wheel', handleWheel, { passive: false });

    return () => {
      cancelAnimationFrame(animationFrameId);
      canvas.removeEventListener('mousedown', handleMouseDown);
      canvas.removeEventListener('mousemove', handleMouseMove);
      canvas.removeEventListener('mouseup', handleMouseUp);
      canvas.removeEventListener('mouseleave', handleMouseLeave);
      canvas.removeEventListener('click', handleCanvasClick);
      canvas.removeEventListener('wheel', handleWheel);
    };
  }, [networks, activeNetwork, onNodeClick]);

  return (
    <canvas
      ref={canvasRef}
      className="w-full h-full min-h-[450px]"
      style={{ display: 'block' }}
    />
  );
}
