import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useAppContext } from '../context/AppContext';
import { useUI } from '../context/UIContext';
import { Activity, Server, Cpu, Network, RefreshCw } from 'lucide-react';
import { SpaceParticles } from '../components/SpaceParticles';
import { FixedButton } from '../components/Button';

type SubagentTask = {
  id: string;
  status?: string;
  label?: string;
  role?: string;
  agent_id?: string;
  session_key?: string;
  memory_ns?: string;
  tool_allowlist?: string[];
  max_retries?: number;
  retry_count?: number;
  retry_backoff?: number;
  max_task_chars?: number;
  max_result_chars?: number;
  created?: number;
  updated?: number;
  task?: string;
  result?: string;
  thread_id?: string;
  correlation_id?: string;
  waiting_for_reply?: boolean;
};

type RouterReply = {
  task_id?: string;
  thread_id?: string;
  correlation_id?: string;
  agent_id?: string;
  status?: string;
  result?: string;
};

type AgentThread = {
  thread_id?: string;
  owner?: string;
  participants?: string[];
  status?: string;
  topic?: string;
};

type AgentMessage = {
  message_id?: string;
  thread_id?: string;
  from_agent?: string;
  to_agent?: string;
  reply_to?: string;
  correlation_id?: string;
  type?: string;
  content?: string;
  requires_reply?: boolean;
  status?: string;
  created_at?: number;
};

type StreamItem = {
  kind?: 'event' | 'message' | string;
  at?: number;
  run_id?: string;
  agent_id?: string;
  event_type?: string;
  message?: string;
  retry_count?: number;
  message_id?: string;
  thread_id?: string;
  from_agent?: string;
  to_agent?: string;
  reply_to?: string;
  correlation_id?: string;
  message_type?: string;
  content?: string;
  status?: string;
  requires_reply?: boolean;
};

type RegistrySubagent = {
  agent_id?: string;
  enabled?: boolean;
  type?: string;
  transport?: string;
  node_id?: string;
  parent_agent_id?: string;
  managed_by?: string;
  notify_main_policy?: string;
  display_name?: string;
  role?: string;
  description?: string;
  system_prompt_file?: string;
  prompt_file_found?: boolean;
  memory_namespace?: string;
  tool_allowlist?: string[];
  inherited_tools?: string[];
  effective_tools?: string[];
  tool_visibility?: {
    mode?: string;
    inherited_tool_count?: number;
    effective_tool_count?: number;
  };
  routing_keywords?: string[];
};

type AgentTreeNode = {
  agent_id?: string;
  display_name?: string;
  role?: string;
  type?: string;
  transport?: string;
  managed_by?: string;
  node_id?: string;
  enabled?: boolean;
  children?: AgentTreeNode[];
};

type NodeTree = {
  node_id?: string;
  node_name?: string;
  online?: boolean;
  source?: string;
  readonly?: boolean;
  root?: {
    root?: AgentTreeNode;
  };
};

type NodeInfo = {
  id?: string;
  name?: string;
  endpoint?: string;
  version?: string;
  online?: boolean;
};

type AgentTaskStats = {
  total: number;
  running: number;
  failed: number;
  waiting: number;
  latestStatus: string;
  latestUpdated: number;
  active: Array<{ id: string; status: string; title: string }>;
};

type GraphCardSpec = {
  key: string;
  branch: string;
  agentID?: string;
  transportType?: 'local' | 'remote';
  x: number;
  y: number;
  w: number;
  h: number;
  kind: 'node' | 'agent';
  title: string;
  subtitle: string;
  meta: string[];
  accentTone: 'success' | 'danger' | 'warning' | 'info' | 'accent' | 'neutral';
  online?: boolean;
  clickable?: boolean;
  highlighted?: boolean;
  dimmed?: boolean;
  hidden?: boolean;
  scale: number;
  onClick?: () => void;
};

function graphAccentBackgroundClass(accentTone: GraphCardSpec['accentTone']) {
  switch (accentTone) {
    case 'success': return 'bg-gradient-to-br from-transparent to-emerald-500';
    case 'danger': return 'bg-gradient-to-br from-transparent to-red-500';
    case 'warning': return 'bg-gradient-to-br from-transparent to-amber-400';
    case 'info': return 'bg-gradient-to-br from-transparent to-sky-400';
    case 'accent': return 'bg-gradient-to-br from-transparent to-violet-400';
    case 'neutral':
    default: return 'bg-gradient-to-br from-transparent to-zinc-500';
  }
}

function graphAccentIconClass(accentTone: GraphCardSpec['accentTone']) {
  switch (accentTone) {
    case 'success': return 'text-emerald-500';
    case 'danger': return 'topology-icon-danger';
    case 'warning': return 'text-amber-400';
    case 'info': return 'text-sky-400';
    case 'accent': return 'text-violet-400';
    case 'neutral':
    default: return 'text-zinc-500';
  }
}

type GraphLineSpec = {
  path: string;
  dashed?: boolean;
  branch: string;
  highlighted?: boolean;
  dimmed?: boolean;
  hidden?: boolean;
};

type TopologyTooltipState = {
  title: string;
  subtitle: string;
  meta: string[];
  x: number;
  y: number;
  agentID?: string;
  transportType?: 'local' | 'remote';
} | null;

type StreamPreviewState = {
  task: SubagentTask | null;
  items: StreamItem[];
  taskID: string;
  loading?: boolean;
};

type TopologyDragState = {
  active: boolean;
  startX: number;
  startY: number;
  scrollLeft: number;
  scrollTop: number;
};

const cardWidth = 140;
const cardHeight = 140;
const clusterGap = 60;
const sectionGap = 160;
const topY = 96;
const mainY = 96;
const childStartY = 320;

function normalizeTitle(value?: string, fallback = '-'): string {
  const trimmed = `${value || ''}`.trim();
  return trimmed || fallback;
}

function summarizeTask(task?: string, label?: string): string {
  const text = normalizeTitle(label || task, '-');
  return text.length > 52 ? `${text.slice(0, 49)}...` : text;
}

function formatStreamTime(ts?: number): string {
  if (!ts) return '--:--:--';
  return new Date(ts).toLocaleTimeString([], { hour12: false });
}

function formatRuntimeTimestamp(value?: string): string {
  const raw = `${value || ''}`.trim();
  if (!raw || raw === '0001-01-01T00:00:00Z') return '-';
  const ts = Date.parse(raw);
  if (Number.isNaN(ts)) return raw;
  return new Date(ts).toLocaleString();
}

function summarizePreviewText(value?: string, limit = 180): string {
  const compact = `${value || ''}`.replace(/\s+/g, ' ').trim();
  if (!compact) return '(empty)';
  return compact.length > limit ? `${compact.slice(0, limit - 3)}...` : compact;
}

function tokenFromQuery(q: string): string {
  const raw = String(q || '').trim();
  if (!raw) return '';
  const search = raw.startsWith('?') ? raw.slice(1) : raw;
  const params = new URLSearchParams(search);
  return params.get('token') || '';
}

