import { feature } from 'bun:bundle'
import figures from 'figures'
import React, {
  type ReactNode,
  useEffect,
  useEffectEvent,
  useMemo,
  useRef,
  useState,
} from 'react'
import { isCoordinatorMode } from 'src/coordinator/coordinatorMode.js'
import { useTerminalSize } from 'src/hooks/useTerminalSize.js'
import { useAppState, useSetAppState } from 'src/state/AppState.js'
import {
  enterTeammateView,
  exitTeammateView,
} from 'src/state/teammateViewHelpers.js'
import type { ToolUseContext } from 'src/Tool.js'
import {
  DreamTask,
  type DreamTaskState,
} from 'src/tasks/DreamTask/DreamTask.js'
import { InProcessTeammateTask } from 'src/tasks/InProcessTeammateTask/InProcessTeammateTask.js'
import type { InProcessTeammateTaskState } from 'src/tasks/InProcessTeammateTask/types.js'
import type { LocalAgentTaskState } from 'src/tasks/LocalAgentTask/LocalAgentTask.js'
import { LocalAgentTask } from 'src/tasks/LocalAgentTask/LocalAgentTask.js'
import type { LocalShellTaskState } from 'src/tasks/LocalShellTask/guards.js'
import { LocalShellTask } from 'src/tasks/LocalShellTask/LocalShellTask.js'
// Type import is erased at build time — safe even though module is ant-gated.
import type { LocalWorkflowTaskState } from 'src/tasks/LocalWorkflowTask/LocalWorkflowTask.js'
import type { MonitorMcpTaskState } from 'src/tasks/MonitorMcpTask/MonitorMcpTask.js'
import {
  RemoteAgentTask,
  type RemoteAgentTaskState,
} from 'src/tasks/RemoteAgentTask/RemoteAgentTask.js'
import {
  type BackgroundTaskState,
  isBackgroundTask,
  type TaskState,
} from 'src/tasks/types.js'
import type { DeepImmutable } from 'src/types/utils.js'
import { intersperse } from 'src/utils/array.js'
import { TEAM_LEAD_NAME } from 'src/utils/swarm/constants.js'
import { stopUltraplan } from '../../commands/ultraplan.js'
import type { CommandResultDisplay } from '../../commands.js'
import { useRegisterOverlay } from '../../context/overlayContext.js'
import type { ExitState } from '../../hooks/useExitOnCtrlCDWithKeybindings.js'
import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'
import { Box, Text } from '../../ink.js'
import { useKeybindings } from '../../keybindings/useKeybinding.js'
import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js'
import { count } from '../../utils/array.js'
import { Byline } from '../design-system/Byline.js'
import { Dialog } from '../design-system/Dialog.js'
import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'
import { AsyncAgentDetailDialog } from './AsyncAgentDetailDialog.js'
import { BackgroundTask as BackgroundTaskComponent } from './BackgroundTask.js'
import { DreamDetailDialog } from './DreamDetailDialog.js'
import { InProcessTeammateDetailDialog } from './InProcessTeammateDetailDialog.js'
import { RemoteSessionDetailDialog } from './RemoteSessionDetailDialog.js'
import { ShellDetailDialog } from './ShellDetailDialog.js'

type ViewState = { mode: 'list' } | { mode: 'detail'; itemId: string }

type Props = {
  onDone: (
    result?: string,
    options?: { display?: CommandResultDisplay },
  ) => void
  toolUseContext: ToolUseContext
  initialDetailTaskId?: string
}

