// ─── Agent Activity Tree (Mission Control) ──────────────────────────
// Full-depth parallel agent activity tracking for the 3-Tier system.
// Provides: initActivityTree, addNode, addToolCall, updateToolCall,
//           finalizeNode, getStats, createMockActivityTree, formatElapsed

// Agent metadata — maps agent IDs to display info
const AGENT_META: Record<string, { emoji: string; name: string; tier: 1|2|3 }> = {
  rose:           { emoji: '🌹', name: 'Rose',           tier: 1 },
  lotus:          { emoji: '🪷', name: 'Lotus',          tier: 2 },
  'forget-me-not':{ emoji: '🌼', name: 'Forget-me-not',  tier: 2 },
  sunflower:      { emoji: '🌻', name: 'Sunflower',      tier: 2 },
  iris:           { emoji: '⚜️', name: 'Iris',           tier: 2 },
  ivy:            { emoji: '🌿', name: 'Ivy',            tier: 2 },
  dandelion:      { emoji: '🛡', name: 'Dandelion',      tier: 2 },
  root:           { emoji: '🌳', name: 'Root',           tier: 2 },
};

// Tier-3 sub-agent detection — tool names that indicate sub-agent work
const TIER3_TOOL_NAMES = new Set([
  'search_files', 'read_file', 'write_file', 'terminal', 'browser_navigate',
  'browser_snapshot', 'browser_click', 'browser_type', 'browser_press',
  'web_search', 'delegate_task', 'execute_code', 'patch',
]);

let _nodeCounter = 0;
function _nextNodeId(agentId: string): string {
  return `${agentId}-${++_nodeCounter}`;
}

// ─── Core Functions ────────────────────────────────────────────────

function initActivityTree(): ActivityTree {
  const tree: ActivityTree = {
    version: 1,
    rootId: 'rose',
    nodes: {
      rose: {
        id: 'rose',
        parentId: null,
        agentId: 'rose',
        agentEmoji: '🌹',
        agentName: 'Rose',
        tier: 1,
        status: 'running',
        task: 'Orchestrating',
        toolCalls: [],
        startedAt: Date.now(),
        endedAt: null,
        duration: null,
        children: [],
        collapsed: false,
        metadata: {},
      }
    },
    stats: _emptyStats(),
  };
  S.activityTree = tree;
  S.mcFilter = {};
  S.mcSort = 'runtime';
  return tree;
}

function _emptyStats(): MCStats {
  return {
    totalAgents: 0,
    runningAgents: 0,
    pendingAgents: 0,
    doneAgents: 0,
    errorAgents: 0,
    totalTools: 0,
    doneTools: 0,
    runningTools: 0,
    avgResponseTime: 0,
    totalElapsed: 0,
  };
}

// ─── Node Operations ───────────────────────────────────────────────

function atAddNode(opts: {
  parentId?: string;
  agentId: string;
  task: string;
  status?: ActivityNode['status'];
  tier?: 1|2|3;
}): ActivityNode | null {
  const tree = S.activityTree;
  if (!tree) return null;

  const parentId = opts.parentId || tree.rootId;
  const parent = tree.nodes[parentId];
  if (!parent) return null;

  const meta = AGENT_META[opts.agentId];
  const tier = opts.tier || (meta ? meta.tier : 3);
  const id = _nextNodeId(opts.agentId);

  const node: ActivityNode = {
    id,
    parentId,
    agentId: opts.agentId,
    agentEmoji: meta?.emoji || '⚙️',
    agentName: meta?.name || opts.agentId,
    tier,
    status: opts.status || 'pending',
    task: opts.task,
    toolCalls: [],
    startedAt: opts.status === 'running' ? Date.now() : null,
    endedAt: null,
    duration: null,
    children: [],
    collapsed: false,
    metadata: {},
  };

  tree.nodes[id] = node;
  parent.children.push(id);
  _recalcStats(tree);
  return node;
}

function atUpdateNode(nodeId: string, updates: Partial<ActivityNode>): void {
  const tree = S.activityTree;
  if (!tree) return;
  const node = tree.nodes[nodeId];
  if (!node) return;
  Object.assign(node, updates);
  _recalcStats(tree);
}

function atGetNode(nodeId: string): ActivityNode | null {
  return S.activityTree?.nodes[nodeId] || null;
}

function atGetChildren(nodeId: string): ActivityNode[] {
  const tree = S.activityTree;
  if (!tree) return [];
  const node = tree.nodes[nodeId];
  if (!node) return [];
  return node.children.map(id => tree.nodes[id]).filter(Boolean);
}

function atGetRunningNodes(): ActivityNode[] {
  const tree = S.activityTree;
  if (!tree) return [];
  return Object.values(tree.nodes).filter(
    n => n.status === 'running' || n.status === 'thinking'
  );
}

