import { useRef, useState, useCallback, useEffect } from 'react';
import type { GlobalToken } from 'antd/es/theme/interface';
import type { GraphNode, GraphEdge, NodePosition, HoverState } from '../graph/graphTypes';
import { computeCircularLayout } from '../graph/graphLayout';
import {
  getEdgeColor,
  NODE_HOVER_SCALE,
  getRelColor,
  getNodeDegree,
  degreeToRadius,
  drawCurvedEdge,
  drawNode,
  drawEdgeLabel,
  drawNodeLabel,
} from '../graph/graphRenderer';

interface UseGraphCanvasParams {
  token: GlobalToken;
  canvasSize: { width: number; height: number };
  selectedCenter: string | null;
  visibleNodes: GraphNode[];
  visibleEdges: GraphEdge[];
}

export function useGraphCanvas({
  token,
  canvasSize,
  selectedCenter,
  visibleNodes,
  visibleEdges,
}: UseGraphCanvasParams) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const nodePositionsRef = useRef<Map<string, NodePosition>>(new Map());
  const visibleNodesRef = useRef<GraphNode[]>(visibleNodes);
  const visibleEdgesRef = useRef<GraphEdge[]>(visibleEdges);
  const [hoverState, setHoverState] = useState<HoverState>({ nodeId: null, x: 0, y: 0 });

  useEffect(() => {
    visibleNodesRef.current = visibleNodes;
    visibleEdgesRef.current = visibleEdges;
  }, [visibleNodes, visibleEdges]);

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

    const nodes = visibleNodesRef.current;
    const edges = visibleEdgesRef.current;
    const width = canvasSize.width;
    const height = canvasSize.height;

    const dpr = window.devicePixelRatio || 1;
    canvas.width = width * dpr;
    canvas.height = height * dpr;
    canvas.style.width = `${width}px`;
    canvas.style.height = `${height}px`;
    ctx.scale(dpr, dpr);

    const textColor = token.colorText;
    const bgColor = token.colorBgContainer;

    ctx.clearRect(0, 0, width, height);
    ctx.fillStyle = bgColor;
    ctx.fillRect(0, 0, width, height);

    if (nodes.length === 0) return;

    const centerX = width / 2;
    const centerY = height / 2;
    const radius = Math.min(width, height) * 0.36;
    const positions = computeCircularLayout(nodes, centerX, centerY, radius);
    nodePositionsRef.current = positions;

    const degreeMap = new Map<string, number>();
    for (const node of nodes) {
      degreeMap.set(node.id, getNodeDegree(node.id, edges));
    }

    for (const edge of edges) {
      const from = positions.get(edge.source);
      const to = positions.get(edge.target);
      if (!from || !to) continue;

      const colors = getRelColor(edge.label);
      const isHighlighted = hoverState.nodeId === edge.source || hoverState.nodeId === edge.target;
      const alpha = hoverState.nodeId ? (isHighlighted ? 1 : 0.15) : 0.7;
      const lw = isHighlighted ? 2.5 : 1.5;

      const labelPos = drawCurvedEdge(ctx, from.x, from.y, to.x, to.y, colors.base, lw, isHighlighted, alpha);
      if (edge.label && labelPos) {
        const labelAlpha = hoverState.nodeId ? (isHighlighted ? 1 : 0.1) : 0.9;
        drawEdgeLabel(ctx, labelPos.labelX, labelPos.labelY - 10, edge.label, colors.base, labelAlpha);
      }
    }

    for (const node of nodes) {
      const pos = positions.get(node.id);
      if (!pos) continue;

      const isCenter = node.id === selectedCenter;
      const isHovered = node.id === hoverState.nodeId;
      const degree = degreeMap.get(node.id) || 0;
      const r = degreeToRadius(degree, isCenter);

      let nodeColorBase = '#2563eb';
      let nodeColorLight = '#60a5fa';
      let nodeColorGlow = 'rgba(79,70,229,0.3)';

      if (isCenter) {
        const firstEdge = edges.find((e) => e.source === node.id || e.target === node.id);
        if (firstEdge) {
          const rc = getRelColor(firstEdge.label);
          nodeColorBase = rc.base;
          nodeColorLight = rc.light;
          nodeColorGlow = rc.glow;
        }
      } else {
        const idx = nodes.indexOf(node);
        const pick = getEdgeColor(`_node_${idx}`);
        nodeColorBase = pick.base;
        nodeColorLight = pick.light;
        nodeColorGlow = pick.glow;
      }

      const nodeAlpha = hoverState.nodeId
        ? (isHovered || edges.some(
            (e) => (e.source === hoverState.nodeId && e.target === node.id) ||
                   (e.target === hoverState.nodeId && e.source === node.id),
          ) ? 1 : 0.2)
        : 1;

      drawNode(ctx, pos.x, pos.y, r, nodeColorBase, nodeColorLight, nodeColorGlow, isCenter, isHovered, nodeAlpha);
      drawNodeLabel(ctx, pos.x, pos.y, r, node.label, textColor, isCenter, isHovered);
    }

    if (hoverState.nodeId) {
      const hoveredNode = nodes.find((n) => n.id === hoverState.nodeId);
      if (hoveredNode) {
        const degree = degreeMap.get(hoveredNode.id) || 0;
        const tooltipText = `${hoveredNode.label} (${degree} 条关系)`;
        ctx.save();
        ctx.font = '12px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
        const metrics = ctx.measureText(tooltipText);
        const tw = metrics.width + 16;
        const th = 28;
        const tx = hoverState.x - tw / 2;
        const ty = hoverState.y - 40;

        ctx.fillStyle = token.colorBgElevated;
        ctx.shadowColor = 'rgba(0,0,0,0.15)';
        ctx.shadowBlur = 8;
        ctx.beginPath();
        ctx.roundRect(tx, ty, tw, th, 6);
        ctx.fill();

        ctx.shadowBlur = 0;
        ctx.fillStyle = token.colorText;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(tooltipText, hoverState.x, ty + th / 2);
        ctx.restore();
      }
    }
  }, [canvasSize, selectedCenter, hoverState, token]);

  useEffect(() => { drawGraph(); }, [drawGraph]);

  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;
    const observer = new ResizeObserver(() => { drawGraph(); });
    observer.observe(container);
    return () => observer.disconnect();
  }, [drawGraph]);

  const handleCanvasMouseMove = useCallback(
    (e: React.MouseEvent<HTMLCanvasElement>) => {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const rect = canvas.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      const positions = nodePositionsRef.current;
      const nodes = visibleNodesRef.current;
      const edges = visibleEdgesRef.current;

      let foundId: string | null = null;
      for (const node of nodes) {
        const pos = positions.get(node.id);
        if (!pos) continue;
        const degree = getNodeDegree(node.id, edges);
        const r = degreeToRadius(degree, node.id === selectedCenter) * NODE_HOVER_SCALE;
        const dx = x - pos.x;
        const dy = y - pos.y;
        if (dx * dx + dy * dy < r * r) { foundId = node.id; break; }
      }

      canvas.style.cursor = foundId ? 'pointer' : 'default';
      setHoverState((prev) =>
        prev.nodeId === foundId ? prev : { nodeId: foundId, x, y },
      );
    },
    [selectedCenter],
  );

  const handleCanvasMouseLeave = useCallback(() => {
    setHoverState({ nodeId: null, x: 0, y: 0 });
  }, []);

  const handleCanvasClick = useCallback(
    (e: React.MouseEvent<HTMLCanvasElement>) => {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const rect = canvas.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      const positions = nodePositionsRef.current;
      const nodes = visibleNodesRef.current;
      const edges = visibleEdgesRef.current;

      for (const node of nodes) {
        const pos = positions.get(node.id);
        if (!pos) continue;
        const degree = getNodeDegree(node.id, edges);
        const r = degreeToRadius(degree, node.id === selectedCenter);
        const dx = x - pos.x;
        const dy = y - pos.y;
        if (dx * dx + dy * dy < r * r) {
          return { clicked: node.id };
        }
      }
      return { clicked: null };
    },
    [selectedCenter],
  );

  return {
    canvasRef,
    containerRef,
    hoverState,
    drawGraph,
    handleCanvasMouseMove,
    handleCanvasMouseLeave,
    handleCanvasClick,
  };
}