type ListItem =
  | {
      id: string
      type: 'local_bash'
      label: string
      status: string
      task: DeepImmutable<LocalShellTaskState>
    }
  | {
      id: string
      type: 'remote_agent'
      label: string
      status: string
      task: DeepImmutable<RemoteAgentTaskState>
    }
  | {
      id: string
      type: 'local_agent'
      label: string
      status: string
      task: DeepImmutable<LocalAgentTaskState>
    }
  | {
      id: string
      type: 'in_process_teammate'
      label: string
      status: string
      task: DeepImmutable<InProcessTeammateTaskState>
    }
  | {
      id: string
      type: 'local_workflow'
      label: string
      status: string
      task: DeepImmutable<LocalWorkflowTaskState>
    }
  | {
      id: string
      type: 'monitor_mcp'
      label: string
      status: string
      task: DeepImmutable<MonitorMcpTaskState>
    }
  | {
      id: string
      type: 'dream'
      label: string
      status: string
      task: DeepImmutable<DreamTaskState>
    }
  | {
      id: string
      type: 'leader'
      label: string
      status: 'running'
    }

// WORKFLOW_SCRIPTS is ant-only (build_flags.yaml). Static imports would leak
// ~1.3K lines into external builds. Gate with feature() + require so the
// bundler can dead-code-eliminate the branch.
/* eslint-disable @typescript-eslint/no-require-imports */
const WorkflowDetailDialog = feature('WORKFLOW_SCRIPTS')
  ? (
      require('./WorkflowDetailDialog.js') as typeof import('./WorkflowDetailDialog.js')
    ).WorkflowDetailDialog
  : null
const workflowTaskModule = feature('WORKFLOW_SCRIPTS')
  ? (require('src/tasks/LocalWorkflowTask/LocalWorkflowTask.js') as typeof import('src/tasks/LocalWorkflowTask/LocalWorkflowTask.js'))
  : null
const killWorkflowTask = workflowTaskModule?.killWorkflowTask ?? null
const skipWorkflowAgent = workflowTaskModule?.skipWorkflowAgent ?? null
const retryWorkflowAgent = workflowTaskModule?.retryWorkflowAgent ?? null
// Relative path, not `src/...` path-mapping — Bun's DCE can statically
// resolve + eliminate `./` requires, but path-mapped strings stay opaque
// and survive as dead literals in the bundle. Matches tasks.ts pattern.
const monitorMcpModule = feature('MONITOR_TOOL')
  ? (require('../../tasks/MonitorMcpTask/MonitorMcpTask.js') as typeof import('../../tasks/MonitorMcpTask/MonitorMcpTask.js'))
  : null
const killMonitorMcp = monitorMcpModule?.killMonitorMcp ?? null
const MonitorMcpDetailDialog = feature('MONITOR_TOOL')
  ? (
      require('./MonitorMcpDetailDialog.js') as typeof import('./MonitorMcpDetailDialog.js')
    ).MonitorMcpDetailDialog
  : null
/* eslint-enable @typescript-eslint/no-require-imports */

// Helper to get filtered background tasks (excludes foregrounded local_agent)
function getSelectableBackgroundTasks(
  tasks: Record<string, TaskState> | undefined,
  foregroundedTaskId: string | undefined,
): TaskState[] {
  const backgroundTasks = Object.values(tasks ?? {}).filter(isBackgroundTask)
  return backgroundTasks.filter(
    task => !(task.type === 'local_agent' && task.id === foregroundedTaskId),
  )
}