// ─── Tool Call Operations ──────────────────────────────────────────

function atAddToolCall(nodeId: string, tc: {
  name: string;
  args?: Record<string, any>;
  status?: ActivityToolCall['status'];
}): ActivityToolCall | null {
  const tree = S.activityTree;
  if (!tree) return null;
  const node = tree.nodes[nodeId];
  if (!node) return null;

  // If node was pending, it's now running
  if (node.status === 'pending') {
    node.status = 'running';
    node.startedAt = node.startedAt || Date.now();
  }

  const toolCall: ActivityToolCall = {
    id: `tc-${++_nodeCounter}`,
    name: tc.name,
    status: tc.status || 'running',
    args: tc.args || {},
    startedAt: Date.now(),
    endedAt: null,
  };

  node.toolCalls.push(toolCall);
  _recalcStats(tree);
  return toolCall;
}

function atUpdateToolCall(nodeId: string, toolId: string, updates: Partial<ActivityToolCall>): void {
  const tree = S.activityTree;
  if (!tree) return;
  const node = tree.nodes[nodeId];
  if (!node) return;

  const tc = node.toolCalls.find(t => t.id === toolId);
  if (!tc) return;

  Object.assign(tc, updates);
  if (updates.status === 'done' || updates.status === 'error') {
    tc.endedAt = tc.endedAt || Date.now();
    tc.duration = tc.startedAt ? (tc.endedAt - tc.startedAt) / 1000 : null;
  }
  _recalcStats(tree);
}

// ─── Finalization ──────────────────────────────────────────────────

function atFinalizeNode(nodeId: string, status: ActivityNode['status']): void {
  const tree = S.activityTree;
  if (!tree) return;
  const node = tree.nodes[nodeId];
  if (!node) return;

  node.status = status;
  node.endedAt = Date.now();
  node.duration = node.startedAt ? (node.endedAt - node.startedAt) / 1000 : null;

  // Finalize any still-running tool calls
  for (const tc of node.toolCalls) {
    if (tc.status === 'running' || tc.status === 'pending') {
      tc.status = status === 'error' ? 'error' : 'done';
      tc.endedAt = node.endedAt;
      tc.duration = tc.startedAt ? (tc.endedAt - tc.startedAt) / 1000 : null;
    }
  }

  // Recursively finalize children that are still active
  for (const childId of node.children) {
    const child = tree.nodes[childId];
    if (child && (child.status === 'running' || child.status === 'thinking' || child.status === 'pending')) {
      atFinalizeNode(childId, status);
    }
  }

  _recalcStats(tree);
}

// ─── Stats ─────────────────────────────────────────────────────────

function _recalcStats(tree: ActivityTree): void {
  const nodes = Object.values(tree.nodes).filter(n => n.id !== tree.rootId);
  const tools = nodes.flatMap(n => n.toolCalls);

  const doneDurations = nodes
    .filter(n => n.duration !== null)
    .map(n => n.duration!);

  const runningNodes = nodes.filter(n => n.status === 'running' || n.status === 'thinking');
  const maxElapsed = runningNodes.reduce((max, n) => {
    const elapsed = n.startedAt ? (Date.now() - n.startedAt) / 1000 : 0;
    return Math.max(max, elapsed);
  }, 0);

  tree.stats = {
    totalAgents: nodes.length,
    runningAgents: runningNodes.length,
    pendingAgents: nodes.filter(n => n.status === 'pending').length,
    doneAgents: nodes.filter(n => n.status === 'done').length,
    errorAgents: nodes.filter(n => n.status === 'error').length,
    totalTools: tools.length,
    doneTools: tools.filter(t => t.status === 'done').length,
    runningTools: tools.filter(t => t.status === 'running').length,
    avgResponseTime: doneDurations.length
      ? doneDurations.reduce((a, b) => a + b, 0) / doneDurations.length
      : 0,
    totalElapsed: maxElapsed,
  };
}

function atGetStats(): MCStats {
  return S.activityTree?.stats || _emptyStats();
}

// ─── Reset ─────────────────────────────────────────────────────────

function atReset(): void {
  _nodeCounter = 0;
  initActivityTree();
}

// ─── Formatting ────────────────────────────────────────────────────

function formatElapsed(ms: number): string {
  if (ms < 1000) return `${Math.round(ms)}ms`;
  const s = Math.floor(ms / 1000);
  if (s < 60) return `${s}s`;
  const m = Math.floor(s / 60);
  const rs = s % 60;
  if (m < 60) return `${m}m ${rs}s`;
  const h = Math.floor(m / 60);
  const rm = m % 60;
  return `${h}h ${rm}m`;
}

