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',

}) {

  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 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 },

      });

    },

    [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) / view.scale;

        const dy = (e.clientY - dragging.startY) / view.scale;

        setNodeOverrides((prev) => ({

          ...prev,

          [dragging.nodeId]: {

            x: dragging.origin.x + dx,

            y: dragging.origin.y + dy,

          },

        }));

      }

    };



    const onUp = () => setDragging(null);



    window.addEventListener('mousemove', onMove);

    window.addEventListener('mouseup', onUp);

    return () => {

      window.removeEventListener('mousemove', onMove);

      window.removeEventListener('mouseup', onUp);

    };

  }, [dragging, view.scale]);



  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) => (

            <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' : ''}`}

              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;