export function BackgroundTasksDialog({
  onDone,
  toolUseContext,
  initialDetailTaskId,
}: Props): React.ReactNode {
  const tasks = useAppState(s => s.tasks)
  const foregroundedTaskId = useAppState(s => s.foregroundedTaskId)
  const showSpinnerTree = useAppState(s => s.expandedView) === 'teammates'
  const setAppState = useSetAppState()
  const killAgentsShortcut = useShortcutDisplay(
    'chat:killAgents',
    'Chat',
    'ctrl+x ctrl+k',
  )
  const typedTasks = tasks as Record<string, TaskState> | undefined

  // Track if we skipped list view on mount (for back button behavior)
  const skippedListOnMount = useRef(false)

  // Compute initial view state - skip list if caller provided a specific task,
  // or if there's exactly one task
  const [viewState, setViewState] = useState<ViewState>(() => {
    if (initialDetailTaskId) {
      skippedListOnMount.current = true
      return { mode: 'detail', itemId: initialDetailTaskId }
    }
    const allItems = getSelectableBackgroundTasks(
      typedTasks,
      foregroundedTaskId,
    )
    if (allItems.length === 1) {
      skippedListOnMount.current = true
      return { mode: 'detail', itemId: allItems[0]!.id }
    }
    return { mode: 'list' }
  })
  const [selectedIndex, setSelectedIndex] = useState<number>(0)

  // Register as modal overlay so parent Chat keybindings (up/down for history)
  // are deactivated while this dialog is open
  useRegisterOverlay('background-tasks-dialog')

  // Memoize the sorted and categorized items together to ensure stable references
  const {
    bashTasks,
    remoteSessions,
    agentTasks,
    teammateTasks,
    workflowTasks,
    mcpMonitors,
    dreamTasks,
    allSelectableItems,
  } = useMemo(() => {
    // Filter to only show running/pending background tasks, matching the status bar count
    const backgroundTasks = Object.values(typedTasks ?? {}).filter(
      isBackgroundTask,
    )
    const allItems = backgroundTasks.map(toListItem)
    const sorted = allItems.sort((a, b) => {
      const aStatus = a.status
      const bStatus = b.status
      if (aStatus === 'running' && bStatus !== 'running') return -1
      if (aStatus !== 'running' && bStatus === 'running') return 1
      const aTime = 'task' in a ? a.task.startTime : 0
      const bTime = 'task' in b ? b.task.startTime : 0
      return bTime - aTime
    })
    const bash = sorted.filter(item => item.type === 'local_bash')
    const remote = sorted.filter(item => item.type === 'remote_agent')
    // Exclude foregrounded task - it's being viewed in the main UI, not a background task
    const agent = sorted.filter(
      item => item.type === 'local_agent' && item.id !== foregroundedTaskId,
    )
    const workflows = sorted.filter(item => item.type === 'local_workflow')
    const monitorMcp = sorted.filter(item => item.type === 'monitor_mcp')
    const dreamTasks = sorted.filter(item => item.type === 'dream')
    // In spinner-tree mode, exclude teammates from the dialog (they appear in the tree)
    const teammates = showSpinnerTree
      ? []
      : sorted.filter(item => item.type === 'in_process_teammate')
    // Add leader entry when there are teammates, so users can foreground back to leader
    const leaderItem: ListItem[] =
      teammates.length > 0
        ? [
            {
              id: '__leader__',
              type: 'leader',
              label: `@${TEAM_LEAD_NAME}`,
              status: 'running',
            },
          ]
        : []
    return {
      bashTasks: bash,
      remoteSessions: remote,
      agentTasks: agent,
      workflowTasks: workflows,
      mcpMonitors: monitorMcp,
      dreamTasks,
      teammateTasks: [...leaderItem, ...teammates],
      // Order MUST match JSX render order (teammates \u2192 bash \u2192 monitorMcp \u2192
      // remote \u2192 agent \u2192 workflows \u2192 dream) so \u2193/\u2191 navigation moves the cursor
      // visually downward.
      allSelectableItems: [
        ...leaderItem,
        ...teammates,
        ...bash,
        ...monitorMcp,
        ...remote,
        ...agent,
        ...workflows,
        ...dreamTasks,
      ],
    }
  }, [typedTasks, foregroundedTaskId, showSpinnerTree])

  const currentSelection = allSelectableItems[selectedIndex] ?? null

  // Use configurable keybindings for standard navigation and confirm/cancel.
  // confirm:no is handled by Dialog's onCancel prop.
  useKeybindings(
    {
      'confirm:previous': () => setSelectedIndex(prev => Math.max(0, prev - 1)),
      'confirm:next': () =>
        setSelectedIndex(prev =>
          Math.min(allSelectableItems.length - 1, prev + 1),
        ),
      'confirm:yes': () => {
        const current = allSelectableItems[selectedIndex]
        if (current) {
          if (current.type === 'leader') {
            exitTeammateView(setAppState)
            onDone('Viewing leader', { display: 'system' })
          } else {
            setViewState({ mode: 'detail', itemId: current.id })
          }
        }
      },
    },
    { context: 'Confirmation', isActive: viewState.mode === 'list' },
  )

  // Component-specific shortcuts (x=stop, f=foreground, right=zoom) shown in UI.
  // These are task-type and status dependent, not standard dialog keybindings.
  const handleKeyDown = (e: KeyboardEvent) => {
    // Only handle input when in list mode
    if (viewState.mode !== 'list') return

    if (e.key === 'left') {
      e.preventDefault()
      onDone('Background tasks dialog dismissed', { display: 'system' })
      return
    }

    // Compute current selection at the time of the key press
    const currentSelection = allSelectableItems[selectedIndex]
    if (!currentSelection) return // everything below requires a selection

    if (e.key === 'x') {
      e.preventDefault()
      if (
        currentSelection.type === 'local_bash' &&
        currentSelection.status === 'running'
      ) {
        void killShellTask(currentSelection.id)
      } else if (
        currentSelection.type === 'local_agent' &&
        currentSelection.status === 'running'
      ) {
        void killAgentTask(currentSelection.id)
      } else if (
        currentSelection.type === 'in_process_teammate' &&
        currentSelection.status === 'running'
      ) {
        void killTeammateTask(currentSelection.id)
      } else if (
        currentSelection.type === 'local_workflow' &&
        currentSelection.status === 'running' &&
        killWorkflowTask
      ) {
        killWorkflowTask(currentSelection.id, setAppState)
      } else if (
        currentSelection.type === 'monitor_mcp' &&
        currentSelection.status === 'running' &&
        killMonitorMcp
      ) {
        killMonitorMcp(currentSelection.id, setAppState)
      } else if (
        currentSelection.type === 'dream' &&
        currentSelection.status === 'running'
      ) {
        void killDreamTask(currentSelection.id)
      } else if (
        currentSelection.type === 'remote_agent' &&
        currentSelection.status === 'running'
      ) {
        if (currentSelection.task.isUltraplan) {
          void stopUltraplan(
            currentSelection.id,
            currentSelection.task.sessionId,
            setAppState,
          )
        } else {
          void killRemoteAgentTask(currentSelection.id)
        }
      }
    }

    if (e.key === 'f') {
      if (
        currentSelection.type === 'in_process_teammate' &&
        currentSelection.status === 'running'
      ) {
        e.preventDefault()
        enterTeammateView(currentSelection.id, setAppState)
        onDone('Viewing teammate', { display: 'system' })
      } else if (currentSelection.type === 'leader') {
        e.preventDefault()
        exitTeammateView(setAppState)
        onDone('Viewing leader', { display: 'system' })
      }
    }
  }

  async function killShellTask(taskId: string): Promise<void> {
    await LocalShellTask.kill(taskId, setAppState)
  }

  async function killAgentTask(taskId: string): Promise<void> {
    await LocalAgentTask.kill(taskId, setAppState)
  }

  async function killTeammateTask(taskId: string): Promise<void> {
    await InProcessTeammateTask.kill(taskId, setAppState)
  }

  async function killDreamTask(taskId: string): Promise<void> {
    await DreamTask.kill(taskId, setAppState)
  }

  async function killRemoteAgentTask(taskId: string): Promise<void> {
    await RemoteAgentTask.kill(taskId, setAppState)
  }

  // Wrap onDone in useEffectEvent to get a stable reference that always calls
  // the current onDone callback without causing the effect to re-fire.
  const onDoneEvent = useEffectEvent(onDone)

  useEffect(() => {
    if (viewState.mode !== 'list') {
      const task = (typedTasks ?? {})[viewState.itemId]
      // Workflow tasks get a grace: their detail view stays open through
      // completion so the user sees the final state before eviction.
      if (
        !task ||
        (task.type !== 'local_workflow' && !isBackgroundTask(task))
      ) {
        // Task was removed or is no longer a background task (e.g. killed).
        // If we skipped the list on mount, close the dialog entirely.
        if (skippedListOnMount.current) {
          onDoneEvent('Background tasks dialog dismissed', {
            display: 'system',
          })
        } else {
          setViewState({ mode: 'list' })
        }
      }
    }

    const totalItems = allSelectableItems.length
    if (selectedIndex >= totalItems && totalItems > 0) {
      setSelectedIndex(totalItems - 1)
    }
  }, [viewState, typedTasks, selectedIndex, allSelectableItems, onDoneEvent])

  // Helper to go back to list view (or close dialog if we skipped list on
  // mount AND there's still only ≤1 item). Checking current count prevents
  // the stale-state trap: if you opened with 1 task (auto-skipped to detail),
  // then a second task started, 'back' should show the list — not close.
  const goBackToList = () => {
    if (skippedListOnMount.current && allSelectableItems.length <= 1) {
      onDone('Background tasks dialog dismissed', { display: 'system' })
    } else {
      skippedListOnMount.current = false
      setViewState({ mode: 'list' })
    }
  }

  // If an item is selected, show the appropriate view
  if (viewState.mode !== 'list' && typedTasks) {
    const task = typedTasks[viewState.itemId]
    if (!task) {
      return null
    }

    // Detail mode - show appropriate detail dialog
    switch (task.type) {
      case 'local_bash':
        return (
          <ShellDetailDialog
            shell={task}
            onDone={onDone}
            onKillShell={() => void killShellTask(task.id)}
            onBack={goBackToList}
            key={`shell-${task.id}`}
          />
        )
      case 'local_agent':
        return (
          <AsyncAgentDetailDialog
            agent={task}
            onDone={onDone}
            onKillAgent={() => void killAgentTask(task.id)}
            onBack={goBackToList}
            key={`agent-${task.id}`}
          />
        )
      case 'remote_agent':
        return (
          <RemoteSessionDetailDialog
            session={task}
            onDone={onDone}
            toolUseContext={toolUseContext}
            onBack={goBackToList}
            onKill={
              task.status !== 'running'
                ? undefined
                : task.isUltraplan
                  ? () =>
                      void stopUltraplan(task.id, task.sessionId, setAppState)
                  : () => void killRemoteAgentTask(task.id)
            }
            key={`session-${task.id}`}
          />
        )
      case 'in_process_teammate':
        return (
          <InProcessTeammateDetailDialog
            teammate={task}
            onDone={onDone}
            onKill={
              task.status === 'running'
                ? () => void killTeammateTask(task.id)
                : undefined
            }
            onBack={goBackToList}
            onForeground={
              task.status === 'running'
                ? () => {
                    enterTeammateView(task.id, setAppState)
                    onDone('Viewing teammate', { display: 'system' })
                  }
                : undefined
            }
            key={`teammate-${task.id}`}
          />
        )
      case 'local_workflow':
        if (!WorkflowDetailDialog) return null
        return (
          <WorkflowDetailDialog
            workflow={task}
            onDone={onDone}
            onKill={
              task.status === 'running' && killWorkflowTask
                ? () => killWorkflowTask(task.id, setAppState)
                : undefined
            }
            onSkipAgent={
              task.status === 'running' && skipWorkflowAgent
                ? agentId => skipWorkflowAgent(task.id, agentId, setAppState)
                : undefined
            }
            onRetryAgent={
              task.status === 'running' && retryWorkflowAgent
                ? agentId => retryWorkflowAgent(task.id, agentId, setAppState)
                : undefined
            }
            onBack={goBackToList}
            key={`workflow-${task.id}`}
          />
        )
      case 'monitor_mcp':
        if (!MonitorMcpDetailDialog) return null
        return (
          <MonitorMcpDetailDialog
            task={task}
            onKill={
              task.status === 'running' && killMonitorMcp
                ? () => killMonitorMcp(task.id, setAppState)
                : undefined
            }
            onBack={goBackToList}
            key={`monitor-mcp-${task.id}`}
          />
        )
      case 'dream':
        return (
          <DreamDetailDialog
            task={task}
            onDone={() =>
              onDone('Background tasks dialog dismissed', {
                display: 'system',
              })
            }
            onBack={goBackToList}
            onKill={
              task.status === 'running'
                ? () => void killDreamTask(task.id)
                : undefined
            }
            key={`dream-${task.id}`}
          />
        )
    }
  }

  const runningBashCount = count(bashTasks, _ => _.status === 'running')
  const runningAgentCount =
    count(
      remoteSessions,
      _ => _.status === 'running' || _.status === 'pending',
    ) + count(agentTasks, _ => _.status === 'running')
  const runningTeammateCount = count(teammateTasks, _ => _.status === 'running')
  const subtitle = intersperse(
    [
      ...(runningTeammateCount > 0
        ? [
            <Text key="teammates">
              {runningTeammateCount}{' '}
              {runningTeammateCount !== 1 ? 'agents' : 'agent'}
            </Text>,
          ]
        : []),
      ...(runningBashCount > 0
        ? [
            <Text key="shells">
              {runningBashCount}{' '}
              {runningBashCount !== 1 ? 'active shells' : 'active shell'}
            </Text>,
          ]
        : []),
      ...(runningAgentCount > 0
        ? [
            <Text key="agents">
              {runningAgentCount}{' '}
              {runningAgentCount !== 1 ? 'active agents' : 'active agent'}
            </Text>,
          ]
        : []),
    ],
    index => <Text key={`separator-${index}`}> · </Text>,
  )

  const actions = [
    <KeyboardShortcutHint key="upDown" shortcut="↑/↓" action="select" />,
    <KeyboardShortcutHint key="enter" shortcut="Enter" action="view" />,
    ...(currentSelection?.type === 'in_process_teammate' &&
    currentSelection.status === 'running'
      ? [
          <KeyboardShortcutHint
            key="foreground"
            shortcut="f"
            action="foreground"
          />,
        ]
      : []),
    ...((currentSelection?.type === 'local_bash' ||
      currentSelection?.type === 'local_agent' ||
      currentSelection?.type === 'in_process_teammate' ||
      currentSelection?.type === 'local_workflow' ||
      currentSelection?.type === 'monitor_mcp' ||
      currentSelection?.type === 'dream' ||
      currentSelection?.type === 'remote_agent') &&
    currentSelection.status === 'running'
      ? [<KeyboardShortcutHint key="kill" shortcut="x" action="stop" />]
      : []),
    ...(agentTasks.some(t => t.status === 'running')
      ? [
          <KeyboardShortcutHint
            key="kill-all"
            shortcut={killAgentsShortcut}
            action="stop all agents"
          />,
        ]
      : []),
    <KeyboardShortcutHint key="esc" shortcut="←/Esc" action="close" />,
  ]

  const handleCancel = () =>
    onDone('Background tasks dialog dismissed', { display: 'system' })

  function renderInputGuide(exitState: ExitState): React.ReactNode {
    if (exitState.pending) {
      return <Text>Press {exitState.keyName} again to exit</Text>
    }
    return <Byline>{actions}</Byline>
  }

  return (
    <Box
      flexDirection="column"
      tabIndex={0}
      autoFocus
      onKeyDown={handleKeyDown}
    >
      <Dialog
        title="Background tasks"
        subtitle={<>{subtitle}</>}
        onCancel={handleCancel}
        color="background"
        inputGuide={renderInputGuide}
      >
        {allSelectableItems.length === 0 ? (
          <Text dimColor>No tasks currently running</Text>
        ) : (
          <Box flexDirection="column">
            {teammateTasks.length > 0 && (
              <Box flexDirection="column">
                {(bashTasks.length > 0 ||
                  remoteSessions.length > 0 ||
                  agentTasks.length > 0) && (
                  <Text dimColor>
                    <Text bold>{'  '}Agents</Text> (
                    {count(teammateTasks, i => i.type !== 'leader')})
                  </Text>
                )}
                <Box flexDirection="column">
                  <TeammateTaskGroups
                    teammateTasks={teammateTasks}
                    currentSelectionId={currentSelection?.id}
                  />
                </Box>
              </Box>
            )}

            {bashTasks.length > 0 && (
              <Box
                flexDirection="column"
                marginTop={teammateTasks.length > 0 ? 1 : 0}
              >
                {(teammateTasks.length > 0 ||
                  remoteSessions.length > 0 ||
                  agentTasks.length > 0) && (
                  <Text dimColor>
                    <Text bold>{'  '}Shells</Text> ({bashTasks.length})
                  </Text>
                )}
                <Box flexDirection="column">
                  {bashTasks.map(item => (
                    <Item
                      key={item.id}
                      item={item}
                      isSelected={item.id === currentSelection?.id}
                    />
                  ))}
                </Box>
              </Box>
            )}

            {mcpMonitors.length > 0 && (
              <Box
                flexDirection="column"
                marginTop={
                  teammateTasks.length > 0 || bashTasks.length > 0 ? 1 : 0
                }
              >
                <Text dimColor>
                  <Text bold>{'  '}Monitors</Text> ({mcpMonitors.length})
                </Text>
                <Box flexDirection="column">
                  {mcpMonitors.map(item => (
                    <Item
                      key={item.id}
                      item={item}
                      isSelected={item.id === currentSelection?.id}
                    />
                  ))}
                </Box>
              </Box>
            )}

            {remoteSessions.length > 0 && (
              <Box
                flexDirection="column"
                marginTop={
                  teammateTasks.length > 0 ||
                  bashTasks.length > 0 ||
                  mcpMonitors.length > 0
                    ? 1
                    : 0
                }
              >
                <Text dimColor>
                  <Text bold>{'  '}Remote agents</Text> ({remoteSessions.length}
                  )
                </Text>
                <Box flexDirection="column">
                  {remoteSessions.map(item => (
                    <Item
                      key={item.id}
                      item={item}
                      isSelected={item.id === currentSelection?.id}
                    />
                  ))}
                </Box>
              </Box>
            )}

            {agentTasks.length > 0 && (
              <Box
                flexDirection="column"
                marginTop={
                  teammateTasks.length > 0 ||
                  bashTasks.length > 0 ||
                  mcpMonitors.length > 0 ||
                  remoteSessions.length > 0
                    ? 1
                    : 0
                }
              >
                <Text dimColor>
                  <Text bold>{'  '}Local agents</Text> ({agentTasks.length})
                </Text>
                <Box flexDirection="column">
                  {agentTasks.map(item => (
                    <Item
                      key={item.id}
                      item={item}
                      isSelected={item.id === currentSelection?.id}
                    />
                  ))}
                </Box>
              </Box>
            )}

            {workflowTasks.length > 0 && (
              <Box
                flexDirection="column"
                marginTop={
                  teammateTasks.length > 0 ||
                  bashTasks.length > 0 ||
                  mcpMonitors.length > 0 ||
                  remoteSessions.length > 0 ||
                  agentTasks.length > 0
                    ? 1
                    : 0
                }
              >
                <Text dimColor>
                  <Text bold>{'  '}Workflows</Text> ({workflowTasks.length})
                </Text>
                <Box flexDirection="column">
                  {workflowTasks.map(item => (
                    <Item
                      key={item.id}
                      item={item}
                      isSelected={item.id === currentSelection?.id}
                    />
                  ))}
                </Box>
              </Box>
            )}

            {dreamTasks.length > 0 && (
              <Box
                flexDirection="column"
                marginTop={
                  teammateTasks.length > 0 ||
                  bashTasks.length > 0 ||
                  mcpMonitors.length > 0 ||
                  remoteSessions.length > 0 ||
                  agentTasks.length > 0 ||
                  workflowTasks.length > 0
                    ? 1
                    : 0
                }
              >
                <Box flexDirection="column">
                  {dreamTasks.map(item => (
                    <Item
                      key={item.id}
                      item={item}
                      isSelected={item.id === currentSelection?.id}
                    />
                  ))}
                </Box>
              </Box>
            )}
          </Box>
        )}
      </Dialog>
    </Box>
  )
}