function formatDuration(seconds: number | null): string {
  if (seconds === null) return '—';
  if (seconds < 0.01) return '<0.01s';
  if (seconds < 1) return `${seconds.toFixed(2)}s`;
  if (seconds < 60) return `${seconds.toFixed(1)}s`;
  const m = Math.floor(seconds / 60);
  const rs = Math.round(seconds % 60);
  return `${m}m ${rs}s`;
}

// ─── Mock Data ─────────────────────────────────────────────────────

function createMockActivityTree(): ActivityTree {
  initActivityTree();
  const tree = S.activityTree!;
  const now = Date.now();

  // Lotus — running with 2 tool calls
  const lotus = atAddNode({ agentId: 'lotus', task: 'Analysiere Gesundheitsdaten', status: 'running' })!;
  lotus.startedAt = now - 12000;
  atAddToolCall(lotus.id, { name: 'search_files', args: { pattern: 'health*', path: '/data' } });
  atUpdateToolCall(lotus.id, lotus.toolCalls[0].id, { status: 'done', result: '12 matches', duration: 0.3 });
  lotus.toolCalls[0].startedAt = now - 11000;
  lotus.toolCalls[0].endedAt = now - 10997;

  atAddToolCall(lotus.id, { name: 'read_file', args: { path: '/data/health-log.md' } });

  // Sunflower — running with 1 done, 1 running tool
  const sunflower = atAddNode({ agentId: 'sunflower', task: 'Portfolio-Analyse Q1 2026', status: 'running' })!;
  sunflower.startedAt = now - 8000;
  atAddToolCall(sunflower.id, { name: 'browser_navigate', args: { url: 'https://finance.example.com' } });
  atUpdateToolCall(sunflower.id, sunflower.toolCalls[0].id, { status: 'done', result: 'Page loaded', duration: 2.1 });
  sunflower.toolCalls[0].startedAt = now - 7500;
  sunflower.toolCalls[0].endedAt = now - 5400;

  atAddToolCall(sunflower.id, { name: 'terminal', args: { command: 'python3 analyse.py' } });

  // Dandelion — pending
  const dandelion = atAddNode({ agentId: 'dandelion', task: 'Triaging unread messages', status: 'pending' })!;

  // Add a tier-3 sub-agent under Lotus
  const researcher = atAddNode({ parentId: lotus.id, agentId: 'researcher', task: 'Looking up nutrition data', status: 'running', tier: 3 })!;
  researcher.agentEmoji = '🔍';
  researcher.agentName = 'Researcher';
  researcher.startedAt = now - 3000;

  atAddToolCall(researcher.id, { name: 'web_search', args: { query: 'nutrition database API' } });

  _recalcStats(tree);
  return tree;
}

// ─── Make core functions global (IIFE-safe) ────────────────────────
window.initActivityTree = initActivityTree;
window.createMockActivityTree = createMockActivityTree;
window.formatElapsed = formatElapsed;
window.formatDuration = formatDuration;
window.atAddNode = atAddNode;
window.atUpdateNode = atUpdateNode;
window.atGetNode = atGetNode;
window.atAddToolCall = atAddToolCall;
window.atUpdateToolCall = atUpdateToolCall;
window.atFinalizeNode = atFinalizeNode;
window.atGetStats = atGetStats;
window.atReset = atReset;
window._atTrackTool = _atTrackTool;
window._atTrackToolComplete = _atTrackToolComplete;
window._atTrackSubagent = _atTrackSubagent;
window._atTrackDone = _atTrackDone;

// ─── Export for console testing ─────────────────────────────────────
(window as any)._at = {
  init: initActivityTree,
  addNode: atAddNode,
  updateNode: atUpdateNode,
  getNode: atGetNode,
  addToolCall: atAddToolCall,
  updateToolCall: atUpdateToolCall,
  finalize: atFinalizeNode,
  stats: atGetStats,
  reset: atReset,
  mock: createMockActivityTree,
  formatElapsed,
  formatDuration,
  AGENT_META,
  trackSubagent: _atTrackSubagent,
};

// ─── SSE Event Bridge (called from messages.ts) ────────────────────
// Tracks which agent is currently active based on tool calls.
// When delegate_task is detected, creates a new agent node.
// Otherwise, attaches tool calls to the current active agent node.

let _activeAgentNodeId: string | null = null;  // Current agent node receiving tool calls
let _lastToolId: string | null = null;          // Last tool call ID (for complete matching)

