import figures from 'figures'
import * as React from 'react'
import { useMemo, useState } from 'react'
import { useTerminalSize } from 'src/hooks/useTerminalSize.js'
import { stringWidth } from '@anthropic/ink'
import { useAppState, useSetAppState } from 'src/state/AppState.js'
import {
  enterTeammateView,
  exitTeammateView,
} from 'src/state/teammateViewHelpers.js'
import { isPanelAgentTask } from 'src/tasks/LocalAgentTask/LocalAgentTask.js'
import { getPillLabel, pillNeedsCta } from 'src/tasks/pillLabel.js'
import {
  type BackgroundTaskState,
  isBackgroundTask,
  type TaskState,
} from 'src/tasks/types.js'
import { calculateHorizontalScrollWindow } from 'src/utils/horizontalScroll.js'
import { Box, Text } from '@anthropic/ink'
import {
  AGENT_COLOR_TO_THEME_COLOR,
  AGENT_COLORS,
  type AgentColorName,
} from '@claude-code-best/builtin-tools/tools/AgentTool/agentColorManager.js'
import type { Theme } from '../../utils/theme.js'
import { KeyboardShortcutHint } from '@anthropic/ink'
import { shouldHideTasksFooter } from './taskStatusUtils.js'

type Props = {
  tasksSelected: boolean
  isViewingTeammate?: boolean
  teammateFooterIndex?: number
  isLeaderIdle?: boolean
  onOpenDialog?: (taskId?: string) => void
}

export function BackgroundTaskStatus({
  tasksSelected,
  isViewingTeammate,
  teammateFooterIndex = 0,
  isLeaderIdle = false,
  onOpenDialog,
}: Props): React.ReactNode {
  const setAppState = useSetAppState()
  const { columns } = useTerminalSize()
  const tasks = useAppState(s => s.tasks)
  const viewingAgentTaskId = useAppState(s => s.viewingAgentTaskId)

  const runningTasks = useMemo(
    () =>
      (Object.values(tasks ?? {}) as TaskState[]).filter(
        t =>
          isBackgroundTask(t) &&
          !(process.env.USER_TYPE === 'ant' && isPanelAgentTask(t)),
      ),
    [tasks],
  )

  // Check if all tasks are in-process teammates (team mode)
  // In spinner-tree mode, don't show teammate pills (teammates appear in the spinner tree)
  const expandedView = useAppState(s => s.expandedView)
  const showSpinnerTree = expandedView === 'teammates'
  const allTeammates =
    !showSpinnerTree &&
    runningTasks.length > 0 &&
    runningTasks.every(t => t.type === 'in_process_teammate')

  // Memoize teammate-related computations at the top level (rules of hooks)
  const teammateEntries = useMemo(
    () =>
      runningTasks
        .filter(
          (t): t is BackgroundTaskState & { type: 'in_process_teammate' } =>
            t.type === 'in_process_teammate',
        )
        .sort((a, b) =>
          a.identity.agentName.localeCompare(b.identity.agentName),
        ),
    [runningTasks],
  )

  // Build array of all pills with their activity state
  // Each pill is "@{name}" and separator is " " (1 char)
  // Sort idle agents to the end, but only when not in selection mode
  // to avoid reordering while user is arrowing through the list
  // "main" always stays first regardless of idle state
  const allPills = useMemo(() => {
    const mainPill = {
      name: 'main',
      color: undefined as keyof Theme | undefined,
      isIdle: isLeaderIdle,
      taskId: undefined as string | undefined,
    }

    const teammatePills = teammateEntries.map(t => ({
      name: t.identity.agentName,
      color: getAgentThemeColor(t.identity.color),
      isIdle: t.isIdle,
      taskId: t.id,
    }))

    // Only sort teammates when not selecting to avoid reordering during navigation
    if (!tasksSelected) {
      teammatePills.sort((a, b) => {
        // Active agents first, idle agents last
        if (a.isIdle !== b.isIdle) return a.isIdle ? 1 : -1
        return 0 // Keep original order within each group
      })
    }

    // main always first, then sorted teammates
    const pills = [mainPill, ...teammatePills]

    // Add idx after sorting
    return pills.map((pill, i) => ({ ...pill, idx: i }))
  }, [teammateEntries, isLeaderIdle, tasksSelected])

  // Calculate pill widths (including separator space, except first)
  const pillWidths = useMemo(
    () =>
      allPills.map((pill, i) => {
        const pillText = `@${pill.name}`
        // First pill has no leading space, others have 1 space separator
        return stringWidth(pillText) + (i > 0 ? 1 : 0)
      }),
    [allPills],
  )

  if (allTeammates || (!showSpinnerTree && isViewingTeammate)) {
    const selectedIdx = tasksSelected ? teammateFooterIndex : -1
    // Which agent is currently foregrounded (bold)
    const viewedIdx = viewingAgentTaskId
      ? teammateEntries.findIndex(t => t.id === viewingAgentTaskId) + 1
      : 0 // 0 = main/leader

    // Calculate available width for pills
    // Reserve space for: arrows, hint, and minimal padding
    // Pills are rendered on their own line when in team mode
    const ARROW_WIDTH = 2 // arrow char + space
    const HINT_WIDTH = 20 // shift+↓ to expand
    const PADDING = 4 // minimal safety margin
    const availableWidth = Math.max(20, columns - HINT_WIDTH - PADDING)

    // Calculate visible window of pills
    const { startIndex, endIndex, showLeftArrow, showRightArrow } =
      calculateHorizontalScrollWindow(
        pillWidths,
        availableWidth,
        ARROW_WIDTH,
        selectedIdx >= 0 ? selectedIdx : 0,
      )

    const visiblePills = allPills.slice(startIndex, endIndex)

    return (
      <>
        {showLeftArrow && <Text dimColor>{figures.arrowLeft} </Text>}
        {visiblePills.map((pill, i) => {
          // First visible pill has no leading separator
          // (left arrow already provides spacing if present)
          const needsSeparator = i > 0
          return (
            <React.Fragment key={pill.name}>
              {needsSeparator && <Text> </Text>}
              <AgentPill
                name={pill.name}
                color={pill.color}
                isSelected={selectedIdx === pill.idx}
                isViewed={viewedIdx === pill.idx}
                isIdle={pill.isIdle}
                onClick={() =>
                  pill.taskId
                    ? enterTeammateView(pill.taskId, setAppState)
                    : exitTeammateView(setAppState)
                }
              />
            </React.Fragment>
          )
        })}
        {showRightArrow && <Text dimColor> {figures.arrowRight}</Text>}
        <Text dimColor>
          {' · '}
          <KeyboardShortcutHint shortcut="shift + ↓" action="expand" />
        </Text>
      </>
    )
  }

  // In spinner-tree mode, don't show any footer status for teammates
  // (they appear in the spinner tree above)
  if (shouldHideTasksFooter(tasks ?? {}, showSpinnerTree)) {
    return null
  }

  if (runningTasks.length === 0) {
    return null
  }

  return (
    <>
      <SummaryPill selected={tasksSelected} onClick={onOpenDialog}>
        {getPillLabel(runningTasks)}
      </SummaryPill>
      {pillNeedsCta(runningTasks) && (
        <Text dimColor> · {figures.arrowDown} to view</Text>
      )}
    </>
  )
}