function toListItem(task: BackgroundTaskState): ListItem {
  switch (task.type) {
    case 'local_bash':
      return {
        id: task.id,
        type: 'local_bash',
        label: task.kind === 'monitor' ? task.description : task.command,
        status: task.status,
        task,
      }
    case 'remote_agent':
      return {
        id: task.id,
        type: 'remote_agent',
        label: task.title,
        status: task.status,
        task,
      }
    case 'local_agent':
      return {
        id: task.id,
        type: 'local_agent',
        label: task.description,
        status: task.status,
        task,
      }
    case 'in_process_teammate':
      return {
        id: task.id,
        type: 'in_process_teammate',
        label: `@${task.identity.agentName}`,
        status: task.status,
        task,
      }
    case 'local_workflow':
      return {
        id: task.id,
        type: 'local_workflow',
        label: task.summary ?? task.description,
        status: task.status,
        task,
      }
    case 'monitor_mcp':
      return {
        id: task.id,
        type: 'monitor_mcp',
        label: task.description,
        status: task.status,
        task,
      }
    case 'dream':
      return {
        id: task.id,
        type: 'dream',
        label: task.description,
        status: task.status,
        task,
      }
  }
}

function Item({
  item,
  isSelected,
}: {
  item: ListItem
  isSelected: boolean
}): ReactNode {
  const { columns } = useTerminalSize()
  // Dialog border (2) + padding (2) + pointer prefix (2) + name/status overhead (~20)
  const maxActivityWidth = Math.max(30, columns - 26)
  // In coordinator mode, use grey pointer instead of blue
  const useGreyPointer = isCoordinatorMode()

  return (
    <Box flexDirection="row">
      <Text dimColor={useGreyPointer && isSelected}>
        {isSelected ? figures.pointer + ' ' : '  '}
      </Text>
      <Text color={isSelected && !useGreyPointer ? 'suggestion' : undefined}>
        {item.type === 'leader' ? (
          <Text>@{TEAM_LEAD_NAME}</Text>
        ) : (
          <BackgroundTaskComponent
            task={item.task}
            maxActivityWidth={maxActivityWidth}
          />
        )}
      </Text>
    </Box>
  )
}