function _atTrackTool(d: any): void {
  if (!S.activityTree) initActivityTree();
  const tree = S.activityTree!;

  // Detect delegation — when tool name is 'delegate_task'
  if (d.name === 'delegate_task') {
    // Prefer explicit agent field from SSE payload (set by streaming.py
    // when it detects delegate_task), then fall back to args lookup.
    const agentId = d.agent || d.args?.agentId || d.args?.agent || d.args?.name || 'unknown';
    const task = d.args?.goal || d.args?.prompt || d.args?.task || d.preview || 'Delegated task';
    const node = atAddNode({ agentId, task, status: 'running' });
    if (node) {
      _activeAgentNodeId = node.id;
    }
    return;
  }

  // Determine which node to attach the tool call to
  let targetNodeId = _activeAgentNodeId || tree.rootId;

  // Check if this tool is from a specific agent (from inflight metadata)
  const inflight = (window as any).INFLIGHT?.[S.session?.session_id];
  if (inflight?.thisTurnAgent) {
    const agentKey = inflight.thisTurnAgent;
    // Find or create node for this agent
    const existingNode = Object.values(tree.nodes).find(
      (n: ActivityNode) => n.agentId === agentKey && (n.status === 'running' || n.status === 'thinking')
    );
    if (existingNode) {
      targetNodeId = existingNode.id;
      _activeAgentNodeId = existingNode.id;
    } else if (AGENT_META[agentKey]) {
      // New agent appeared — create node
      const task = inflight.thisTurnModel ? `${inflight.thisTurnModel}` : 'Working...';
      const node = atAddNode({ agentId: agentKey, task, status: 'running' });
      if (node) {
        _activeAgentNodeId = node.id;
        targetNodeId = node.id;
      }
    }
  }

  const tc = atAddToolCall(targetNodeId, {
    name: d.name,
    args: d.args || {},
    status: 'running',
  });
  if (tc) {
    _lastToolId = tc.id;
  }
}

// ─── Subagent lifecycle event handler (from SSE 'subagent' events) ────

function _atTrackSubagent(d: any): void {
  if (!S.activityTree) initActivityTree();
  const tree = S.activityTree!;

  if (d.event_type === 'subagent.start') {
    // Derive agentId from subagent_id if available, otherwise from goal hint.
    // subagent_id format: "lotus-1" → agentId = "lotus"
    let agentId = 'unknown';
    if (d.subagent_id) {
      // Strip numeric suffix e.g. "lotus-1" → "lotus"
      agentId = d.subagent_id.replace(/-\d+$/, '');
    }
    const task = d.goal || d.preview || `Subagent ${d.task_index ?? ''}`.trim();
    const node = atAddNode({ agentId, task, status: 'running' });
    if (node) {
      _activeAgentNodeId = node.id;
    }
    return;
  }

  if (d.event_type === 'subagent.complete' || d.event_type === 'subagent.done') {
    // Find the running node for this subagent and finalize it.
    // Use subagent_id to locate the right node.
    let targetNodeId: string | null = null;
    if (d.subagent_id) {
      const agentId = d.subagent_id.replace(/-\d+$/, '');
      const found = Object.values(tree.nodes).find(
        (n: ActivityNode) => n.agentId === agentId && n.status === 'running'
      );
      if (found) targetNodeId = found.id;
    }
    // Fall back to _activeAgentNodeId if no subagent_id match
    if (!targetNodeId) targetNodeId = _activeAgentNodeId;
    if (targetNodeId && tree.nodes[targetNodeId]) {
      const status = d.status === 'timeout' || d.status === 'error' ? 'error' : 'done';
      atFinalizeNode(targetNodeId, status);
      if (_activeAgentNodeId === targetNodeId) {
        _activeAgentNodeId = null;
      }
    }
    return;
  }
}

function _atTrackToolComplete(d: any): void {
  if (!S.activityTree) return;
  const tree = S.activityTree!;

  // Try to find the matching running tool call across all active nodes
  const activeNodes = Object.values(tree.nodes).filter(
    (n: ActivityNode) => n.status === 'running' || n.status === 'thinking'
  );

  for (const node of activeNodes) {
    for (const tc of node.toolCalls) {
      if (tc.status === 'running' && tc.name === d.name) {
        atUpdateToolCall(node.id, tc.id, {
          status: d.is_error ? 'error' : 'done',
          result: d.preview || d.result,
          duration: d.duration,
        });
        return;
      }
    }
  }
}

function _atTrackDone(): void {
  if (!S.activityTree) return;

  // Finalize the active agent node
  if (_activeAgentNodeId) {
    atFinalizeNode(_activeAgentNodeId, 'done');
    _activeAgentNodeId = null;
  }

  // Reset root status
  const root = S.activityTree.nodes[S.activityTree.rootId];
  if (root) {
    root.status = 'running';
  }
}

function _atTrackNewSession(): void {
  _activeAgentNodeId = null;
  _lastToolId = null;
  if (S.activityTree) {
    atReset();
  }
}