function bezierCurve(x1: number, y1: number, x2: number, y2: number): string {
  const offset = Math.max(Math.abs(y2 - y1) * 0.5, 60);
  return `M ${x1} ${y1} C ${x1} ${y1 + offset} ${x2} ${y2 - offset} ${x2} ${y2}`;
}

function horizontalBezierCurve(x1: number, y1: number, x2: number, y2: number): string {
  const offset = Math.max(Math.abs(x2 - x1) * 0.5, 60);
  return `M ${x1} ${y1} C ${x1 + offset} ${y1} ${x2 - offset} ${y2} ${x2} ${y2}`;
}

function buildTaskStats(tasks: SubagentTask[]): Record<string, AgentTaskStats> {
  return tasks.reduce<Record<string, AgentTaskStats>>((acc, task) => {
    const agentID = normalizeTitle(task.agent_id, '');
    if (!agentID) return acc;
    if (!acc[agentID]) {
      acc[agentID] = { total: 0, running: 0, failed: 0, waiting: 0, latestStatus: '', latestUpdated: 0, active: [] };
    }
    const item = acc[agentID];
    item.total += 1;
    if (task.status === 'running') item.running += 1;
    if (task.waiting_for_reply) item.waiting += 1;
    const updatedAt = Math.max(task.updated || 0, task.created || 0);
    if (updatedAt >= item.latestUpdated) {
      item.latestUpdated = updatedAt;
      item.latestStatus = normalizeTitle(task.status, '');
      item.failed = task.status === 'failed' ? 1 : 0;
    }
    if (task.status === 'running' || task.waiting_for_reply) {
      item.active.push({
        id: task.id,
        status: task.status || '-',
        title: summarizeTask(task.task, task.label),
      });
    }
    return acc;
  }, {});
}

function GraphCard({
  card,
  onHover,
  onLeave,
  onDragStart,
}: {
  card: GraphCardSpec;
  onHover: (card: GraphCardSpec, event: React.MouseEvent<HTMLDivElement>) => void;
  onLeave: () => void;
  onDragStart: (key: string, event: React.MouseEvent<HTMLDivElement>) => void;
}) {
  const isNode = card.kind === 'node';
  const Icon = isNode ? Server : Cpu;

  return (
    <foreignObject
      x={card.x}
      y={card.y}
      width={card.w}
      height={card.h}
      className="overflow-visible"
    >
      <div
        onMouseDown={(e) => onDragStart(card.key, e)}
        onClick={(e) => {
          if (e.defaultPrevented) return;
          card.onClick?.();
        }}
        onMouseEnter={(event) => onHover(card, event)}
        onMouseMove={(event) => onHover(card, event)}
        onMouseLeave={onLeave}
        className={`relative w-full h-full rounded-full flex flex-col items-center justify-center gap-1 transition-all duration-300 group ${card.highlighted
          ? 'scale-[1.05] z-10'
          : 'hover:scale-[1.02]'
          }`}
        style={{
          cursor: card.clickable ? 'pointer' : 'default',
          opacity: card.dimmed ? 0.3 : 1,
        }}
      >
        {/* Sleek Glass Node Background */}
        <div className={`absolute inset-0 rounded-full transition-all duration-300 backdrop-blur-md ${card.highlighted
          ? 'topology-node-highlight'
          : 'topology-node-base'
          }`}>
          {/* Base dark glass */}
          <div className="absolute inset-0 rounded-full bg-gradient-to-b from-zinc-800/95 to-zinc-950/95" />

          {/* Subtle accent glow */}
          <div className={`absolute inset-0 rounded-full opacity-20 ${graphAccentBackgroundClass(card.accentTone)}`} />

          {/* Inner depth ring */}
          <div className="topology-node-inner-border absolute inset-[1px] rounded-full border" />

          {/* Border ring */}
          <div className={`absolute inset-0 rounded-full border-[1.5px] ${card.highlighted ? 'topology-node-border-highlight' : 'border-zinc-700/80 group-hover:border-zinc-500/80'
            }`} />
        </div>

        {/* Content */}
        <div className="relative z-10 flex flex-col items-center justify-center w-full px-4 text-center">
          <div className={`flex items-center justify-center w-10 h-10 mb-1 rounded-full bg-zinc-950/60 border border-zinc-700/50 shadow-inner backdrop-blur-sm`}>
            <Icon className={`w-5 h-5 ${graphAccentIconClass(card.accentTone)}`} />
          </div>

          <div className="w-full">
            <div className="text-[13px] font-bold text-zinc-100 truncate leading-tight drop-shadow-md">{card.title}</div>
            <div className="text-[10px] text-zinc-300/90 truncate mt-0.5 drop-shadow-sm">{card.subtitle}</div>
          </div>

          {card.online !== undefined && (
            <div className={`absolute top-6 right-6 w-2.5 h-2.5 rounded-full border border-zinc-900 ${card.online ? 'status-dot-online topology-online-indicator' : 'status-dot-offline'}`} />
          )}
        </div>
      </div>
    </foreignObject>
  );
}