function TeammateTaskGroups({
  teammateTasks,
  currentSelectionId,
}: {
  teammateTasks: ListItem[]
  currentSelectionId: string | undefined
}): ReactNode {
  // Separate leader from teammates, group teammates by team
  const leaderItems = teammateTasks.filter(i => i.type === 'leader')
  const teammateItems = teammateTasks.filter(
    i => i.type === 'in_process_teammate',
  )
  const teams = new Map<string, typeof teammateItems>()
  for (const item of teammateItems) {
    const teamName = item.task.identity.teamName
    const group = teams.get(teamName)
    if (group) {
      group.push(item)
    } else {
      teams.set(teamName, [item])
    }
  }
  const teamEntries = [...teams.entries()]
  return (
    <>
      {teamEntries.map(([teamName, items]) => {
        const memberCount = items.length + leaderItems.length
        return (
          <Box key={teamName} flexDirection="column">
            <Text dimColor>
              {'  '}Team: {teamName} ({memberCount})
            </Text>
            {/* Render leader first within each team */}
            {leaderItems.map(item => (
              <Item
                key={`${item.id}-${teamName}`}
                item={item}
                isSelected={item.id === currentSelectionId}
              />
            ))}
            {items.map(item => (
              <Item
                key={item.id}
                item={item}
                isSelected={item.id === currentSelectionId}
              />
            ))}
          </Box>
        )
      })}
    </>
  )
}
