import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { buildGraphLayout } from './edit/variableUtils';

import './StudioRunNodeGraph.css';

const NODE_STATUS_LABEL = {
  pending: '待执行',
  active: '进行中',
  completed: '已完成',
  skipped: '已跳过',
};

function defaultEdgePath(from, to, nodeW, nodeH) {
  const x1 = from.x + nodeW / 2;
  const y1 = from.y + nodeH;
  const x2 = to.x + nodeW / 2;
  const y2 = to.y;
  const dy = Math.max(24, (y2 - y1) * 0.45);
  return `M ${x1} ${y1} C ${x1} ${y1 + dy}, ${x2} ${y2 - dy}, ${x2} ${y2}`;
}

function StudioRunNodeGraph({
  pipeline,
  nodeStates,
  currentNodeId,
  variant = 'sidebar',
  onNodeSelect,
  canSelectNode,
}) {
  const containerRef = useRef(null);
  const [view, setView] = useState({ x: 0, y: 0, scale: 1 });
  const [dragging, setDragging] = useState(null);
  const [selectedId, setSelectedId] = useState(currentNodeId);
  const [nodeOverrides, setNodeOverrides] = useState({});

  const isCenter = variant === 'center';

  const layout = useMemo(
    () => buildGraphLayout(pipeline, nodeStates, { vertical: true }),
    [pipeline, nodeStates]
  );

  const { nodeW, nodeH } = layout;

  useEffect(() => {
    setSelectedId(currentNodeId);
  }, [currentNodeId]);

  useEffect(() => {
    const el = containerRef.current;
    if (!el || !layout.width) return;
    const cw = el.clientWidth || 200;
    const ch = el.clientHeight || 160;
    const scale = Math.min(1, (cw - 16) / layout.width, (ch - 16) / layout.height);
    setView({
      x: (cw - layout.width * scale) / 2,
      y: Math.max(8, (ch - layout.height * scale) / 2),
      scale: Math.max(0.45, scale),
    });
  }, [layout.width, layout.height, pipeline, variant]);

  const nodeMap = useMemo(
    () => Object.fromEntries(layout.nodes.map((n) => [n.id, n])),
    [layout.nodes]
  );

  const stateMap = useMemo(
    () => Object.fromEntries((nodeStates || []).map((s) => [s.nodeId, s])),
    [nodeStates]
  );

  const isNodeSelectable = useCallback(
    (nodeId) => {
      if (!onNodeSelect) return false;
      if (nodeId === currentNodeId) return false;
      const state = stateMap[nodeId];
      if (canSelectNode) return canSelectNode(state);
      return state?.skillId === 'studio.worldbook_entry';
    },
    [onNodeSelect, currentNodeId, stateMap, canSelectNode]
  );

  const handleWheel = useCallback((e) => {
    e.preventDefault();
    const delta = e.deltaY > 0 ? 0.92 : 1.08;
    setView((v) => ({
      ...v,
      scale: Math.min(2.5, Math.max(0.35, v.scale * delta)),
    }));
  }, []);

  const handleBgMouseDown = useCallback(
    (e) => {
      if (e.button !== 0) return;
      e.preventDefault();
      setDragging({ type: 'pan', startX: e.clientX, startY: e.clientY, origin: { ...view } });
    },
    [view]
  );

  const handleNodeMouseDown = useCallback(
    (e, nodeId) => {
      if (e.button !== 0) return;
      e.stopPropagation();
      setSelectedId(nodeId);
      const node = nodeMap[nodeId];
      if (!node) return;
      setDragging({
        type: 'node',
        nodeId,
        startX: e.clientX,
        startY: e.clientY,
        origin: { x: node.x, y: node.y },
        moved: false,
      });
    },
    [nodeMap]
  );

  useEffect(() => {
    if (!dragging) return undefined;

    const onMove = (e) => {
      if (dragging.type === 'pan') {
        setView((v) => ({
          ...v,
          x: dragging.origin.x + (e.clientX - dragging.startX),
          y: dragging.origin.y + (e.clientY - dragging.startY),
        }));
        return;
      }
      if (dragging.type === 'node') {
        const dx = e.clientX - dragging.startX;
        const dy = e.clientY - dragging.startY;
        if (Math.abs(dx) > 4 || Math.abs(dy) > 4) {
          dragging.moved = true;
        }
        const scaleDx = dx / view.scale;
        const scaleDy = dy / view.scale;
        setNodeOverrides((prev) => ({
          ...prev,
          [dragging.nodeId]: {
            x: dragging.origin.x + scaleDx,
            y: dragging.origin.y + scaleDy,
          },
        }));
      }
    };

    const onUp = () => {
      if (
        dragging?.type === 'node'
        && !dragging.moved
        && onNodeSelect
        && isNodeSelectable(dragging.nodeId)
      ) {
        onNodeSelect(dragging.nodeId);
      }
      setDragging(null);
    };

    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
    };
  }, [dragging, view.scale, onNodeSelect, isNodeSelectable]);

  useEffect(() => {
    setNodeOverrides({});
  }, [pipeline, nodeStates]);

  const positionedNodes = layout.nodes.map((n) => ({
    ...n,
    ...(nodeOverrides[n.id] || {}),
  }));

  const posMap = Object.fromEntries(positionedNodes.map((n) => [n.id, n]));

  const pathFn = layout.edgePath || defaultEdgePath;

  if (!layout.nodes.length) {
    return <div className="studio-run-graph-empty">暂无节点</div>;
  }

  return (
    <div
      ref={containerRef}
      className={`studio-run-graph${isCenter ? ' studio-run-graph--center' : ''}`}
      onWheel={handleWheel}
      onMouseDown={handleBgMouseDown}
    >
      <svg
        className="studio-run-graph__svg"
        width="100%"
        height="100%"
        aria-label="节点依赖进度图"
      >
        <g transform={`translate(${view.x},${view.y}) scale(${view.scale})`}>
          <defs>
            <marker
              id="studio-run-graph-arrow"
              markerWidth="8"
              markerHeight="8"
              refX="4"
              refY="4"
              orient="auto"
            >
              <path d="M0,0 L0,8 L8,4 z" fill="var(--color-text-muted)" />
            </marker>
          </defs>
          {layout.edges.map(({ from, to }) => {
            const a = posMap[from];
            const b = posMap[to];
            if (!a || !b) return null;
            return (
              <path
                key={`${from}-${to}`}
                className="studio-run-graph__edge"
                d={pathFn(a, b, nodeW, nodeH)}
                markerEnd="url(#studio-run-graph-arrow)"
              />
            );
          })}
          {positionedNodes.map((node) => {
            const selectable = isNodeSelectable(node.id);
            return (
            <g
              key={node.id}
              transform={`translate(${node.x},${node.y})`}
              className={`studio-run-graph__node status-${node.status}${selectedId === node.id ? ' is-selected' : ''}${currentNodeId === node.id ? ' is-current' : ''}${selectable ? ' is-selectable' : ' is-locked'}`}
              onMouseDown={(e) => handleNodeMouseDown(e, node.id)}
            >
              <rect
                className="studio-run-graph__node-bg"
                width={nodeW}
                height={nodeH}
                rx="8"
              />
              <text
                className="studio-run-graph__node-label"
                x={nodeW / 2}
                y={nodeH / 2 - 6}
                textAnchor="middle"
              >
                {node.label.length > 8 ? `${node.label.slice(0, 7)}…` : node.label}
              </text>
              <text
                className="studio-run-graph__node-status"
                x={nodeW / 2}
                y={nodeH / 2 + 12}
                textAnchor="middle"
              >
                {NODE_STATUS_LABEL[node.status] || node.status}
              </text>
            </g>
            );
          })}
        </g>
      </svg>
      <div className="studio-run-graph__hint">
        依赖关系树 · 滚轮缩放 · 拖拽平移 · 点击切换步骤
      </div>
    </div>
  );
}

export default StudioRunNodeGraph;