const Subagents: React.FC = () => {
  const { t } = useTranslation();
  const { q, nodeTrees, nodeP2P, nodeDispatchItems, subagentRuntimeItems, subagentRegistryItems } = useAppContext();
  const ui = useUI();

  const [items, setItems] = useState<SubagentTask[]>([]);
  const [selectedId, setSelectedId] = useState<string>('');
  const [selectedAgentID, setSelectedAgentID] = useState<string>('');
  const [spawnTask, setSpawnTask] = useState('');
  const [spawnAgentID, setSpawnAgentID] = useState('');
  const [spawnRole, setSpawnRole] = useState('');
  const [spawnLabel, setSpawnLabel] = useState('');
  const [steerMessage, setSteerMessage] = useState('');
  const [dispatchTask, setDispatchTask] = useState('');
  const [dispatchAgentID, setDispatchAgentID] = useState('');
  const [dispatchRole, setDispatchRole] = useState('');
  const [dispatchReply, setDispatchReply] = useState<RouterReply | null>(null);
  const [dispatchMerged, setDispatchMerged] = useState('');
  const [threadDetail, setThreadDetail] = useState<AgentThread | null>(null);
  const [threadMessages, setThreadMessages] = useState<AgentMessage[]>([]);
  const [inboxMessages, setInboxMessages] = useState<AgentMessage[]>([]);
  const [replyMessage, setReplyMessage] = useState('');
  const [replyToMessageID, setReplyToMessageID] = useState('');
  const [configAgentID, setConfigAgentID] = useState('');
  const [configRole, setConfigRole] = useState('');
  const [configDisplayName, setConfigDisplayName] = useState('');
  const [configSystemPromptFile, setConfigSystemPromptFile] = useState('');
  const [configToolAllowlist, setConfigToolAllowlist] = useState('');
  const [configRoutingKeywords, setConfigRoutingKeywords] = useState('');
  const [registryItems, setRegistryItems] = useState<RegistrySubagent[]>([]);
  const [promptFileContent, setPromptFileContent] = useState('');
  const [promptFileFound, setPromptFileFound] = useState(false);
  const [streamPreviewByAgent, setStreamPreviewByAgent] = useState<Record<string, StreamPreviewState>>({});
  const [selectedTopologyBranch, setSelectedTopologyBranch] = useState('');
  const [topologyFilter, setTopologyFilter] = useState<'all' | 'running' | 'failed' | 'local' | 'remote'>('all');
  const [topologyZoom, setTopologyZoom] = useState(0.9);
  const [topologyPan, setTopologyPan] = useState({ x: 0, y: 0 });
  const [nodeOverrides, setNodeOverrides] = useState<Record<string, { x: number, y: number }>>({});
  const [draggedNode, setDraggedNode] = useState<string | null>(null);
  const [topologyTooltip, setTopologyTooltip] = useState<TopologyTooltipState>(null);
  const [topologyDragging, setTopologyDragging] = useState(false);
  const topologyViewportRef = useRef<HTMLDivElement | null>(null);
  const topologyDragRef = useRef<{
    active: boolean;
    startX: number;
    startY: number;
    panX: number;
    panY: number;
  }>({
    active: false,
    startX: 0,
    startY: 0,
    panX: 0,
    panY: 0,
  });
  const nodeDragRef = useRef<{
    startX: number;
    startY: number;
    initialNodeX: number;
    initialNodeY: number;
  }>({ startX: 0, startY: 0, initialNodeX: 0, initialNodeY: 0 });
  const hasFittedRef = useRef(false);
  const streamPreviewLoadingRef = useRef<Record<string, string>>({});

  const apiPath = '/webui/api/subagents_runtime';
  const withAction = (action: string) => `${apiPath}${q}${q ? '&' : '?'}action=${encodeURIComponent(action)}`;

  const openAgentStream = (agentID: string, taskID = '', branch = '') => {
    if (branch) setSelectedTopologyBranch(branch);
    setSelectedAgentID(agentID);
    setSelectedId(taskID);
  };

  const load = async () => {
    try {
      if (subagentRuntimeItems.length > 0 || subagentRegistryItems.length > 0) {
        const arr = Array.isArray(subagentRuntimeItems) ? subagentRuntimeItems : [];
        const registry = Array.isArray(subagentRegistryItems) ? subagentRegistryItems : [];
        setItems(arr);
        setRegistryItems(registry);
        if (registry.length === 0) {
          setSelectedAgentID('');
          setSelectedId('');
        } else {
          const nextAgentID = selectedAgentID && registry.find((x: RegistrySubagent) => x.agent_id === selectedAgentID)
            ? selectedAgentID
            : (registry[0]?.agent_id || '');
          setSelectedAgentID(nextAgentID);
          const nextTask = arr.find((x: SubagentTask) => x.agent_id === nextAgentID);
          setSelectedId(nextTask?.id || '');
        }
        return;
      }
      const [tasksRes, registryRes] = await Promise.all([
        fetch(withAction('list')),
        fetch(withAction('registry')),
      ]);
      if (!tasksRes.ok) throw new Error(await tasksRes.text());
      if (!registryRes.ok) throw new Error(await registryRes.text());
      const j = await tasksRes.json();
      const registryJson = await registryRes.json();
      const arr = Array.isArray(j?.result?.items) ? j.result.items : [];
      const registryItems = Array.isArray(registryJson?.result?.items) ? registryJson.result.items : [];
      setItems(arr);
      setRegistryItems(registryItems);
      if (registryItems.length === 0) {
        setSelectedAgentID('');
        setSelectedId('');
      } else {
        const nextAgentID = selectedAgentID && registryItems.find((x: RegistrySubagent) => x.agent_id === selectedAgentID)
          ? selectedAgentID
          : (registryItems[0]?.agent_id || '');
        setSelectedAgentID(nextAgentID);
        const nextTask = arr.find((x: SubagentTask) => x.agent_id === nextAgentID);
        setSelectedId(nextTask?.id || '');
      }
    } catch (e) {
      // Mock data for preview
      setItems([
        { id: 'task-1', status: 'running', agent_id: 'worker-1', role: 'worker', task: 'Process data stream', created: Date.now() }
      ]);
    }
  };

  useEffect(() => {
    load().catch(() => { });
  }, [q, selectedAgentID, subagentRuntimeItems, subagentRegistryItems]);

  const selected = useMemo(() => items.find((x) => x.id === selectedId) || null, [items, selectedId]);
  const parsedNodeTrees = useMemo<NodeTree[]>(() => {
    try {
      const parsed = JSON.parse(nodeTrees);
      return Array.isArray(parsed) ? parsed : [];
    } catch {
      return [];
    }
  }, [nodeTrees]);
  const taskStats = useMemo(() => buildTaskStats(items), [items]);
  const recentTaskByAgent = useMemo(() => {
    return items.reduce<Record<string, SubagentTask>>((acc, task) => {
      const agentID = normalizeTitle(task.agent_id, '');
      if (!agentID) return acc;
      const existing = acc[agentID];
      const currentScore = Math.max(task.updated || 0, task.created || 0);
      const existingScore = existing ? Math.max(existing.updated || 0, existing.created || 0) : -1;
      if (!existing || currentScore > existingScore) {
        acc[agentID] = task;
      }
      return acc;
    }, {});
  }, [items]);
  const p2pSessionByNode = useMemo(() => {
    const out: Record<string, any> = {};
    const sessions = Array.isArray(nodeP2P?.nodes) ? nodeP2P.nodes : [];
    sessions.forEach((session: any) => {
      const nodeID = normalizeTitle(session?.node, '');
      if (!nodeID) return;
      out[nodeID] = session;
    });
    return out;
  }, [nodeP2P]);
  const recentDispatchByNode = useMemo(() => {
    const out: Record<string, any> = {};
    const rows = Array.isArray(nodeDispatchItems) ? nodeDispatchItems : [];
    rows.forEach((row: any) => {
      const nodeID = normalizeTitle(row?.node, '');
      if (!nodeID || out[nodeID]) return;
      out[nodeID] = row;
    });
    return out;
  }, [nodeDispatchItems]);
  const topologyGraph = useMemo(() => {
    const scale = topologyZoom;
    const originX = 56;
    const localTree = parsedNodeTrees.find((tree) => normalizeTitle(tree.node_id, '') === 'local') || null;
    const remoteTrees = parsedNodeTrees.filter((tree) => normalizeTitle(tree.node_id, '') !== 'local');
    const localRoot = localTree?.root?.root || {
      agent_id: 'main',
      display_name: 'Main Agent',
      role: 'orchestrator',
      type: 'router',
      transport: 'local',
      enabled: true,
      children: registryItems
        .filter((item) => item.agent_id && item.agent_id !== 'main' && item.managed_by === 'config.json')
        .map((item) => ({
          agent_id: item.agent_id,
          display_name: item.display_name,
          role: item.role,
          type: item.type,
          transport: item.transport,
          enabled: item.enabled,
          children: [],
        })),
    };
    const localChildren = Array.isArray(localRoot.children) ? localRoot.children : [];
    const localBranchWidth = Math.max(cardWidth, localChildren.length * (cardWidth + clusterGap) - clusterGap);
    const localOriginX = originX;
    const localMainX = localOriginX + Math.max(0, (localBranchWidth - cardWidth) / 2);
    const localMainCenterX = localMainX + cardWidth / 2;
    const localMainCenterY = mainY + cardHeight / 2;
    const remoteClusters = remoteTrees.map((tree) => {
      const root = tree.root?.root;
      const children = Array.isArray(root?.children) ? root.children : [];
      return {
        tree,
        root,
        children,
        width: Math.max(cardWidth, children.length * (cardWidth + clusterGap) - clusterGap),
      };
    });
    const totalRemoteWidth = remoteClusters.reduce((sum, cluster, idx) => {
      return sum + cluster.width + (idx > 0 ? sectionGap : 0);
    }, 0);
    const width = Math.max(900, localOriginX * 2 + localBranchWidth + (remoteClusters.length > 0 ? sectionGap + totalRemoteWidth : 0));
    const height = childStartY + cardHeight + 40;
    const cards: GraphCardSpec[] = [];
    const lines: GraphLineSpec[] = [];
    const localBranch = 'local';
    const localBranchStats = {
      running: 0,
      failed: 0,
    };

    const localMainStats = taskStats[normalizeTitle(localRoot.agent_id, 'main')] || { total: 0, running: 0, failed: 0, waiting: 0, latestStatus: '', latestUpdated: 0, active: [] };
    const localMainTask = recentTaskByAgent[normalizeTitle(localRoot.agent_id, 'main')];
    const localMainRegistry = registryItems.find((item) => item.agent_id === localRoot.agent_id);
    localBranchStats.running += localMainStats.running;
    localBranchStats.failed += localMainStats.failed;
    const localMainCard: GraphCardSpec = {
      key: 'agent-main',
      branch: localBranch,
      agentID: normalizeTitle(localRoot.agent_id, 'main'),
      transportType: 'local',
      kind: 'agent',
      x: localMainX,
      y: mainY,
      w: cardWidth,
      h: cardHeight,
      title: normalizeTitle(localRoot.display_name, 'Main Agent'),
      subtitle: `${normalizeTitle(localRoot.agent_id, 'main')} · ${normalizeTitle(localRoot.role, '-')}`,
      meta: [
        `children=${localChildren.length + remoteClusters.length}`,
        `total=${localMainStats.total} running=${localMainStats.running}`,
        `waiting=${localMainStats.waiting} failed=${localMainStats.failed}`,
        `notify=${normalizeTitle(localMainRegistry?.notify_main_policy, 'final_only')}`,
        `transport=${normalizeTitle(localRoot.transport, 'local')} type=${normalizeTitle(localRoot.type, 'router')}`,
        `tools=${normalizeTitle(localMainRegistry?.tool_visibility?.mode, 'allowlist')} visible=${localMainRegistry?.tool_visibility?.effective_tool_count ?? 0} inherited=${localMainRegistry?.tool_visibility?.inherited_tool_count ?? 0}`,
        (localMainRegistry?.inherited_tools || []).length ? `inherits: ${(localMainRegistry?.inherited_tools || []).join(', ')}` : 'inherits: -',
        localMainStats.active[0] ? `task: ${localMainStats.active[0].title}` : t('noLiveTasks'),
      ],
      accentTone: localMainStats.running > 0 ? 'success' : localMainStats.latestStatus === 'failed' ? 'danger' : 'warning',
      clickable: true,
      scale,
      onClick: () => {
        openAgentStream(normalizeTitle(localRoot.agent_id, 'main'), localMainTask?.id || '', localBranch);
      },
    };
    cards.push(localMainCard);

    localChildren.forEach((child, idx) => {
      const childX = localOriginX + idx * (cardWidth + clusterGap);
      const childY = childStartY;
      const stats = taskStats[normalizeTitle(child.agent_id, '')] || { total: 0, running: 0, failed: 0, waiting: 0, latestStatus: '', latestUpdated: 0, active: [] };
      const task = recentTaskByAgent[normalizeTitle(child.agent_id, '')];
      const childRegistry = registryItems.find((item) => item.agent_id === child.agent_id);
      localBranchStats.running += stats.running;
      localBranchStats.failed += stats.failed;
      cards.push({
        key: `local-child-${child.agent_id || idx}`,
        branch: localBranch,
        agentID: normalizeTitle(child.agent_id, ''),
        transportType: 'local',
        kind: 'agent',
        x: childX,
        y: childY,
        w: cardWidth,
        h: cardHeight,
        title: normalizeTitle(child.display_name, normalizeTitle(child.agent_id, 'agent')),
        subtitle: `${normalizeTitle(child.agent_id, '-')} · ${normalizeTitle(child.role, '-')}`,
        meta: [
          `total=${stats.total} running=${stats.running}`,
          `waiting=${stats.waiting} failed=${stats.failed}`,
          `notify=${normalizeTitle(childRegistry?.notify_main_policy, 'final_only')}`,
          `transport=${normalizeTitle(child.transport, 'local')} type=${normalizeTitle(child.type, 'worker')}`,
          `tools=${normalizeTitle(childRegistry?.tool_visibility?.mode, 'allowlist')} visible=${childRegistry?.tool_visibility?.effective_tool_count ?? 0} inherited=${childRegistry?.tool_visibility?.inherited_tool_count ?? 0}`,
          (childRegistry?.inherited_tools || []).length ? `inherits: ${(childRegistry?.inherited_tools || []).join(', ')}` : 'inherits: -',
          stats.active[0] ? `task: ${stats.active[0].title}` : task ? `last: ${summarizeTask(task.task, task.label)}` : t('noLiveTasks'),
        ],
        accentTone: stats.running > 0 ? 'success' : stats.latestStatus === 'failed' ? 'danger' : 'info',
        clickable: true,
        scale,
        onClick: () => {
          openAgentStream(normalizeTitle(child.agent_id, ''), task?.id || '', localBranch);
        },
      });
      lines.push({
        path: bezierCurve(localMainCard.x + cardWidth / 2, localMainCard.y + cardHeight / 2, childX + cardWidth / 2, childY + cardHeight / 2),
        branch: localBranch,
      });
    });

    let remoteOffsetX = localOriginX + localBranchWidth + sectionGap;
    remoteClusters.forEach((cluster, treeIndex) => {
      const { tree, root: treeRoot, children } = cluster;
      const branch = `node:${normalizeTitle(tree.node_id, `remote-${treeIndex}`)}`;
      const nodeID = normalizeTitle(tree.node_id, '');
      const p2pSession = p2pSessionByNode[nodeID];
      const recentDispatch = recentDispatchByNode[nodeID];
      const rootX = remoteOffsetX + Math.max(0, (cluster.width - cardWidth) / 2);
      if (!treeRoot) return;
      const rootCard: GraphCardSpec = {
        key: `remote-root-${tree.node_id || treeIndex}`,
        branch,
        agentID: normalizeTitle(treeRoot.agent_id, ''),
        transportType: 'remote',
        kind: 'agent',
        x: rootX,
        y: mainY,
        w: cardWidth,
        h: cardHeight,
        title: normalizeTitle(treeRoot.display_name, treeRoot.agent_id || 'main'),
        subtitle: `${normalizeTitle(treeRoot.agent_id, '-')} · ${normalizeTitle(treeRoot.role, '-')}`,
        meta: [
          `status=${tree.online ? t('online') : t('offline')}`,
          `transport=${normalizeTitle(treeRoot.transport, 'node')} type=${normalizeTitle(treeRoot.type, 'router')}`,
          `p2p=${normalizeTitle(nodeP2P?.transport, 'disabled')} session=${normalizeTitle(p2pSession?.status, 'unknown')}`,
          `last_transport=${normalizeTitle(recentDispatch?.used_transport, '-')}${recentDispatch?.fallback_from ? ` fallback=${normalizeTitle(recentDispatch?.fallback_from, '-')}` : ''}`,
          `last_ready=${formatRuntimeTimestamp(p2pSession?.last_ready_at)}`,
          `retry=${Number(p2pSession?.retry_count || 0)}`,
          `${t('error')}=${normalizeTitle(p2pSession?.last_error, '-')}`,
          `source=${normalizeTitle(treeRoot.managed_by, tree.source || '-')}`,
          t('remoteTasksUnavailable'),
        ],
        accentTone: !tree.online ? 'neutral' : normalizeTitle(p2pSession?.status, '').toLowerCase() === 'open' ? 'success' : normalizeTitle(p2pSession?.status, '').toLowerCase() === 'connecting' ? 'warning' : 'accent',
        clickable: true,
        scale,
        onClick: () => {
          openAgentStream(normalizeTitle(treeRoot.agent_id, ''), '', branch);
        },
      };
      cards.push(rootCard);
      lines.push({
        path: horizontalBezierCurve(localMainCard.x + cardWidth / 2, localMainCard.y + cardHeight / 2, rootCard.x + cardWidth / 2, rootCard.y + cardHeight / 2),
        dashed: true,
        branch,
      });
      children.forEach((child, idx) => {
        const childX = remoteOffsetX + idx * (cardWidth + clusterGap);
        const childY = childStartY;
        cards.push({
          key: `remote-child-${tree.node_id || treeIndex}-${child.agent_id || idx}`,
          branch,
          agentID: normalizeTitle(child.agent_id, ''),
          transportType: 'remote',
          kind: 'agent',
          x: childX,
          y: childY,
          w: cardWidth,
          h: cardHeight,
          title: normalizeTitle(child.display_name, child.agent_id || 'agent'),
          subtitle: `${normalizeTitle(child.agent_id, '-')} · ${normalizeTitle(child.role, '-')}`,
          meta: [
            `transport=${normalizeTitle(child.transport, 'node')} type=${normalizeTitle(child.type, 'worker')}`,
            `p2p=${normalizeTitle(nodeP2P?.transport, 'disabled')} session=${normalizeTitle(p2pSession?.status, 'unknown')}`,
            `last_transport=${normalizeTitle(recentDispatch?.used_transport, '-')}${recentDispatch?.fallback_from ? ` fallback=${normalizeTitle(recentDispatch?.fallback_from, '-')}` : ''}`,
            `last_ready=${formatRuntimeTimestamp(p2pSession?.last_ready_at)}`,
            `retry=${Number(p2pSession?.retry_count || 0)}`,
            `${t('error')}=${normalizeTitle(p2pSession?.last_error, '-')}`,
            `source=${normalizeTitle(child.managed_by, 'remote_webui')}`,
            t('remoteTasksUnavailable'),
          ],
          accentTone: normalizeTitle(p2pSession?.status, '').toLowerCase() === 'open' ? 'success' : normalizeTitle(p2pSession?.status, '').toLowerCase() === 'connecting' ? 'warning' : 'accent',
          clickable: true,
          scale,
          onClick: () => {
            openAgentStream(normalizeTitle(child.agent_id, ''), '', branch);
          },
        });
        lines.push({
          path: bezierCurve(rootCard.x + cardWidth / 2, rootCard.y + cardHeight / 2, childX + cardWidth / 2, childY + cardHeight / 2),
          branch,
        });
      });
      remoteOffsetX += cluster.width + sectionGap;
    });

    const highlightedBranch = selectedTopologyBranch.trim();
    const branchFilters = new Map<string, boolean>();
    branchFilters.set(localBranch, topologyFilter === 'all' || topologyFilter === 'local' || (topologyFilter === 'running' && localBranchStats.running > 0) || (topologyFilter === 'failed' && localBranchStats.failed > 0));
    remoteTrees.forEach((tree, treeIndex) => {
      const branch = `node:${normalizeTitle(tree.node_id, `remote-${treeIndex}`)}`;
      branchFilters.set(branch, topologyFilter === 'all' || topologyFilter === 'remote');
    });
    const decoratedCards = cards.map((card) => {
      const override = nodeOverrides[card.key];
      return {
        ...card,
        x: override ? override.x : card.x,
        y: override ? override.y : card.y,
        hidden: branchFilters.get(card.branch) === false,
        highlighted: !highlightedBranch || card.branch === highlightedBranch,
        dimmed: branchFilters.get(card.branch) === false ? true : !!highlightedBranch && card.branch !== highlightedBranch,
      };
    });

    // Recalculate lines based on potentially overridden card positions
    const recalculatedLines: GraphLineSpec[] = [];

    // Helper to find a card's current position
    const getCardPos = (key: string) => {
      const c = decoratedCards.find(c => c.key === key);
      return c ? { cx: c.x + cardWidth / 2, cy: c.y + cardHeight / 2 } : null;
    };

    const localMainPos = getCardPos('agent-main');

    localChildren.forEach((child, idx) => {
      const childKey = `local-child-${child.agent_id || idx}`;
      const childPos = getCardPos(childKey);
      if (localMainPos && childPos) {
        recalculatedLines.push({
          path: bezierCurve(localMainPos.cx, localMainPos.cy, childPos.cx, childPos.cy),
          branch: localBranch,
        });
      }
    });

    remoteClusters.forEach((cluster, treeIndex) => {
      const branch = `node:${normalizeTitle(cluster.tree.node_id, `remote-${treeIndex}`)}`;
      const remoteRootKey = `remote-root-${cluster.tree.node_id || treeIndex}`;
      const remoteRootPos = getCardPos(remoteRootKey);

      if (localMainPos && remoteRootPos) {
        recalculatedLines.push({
          path: horizontalBezierCurve(localMainPos.cx, localMainPos.cy, remoteRootPos.cx, remoteRootPos.cy),
          dashed: true,
          branch,
        });
      }

      cluster.children.forEach((child, idx) => {
        const childKey = `remote-child-${cluster.tree.node_id || treeIndex}-${child.agent_id || idx}`;
        const childPos = getCardPos(childKey);
        if (remoteRootPos && childPos) {
          recalculatedLines.push({
            path: bezierCurve(remoteRootPos.cx, remoteRootPos.cy, childPos.cx, childPos.cy),
            branch,
          });
        }
      });
    });

    const decoratedLines = recalculatedLines.map((line) => ({
      ...line,
      hidden: branchFilters.get(line.branch) === false,
      highlighted: !highlightedBranch || line.branch === highlightedBranch,
      dimmed: branchFilters.get(line.branch) === false ? true : !!highlightedBranch && line.branch !== highlightedBranch,
    }));

    return { width, height, cards: decoratedCards, lines: decoratedLines };
  }, [parsedNodeTrees, registryItems, taskStats, recentTaskByAgent, selectedTopologyBranch, topologyFilter, t, topologyZoom, nodeOverrides, nodeP2P, p2pSessionByNode, recentDispatchByNode]);

  const fitView = () => {
    const viewport = topologyViewportRef.current;
    if (!viewport || !topologyGraph.width) return;
    const availableW = viewport.clientWidth;
    const availableH = viewport.clientHeight;

    const fitted = Math.min(1.15, Math.max(0.2, (availableW - 48) / topologyGraph.width));

    setTopologyZoom(fitted);
    setTopologyPan({
      x: (availableW - topologyGraph.width * fitted) / 2,
      y: Math.max(24, (availableH - topologyGraph.height * fitted) / 2)
    });
  };

  useEffect(() => {
    if (!hasFittedRef.current && topologyGraph.width > 0) {
      fitView();
      hasFittedRef.current = true;
    }
  }, [topologyGraph.width]);

  useEffect(() => {
    const viewport = topologyViewportRef.current;
    if (!viewport) return;

    const handleWheel = (e: WheelEvent) => {
      if (!(e.ctrlKey || e.metaKey)) {
        return;
      }
      e.preventDefault();

      const rect = viewport.getBoundingClientRect();
      const mouseX = e.clientX - rect.left;
      const mouseY = e.clientY - rect.top;

      setTopologyZoom(prevZoom => {
        const zoomSensitivity = 0.002;
        const delta = -e.deltaY * zoomSensitivity;
        const newZoom = Math.min(Math.max(0.1, prevZoom * (1 + delta)), 4);

        setTopologyPan(prevPan => {
          const scaleRatio = newZoom / prevZoom;
          const newPanX = mouseX - (mouseX - prevPan.x) * scaleRatio;
          const newPanY = mouseY - (mouseY - prevPan.y) * scaleRatio;
          return { x: newPanX, y: newPanY };
        });

        return newZoom;
      });
    };

    viewport.addEventListener('wheel', handleWheel, { passive: false });
    return () => viewport.removeEventListener('wheel', handleWheel);
  }, []);

  const handleTopologyHover = (card: GraphCardSpec, event: React.MouseEvent<HTMLDivElement>) => {
    const tooltipWidth = 360;
    const tooltipHeight = 420;
    let x = event.clientX + 14;
    let y = event.clientY + 14;

    if (x + tooltipWidth > window.innerWidth) {
      x = event.clientX - tooltipWidth - 14;
    }
    if (y + tooltipHeight > window.innerHeight) {
      y = event.clientY - tooltipHeight - 14;
    }

    setTopologyTooltip({
      title: card.title,
      subtitle: card.subtitle,
      meta: card.meta,
      x,
      y,
      agentID: card.agentID,
      transportType: card.transportType,
    });
  };

  const clearTopologyTooltip = () => setTopologyTooltip(null);

  const handleNodeDragStart = (key: string, event: React.MouseEvent<HTMLDivElement>) => {
    if (event.button !== 0) return;
    event.stopPropagation();

    const card = topologyGraph.cards.find(c => c.key === key);
    if (!card) return;

    setDraggedNode(key);
    nodeDragRef.current = {
      startX: event.clientX,
      startY: event.clientY,
      initialNodeX: card.x,
      initialNodeY: card.y,
    };
    clearTopologyTooltip();
  };

  const startTopologyDrag = (event: React.MouseEvent<HTMLDivElement>) => {
    if (event.button !== 0) return;
    topologyDragRef.current = {
      active: true,
      startX: event.clientX,
      startY: event.clientY,
      panX: topologyPan.x,
      panY: topologyPan.y,
    };
    setTopologyDragging(true);
    clearTopologyTooltip();
  };

  const moveTopologyDrag = (event: React.MouseEvent<HTMLDivElement>) => {
    if (draggedNode) {
      const deltaX = (event.clientX - nodeDragRef.current.startX) / topologyZoom;
      const deltaY = (event.clientY - nodeDragRef.current.startY) / topologyZoom;

      setNodeOverrides(prev => ({
        ...prev,
        [draggedNode]: {
          x: nodeDragRef.current.initialNodeX + deltaX,
          y: nodeDragRef.current.initialNodeY + deltaY,
        }
      }));
      return;
    }

    if (!topologyDragRef.current.active) return;
    const deltaX = event.clientX - topologyDragRef.current.startX;
    const deltaY = event.clientY - topologyDragRef.current.startY;
    setTopologyPan({
      x: topologyDragRef.current.panX + deltaX,
      y: topologyDragRef.current.panY + deltaY,
    });
  };

  const stopTopologyDrag = () => {
    if (draggedNode) {
      setDraggedNode(null);
    }
    if (topologyDragRef.current.active) {
      topologyDragRef.current.active = false;
      setTopologyDragging(false);
    }
  };

  const callAction = async (payload: Record<string, any>) => {
    const r = await fetch(`${apiPath}${q}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    if (!r.ok) {
      await ui.notify({ title: t('requestFailed'), message: await r.text() });
      return null;
    }
    return r.json();
  };

  const loadThreadAndInbox = async (task: SubagentTask | null) => {
    if (!task?.id) {
      setThreadDetail(null);
      setThreadMessages([]);
      setInboxMessages([]);
      return;
    }
    try {
      const [threadRes, inboxRes] = await Promise.all([
        callAction({ action: 'thread', id: task.id, limit: 50 }),
        callAction({ action: 'inbox', id: task.id, limit: 50 }),
      ]);
      setThreadDetail(threadRes?.result?.thread || null);
      setThreadMessages(Array.isArray(threadRes?.result?.messages) ? threadRes.result.messages : []);
      setInboxMessages(Array.isArray(inboxRes?.result?.messages) ? inboxRes.result.messages : []);
    } catch (e) { }
  };

  const loadStreamPreview = async (agentID: string, task: SubagentTask | null) => {
    const taskID = task?.id || '';
    if (!agentID) return;
    if (streamPreviewLoadingRef.current[agentID] === taskID) return;
    const existing = streamPreviewByAgent[agentID];
    if (existing && existing.taskID === taskID && !existing.loading) return;

    streamPreviewLoadingRef.current[agentID] = taskID;
    setStreamPreviewByAgent((prev) => ({
      ...prev,
      [agentID]: {
        task: task || null,
        items: prev[agentID]?.items || [],
        taskID,
        loading: !!taskID,
      },
    }));

    if (!taskID) {
      delete streamPreviewLoadingRef.current[agentID];
      setStreamPreviewByAgent((prev) => ({
        ...prev,
        [agentID]: { task: null, items: [], taskID: '', loading: false },
      }));
      return;
    }

    try {
      const streamRes = await callAction({ action: 'stream', id: taskID, limit: 12 });
      delete streamPreviewLoadingRef.current[agentID];
      setStreamPreviewByAgent((prev) => ({
        ...prev,
        [agentID]: {
          task: streamRes?.result?.task || task,
          items: Array.isArray(streamRes?.result?.items) ? streamRes.result.items : [],
          taskID,
          loading: false,
        },
      }));
    } catch {
      delete streamPreviewLoadingRef.current[agentID];
      setStreamPreviewByAgent((prev) => ({
        ...prev,
        [agentID]: { task: task || null, items: [], taskID, loading: false },
      }));
    }
  };

  useEffect(() => {
    const selectedTaskID = String(selected?.id || '').trim();
    const previewAgentID = topologyTooltip?.transportType === 'local' ? String(topologyTooltip.agentID || '').trim() : '';
    const previewTask = previewAgentID ? recentTaskByAgent[previewAgentID] || null : null;
    const previewTaskID = String(previewTask?.id || '').trim();

    if (!selectedTaskID) {
      setThreadDetail(null);
      setThreadMessages([]);
      setInboxMessages([]);
    }
    if (!previewAgentID) {
      return;
    }
    setStreamPreviewByAgent((prev) => ({
      ...prev,
      [previewAgentID]: {
        task: previewTask,
        items: prev[previewAgentID]?.items || [],
        taskID: previewTaskID,
        loading: !!previewTaskID,
      },
    }));

    if (!selectedTaskID && !previewTaskID) return;

    const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const url = new URL(`${proto}//${window.location.host}/webui/api/subagents_runtime/live`);
    if (tokenFromQuery(q)) url.searchParams.set('token', tokenFromQuery(q));
    if (selectedTaskID) url.searchParams.set('task_id', selectedTaskID);
    if (previewTaskID) url.searchParams.set('preview_task_id', previewTaskID);

    const ws = new WebSocket(url.toString());
    ws.onmessage = (event) => {
      try {
        const msg = JSON.parse(event.data);
        const payload = msg?.payload || {};
        if (payload.thread) {
          setThreadDetail(payload.thread.thread || null);
          setThreadMessages(Array.isArray(payload.thread.messages) ? payload.thread.messages : []);
        }
        if (payload.inbox) {
          setInboxMessages(Array.isArray(payload.inbox.messages) ? payload.inbox.messages : []);
        }
        if (previewAgentID && payload.preview) {
          setStreamPreviewByAgent((prev) => ({
            ...prev,
            [previewAgentID]: {
              task: payload.preview.task || previewTask,
              items: Array.isArray(payload.preview.items) ? payload.preview.items : [],
              taskID: previewTaskID,
              loading: false,
            },
          }));
        }
      } catch (err) {
        console.error(err);
      }
    };
    ws.onerror = () => {
      if (previewAgentID) {
        setStreamPreviewByAgent((prev) => ({
          ...prev,
          [previewAgentID]: {
            task: previewTask,
            items: prev[previewAgentID]?.items || [],
            taskID: previewTaskID,
            loading: false,
          },
        }));
      }
    };
    return () => {
      ws.close();
    };
  }, [selected?.id, topologyTooltip?.agentID, topologyTooltip?.transportType, recentTaskByAgent, q]);

  return (
    <div className="h-full p-4 md:p-6 xl:p-8 flex flex-col gap-4">
      <div className="flex items-center justify-between gap-3 flex-wrap">
        <h1 className="text-xl md:text-2xl font-semibold">{t('subagentsRuntime')}</h1>
        <FixedButton onClick={() => load()} variant="primary" label={t('refresh')}>
          <RefreshCw className="w-4 h-4" />
        </FixedButton>
      </div>

      <div className="flex-1 min-h-0 brand-card border border-zinc-800 p-4 flex flex-col gap-3">
        <div className="flex items-center justify-between gap-3 flex-wrap">
          <div>
            <div className="text-xs text-zinc-400 uppercase tracking-wider">{t('agentTopology')}</div>
            <div className="text-sm text-zinc-500">{t('agentTopologyHint')}</div>
          </div>
          <div className="flex items-center gap-2 flex-wrap justify-end">
            {(['all', 'running', 'failed', 'local', 'remote'] as const).map((filter) => (
              <button
                key={filter}
                onClick={() => setTopologyFilter(filter)}
                className={`px-2 py-1 rounded-xl text-[11px] ${topologyFilter === filter ? 'control-chip-active' : 'control-chip'
                  }`}
              >
                {t(`topologyFilter.${filter}`)}
              </button>
            ))}
            {selectedTopologyBranch && (
              <button
                onClick={() => setSelectedTopologyBranch('')}
                className="px-2 py-1 rounded-xl text-[11px] control-chip"
              >
                {t('clearFocus')}
              </button>
            )}
            <div className="control-chip-group flex items-center gap-1 rounded-xl px-1 py-1">
              <button
                onClick={() => {
                  const newZoom = Math.max(0.1, Number((topologyZoom - 0.1).toFixed(2)));
                  const viewport = topologyViewportRef.current;
                  if (viewport) {
                    const rect = viewport.getBoundingClientRect();
                    const mouseX = rect.width / 2;
                    const mouseY = rect.height / 2;
                    const scaleRatio = newZoom / topologyZoom;
                    setTopologyPan(prev => ({
                      x: mouseX - (mouseX - prev.x) * scaleRatio,
                      y: mouseY - (mouseY - prev.y) * scaleRatio
                    }));
                  }
                  setTopologyZoom(newZoom);
                }}
                className="px-2 py-1 rounded-lg text-[11px] control-chip"
              >
                {t('zoomOut')}
              </button>
              <button
                onClick={fitView}
                className="px-2 py-1 rounded-lg text-[11px] control-chip"
              >
                {t('fitView')}
              </button>
              <button
                onClick={() => {
                  const newZoom = 1;
                  const viewport = topologyViewportRef.current;
                  if (viewport) {
                    const rect = viewport.getBoundingClientRect();
                    const mouseX = rect.width / 2;
                    const mouseY = rect.height / 2;
                    const scaleRatio = newZoom / topologyZoom;
                    setTopologyPan(prev => ({
                      x: mouseX - (mouseX - prev.x) * scaleRatio,
                      y: mouseY - (mouseY - prev.y) * scaleRatio
                    }));
                  }
                  setTopologyZoom(newZoom);
                }}
                className="px-2 py-1 rounded-lg text-[11px] control-chip"
              >
                100%
              </button>
              <button
                onClick={() => {
                  const newZoom = Math.min(4, Number((topologyZoom + 0.1).toFixed(2)));
                  const viewport = topologyViewportRef.current;
                  if (viewport) {
                    const rect = viewport.getBoundingClientRect();
                    const mouseX = rect.width / 2;
                    const mouseY = rect.height / 2;
                    const scaleRatio = newZoom / topologyZoom;
                    setTopologyPan(prev => ({
                      x: mouseX - (mouseX - prev.x) * scaleRatio,
                      y: mouseY - (mouseY - prev.y) * scaleRatio
                    }));
                  }
                  setTopologyZoom(newZoom);
                }}
                className="px-2 py-1 rounded-lg text-[11px] control-chip"
              >
                {t('zoomIn')}
              </button>
            </div>
            <div className="text-xs text-zinc-400">
              {Math.round(topologyZoom * 100)}% · {items.filter((item) => item.status === 'running').length} {t('runningTasks')}
            </div>
          </div>
        </div>
        <div
          ref={topologyViewportRef}
          onMouseDown={startTopologyDrag}
          onMouseMove={moveTopologyDrag}
          onMouseUp={stopTopologyDrag}
          onMouseLeave={() => {
            stopTopologyDrag();
            clearTopologyTooltip();
          }}
          className="radius-canvas relative flex-1 min-h-[420px] sm:min-h-[560px] xl:min-h-[760px] overflow-hidden border border-zinc-800 bg-zinc-950/80"
          style={{ cursor: topologyDragging ? 'grabbing' : 'grab' }}
        >
          <SpaceParticles />
          <div className="absolute inset-0 z-10">
            <div
              style={{
                transform: `translate(${topologyPan.x}px, ${topologyPan.y}px) scale(${topologyZoom})`,
                transformOrigin: '0 0',
                width: topologyGraph.width,
                height: topologyGraph.height,
                transition: topologyDragging || draggedNode ? 'none' : 'transform 0.1s ease-out',
              }}
              className="relative will-change-transform"
            >
              <svg
                width={topologyGraph.width}
                height={topologyGraph.height}
                className="block absolute top-0 left-0 overflow-visible"
              >
                <style>
                  {`
                    @keyframes flow {
                      from { stroke-dashoffset: 24; }
                      to { stroke-dashoffset: 0; }
                    }
                    .animate-flow {
                      animation: flow 1s linear infinite;
                    }
                    .animate-flow-fast {
                      animation: flow 0.5s linear infinite;
                    }
                  `}
                </style>
                {topologyGraph.lines.map((line, idx) => (
                  line.hidden ? null : (
                    <g key={`line-${idx}`}>
                      {/* Faint energy track */}
                      <path
                        d={line.path}
                        fill="none"
                        stroke={line.highlighted ? 'var(--topology-line-highlight-track)' : 'var(--topology-line-track)'}
                        strokeWidth={line.highlighted ? '6' : '2'}
                        strokeLinecap="round"
                        className="transition-all duration-300"
                      />
                      {/* Flowing light particles */}
                      <path
                        d={line.path}
                        fill="none"
                        stroke={line.highlighted ? 'var(--topology-line-highlight-flow)' : 'var(--topology-line-flow)'}
                        strokeWidth={line.highlighted ? '2.5' : '1.5'}
                        strokeDasharray={line.highlighted ? "6 18" : "4 20"}
                        className={line.highlighted ? "animate-flow-fast" : "animate-flow"}
                        strokeLinecap="round"
                        opacity={line.dimmed ? 0.1 : 1}
                      />
                    </g>
                  )
                ))}
                {topologyGraph.cards.map((card) => (
                  card.hidden ? null : <GraphCard key={card.key} card={card} onHover={handleTopologyHover} onLeave={clearTopologyTooltip} onDragStart={handleNodeDragStart} />
                ))}
              </svg>
            </div>
          </div>
          {topologyTooltip && (
            <div
              className="topology-tooltip pointer-events-none fixed z-50 w-[360px] max-w-[min(360px,calc(100vw-24px))] brand-card-subtle border border-zinc-700/80 p-4 backdrop-blur-md transition-opacity duration-200"
              style={{ left: topologyTooltip.x, top: topologyTooltip.y }}
            >
              <div className="flex items-center gap-2 mb-2">
                <div className="topology-accent-warning w-2 h-2 rounded-full" />
                <div className="text-sm font-semibold text-zinc-100">{topologyTooltip.title}</div>
              </div>
              <div className="text-xs text-zinc-400 mb-3 pb-3 border-b border-zinc-800/60">{topologyTooltip.subtitle}</div>
              <div className="space-y-1.5">
                {topologyTooltip.meta.map((line, idx) => {
                  if (!line.includes('=')) {
                    return (
                      <div key={idx} className="text-xs text-zinc-300 font-medium">
                        {line}
                      </div>
                    );
                  }
                  const [key, ...rest] = line.split('=');
                  const value = rest.join('=');
                  return (
                    <div key={idx} className="flex justify-between gap-3 text-xs">
                      <span className="text-zinc-500">{key}</span>
                      <span className="text-zinc-300 font-medium text-right">{value || '-'}</span>
                    </div>
                  );
                })}
              </div>
              {topologyTooltip.transportType === 'local' && topologyTooltip.agentID && (
                <div className="mt-4 pt-4 border-t border-zinc-800/60 space-y-3">
                  <div className="text-[11px] text-zinc-500 uppercase tracking-wider">{t('internalStream')}</div>
                  {streamPreviewByAgent[topologyTooltip.agentID]?.loading ? (
                    <div className="text-xs text-zinc-400">Loading internal stream...</div>
                  ) : streamPreviewByAgent[topologyTooltip.agentID]?.task ? (
                    <>
                      <div className="brand-card-subtle border border-zinc-800 p-3 space-y-1.5">
                        <div className="text-xs text-zinc-300">run={streamPreviewByAgent[topologyTooltip.agentID]?.task?.id || '-'}</div>
                        <div className="text-xs text-zinc-400">
                          status={streamPreviewByAgent[topologyTooltip.agentID]?.task?.status || '-'} · thread={streamPreviewByAgent[topologyTooltip.agentID]?.task?.thread_id || '-'}
                        </div>
                      </div>
                      {streamPreviewByAgent[topologyTooltip.agentID]?.items?.length ? (
                        <div className="brand-card-subtle border border-zinc-800 p-3 space-y-2">
                          <div className="flex items-center justify-between gap-2">
                            <div className="text-xs font-medium text-zinc-200">
                              {(() => {
                                const item = streamPreviewByAgent[topologyTooltip.agentID].items[streamPreviewByAgent[topologyTooltip.agentID].items.length - 1];
                                return item.kind === 'event'
                                  ? `${item.event_type || 'event'}${item.status ? ` · ${item.status}` : ''}`
                                  : `${item.from_agent || '-'} -> ${item.to_agent || '-'} · ${item.message_type || 'message'}`;
                              })()}
                            </div>
                            <div className="text-[11px] text-zinc-500">
                              {formatStreamTime(streamPreviewByAgent[topologyTooltip.agentID].items[streamPreviewByAgent[topologyTooltip.agentID].items.length - 1]?.at)}
                            </div>
                          </div>
                          <div className="text-xs text-zinc-300 leading-5 whitespace-pre-wrap break-words">
                            {(() => {
                              const item = streamPreviewByAgent[topologyTooltip.agentID].items[streamPreviewByAgent[topologyTooltip.agentID].items.length - 1];
                              return summarizePreviewText(item.kind === 'event' ? (item.message || '(no event message)') : (item.content || '(empty message)'), 520);
                            })()}
                          </div>
                        </div>
                      ) : (
                        <div className="text-xs text-zinc-400">No internal stream events yet.</div>
                      )}
                    </>
                  ) : (
                    <div className="text-xs text-zinc-400">No persisted run for this agent yet.</div>
                  )}
                </div>
              )}
            </div>
          )}
        </div>
      </div>

    </div>
  );
};

export default Subagents;