type AgentPillProps = {
  name: string
  color?: keyof Theme
  isSelected: boolean
  isViewed: boolean
  isIdle: boolean
  onClick?: () => void
}

function AgentPill({
  name,
  color,
  isSelected,
  isViewed,
  isIdle,
  onClick,
}: AgentPillProps): React.ReactNode {
  const [hover, setHover] = useState(false)
  // Hover mirrors the keyboard-selected look so the affordance is familiar.
  const highlighted = isSelected || hover

  let label: React.ReactNode
  if (highlighted) {
    label = color ? (
      <Text backgroundColor={color} color="inverseText" bold={isViewed}>
        @{name}
      </Text>
    ) : (
      <Text color="background" inverse bold={isViewed}>
        @{name}
      </Text>
    )
  } else if (isIdle) {
    label = (
      <Text dimColor bold={isViewed}>
        @{name}
      </Text>
    )
  } else if (isViewed) {
    label = (
      <Text color={color} bold>
        @{name}
      </Text>
    )
  } else {
    label = (
      <Text color={color} dimColor={!color}>
        @{name}
      </Text>
    )
  }

  if (!onClick) return label
  return (
    <Box
      onClick={onClick}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
    >
      {label}
    </Box>
  )
}

function SummaryPill({
  selected,
  onClick,
  children,
}: {
  selected: boolean
  onClick?: () => void
  children: React.ReactNode
}): React.ReactNode {
  const [hover, setHover] = useState(false)
  const label = (
    <Text color="background" inverse={selected || hover}>
      {children}
    </Text>
  )
  if (!onClick) return label
  return (
    <Box
      onClick={onClick}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
    >
      {label}
    </Box>
  )
}

function getAgentThemeColor(
  colorName: string | undefined,
): keyof Theme | undefined {
  if (!colorName) return undefined
  if (AGENT_COLORS.includes(colorName as AgentColorName)) {
    return AGENT_COLOR_TO_THEME_COLOR[colorName as AgentColorName]
  }
  return undefined
}
