import figures from 'figures'
import * as React from 'react'
import { useTerminalSize } from '../hooks/useTerminalSize.js'
import { Box, Text, stringWidth } from '@anthropic/ink'
import { useAppState } from '../state/AppState.js'
import { isInProcessTeammateTask } from '../tasks/InProcessTeammateTask/types.js'
import {
  AGENT_COLOR_TO_THEME_COLOR,
  type AgentColorName,
} from '@claude-code-best/builtin-tools/tools/AgentTool/agentColorManager.js'
import { isAgentSwarmsEnabled } from '../utils/agentSwarmsEnabled.js'
import { count } from '../utils/array.js'
import { summarizeRecentActivities } from '../utils/collapseReadSearch.js'
import { truncateToWidth } from '../utils/format.js'
import { isTodoV2Enabled, type Task } from '../utils/tasks.js'
import type { Theme } from '../utils/theme.js'
import ThemedText from './design-system/ThemedText.js'

type Props = {
  tasks: Task[]
  isStandalone?: boolean
}

const RECENT_COMPLETED_TTL_MS = 30_000

function byIdAsc(a: Task, b: Task): number {
  const aNum = parseInt(a.id, 10)
  const bNum = parseInt(b.id, 10)
  if (!isNaN(aNum) && !isNaN(bNum)) {
    return aNum - bNum
  }
  return a.id.localeCompare(b.id)
}

export function TaskListV2({
  tasks,
  isStandalone = false,
}: Props): React.ReactNode {
  const teamContext = useAppState(s => s.teamContext)
  const appStateTasks = useAppState(s => s.tasks)
  const [, forceUpdate] = React.useState(0)
  const { rows, columns } = useTerminalSize()

  // Track when each task was last observed transitioning to completed
  const completionTimestampsRef = React.useRef(new Map<string, number>())
  const previousCompletedIdsRef = React.useRef<Set<string> | null>(null)
  if (previousCompletedIdsRef.current === null) {
    previousCompletedIdsRef.current = new Set(
      tasks.filter(t => t.status === 'completed').map(t => t.id),
    )
  }
  const maxDisplay = rows <= 10 ? 0 : Math.min(10, Math.max(3, rows - 14))

  // Update completion timestamps: reset when a task transitions to completed
  const currentCompletedIds = new Set(
    tasks.filter(t => t.status === 'completed').map(t => t.id),
  )
  const now = Date.now()
  for (const id of currentCompletedIds) {
    if (!previousCompletedIdsRef.current.has(id)) {
      completionTimestampsRef.current.set(id, now)
    }
  }
  for (const id of completionTimestampsRef.current.keys()) {
    if (!currentCompletedIds.has(id)) {
      completionTimestampsRef.current.delete(id)
    }
  }
  previousCompletedIdsRef.current = currentCompletedIds

  // Schedule re-render when the next recent completion expires.
  // Depend on `tasks` so the timer is only reset when the task list changes,
  // not on every render (which was causing unnecessary work).
  React.useEffect(() => {
    if (completionTimestampsRef.current.size === 0) {
      return
    }
    const currentNow = Date.now()
    let earliestExpiry = Infinity
    for (const ts of completionTimestampsRef.current.values()) {
      const expiry = ts + RECENT_COMPLETED_TTL_MS
      if (expiry > currentNow && expiry < earliestExpiry) {
        earliestExpiry = expiry
      }
    }
    if (earliestExpiry === Infinity) {
      return
    }
    const timer = setTimeout(
      forceUpdate => forceUpdate((n: number) => n + 1),
      earliestExpiry - currentNow,
      forceUpdate,
    )
    return () => clearTimeout(timer)
  }, [tasks])

  if (!isTodoV2Enabled()) {
    return null
  }

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

  // Build a map of teammate name -> theme color
  const teammateColors: Record<string, keyof Theme> = {}
  if (isAgentSwarmsEnabled() && teamContext?.teammates) {
    for (const teammate of Object.values(teamContext.teammates)) {
      if (teammate.color) {
        const themeColor =
          AGENT_COLOR_TO_THEME_COLOR[teammate.color as AgentColorName]
        if (themeColor) {
          teammateColors[teammate.name] = themeColor
        }
      }
    }
  }

  // Build a map of teammate name -> current activity description
  // Map both agentName ("researcher") and agentId ("researcher@team") so
  // task owners match regardless of which format the model used.
  // Rolls up consecutive search/read tool uses into a compact summary.
  // Also track which teammates are still running (not shut down).
  const teammateActivity: Record<string, string> = {}
  const activeTeammates = new Set<string>()
  if (isAgentSwarmsEnabled()) {
    for (const bgTask of Object.values(appStateTasks)) {
      if (isInProcessTeammateTask(bgTask) && bgTask.status === 'running') {
        activeTeammates.add(bgTask.identity.agentName)
        activeTeammates.add(bgTask.identity.agentId)
        const activities = bgTask.progress?.recentActivities
        const desc =
          (activities && summarizeRecentActivities(activities)) ??
          bgTask.progress?.lastActivity?.activityDescription
        if (desc) {
          teammateActivity[bgTask.identity.agentName] = desc
          teammateActivity[bgTask.identity.agentId] = desc
        }
      }
    }
  }

  // Get task counts for display
  const completedCount = count(tasks, t => t.status === 'completed')
  const pendingCount = count(tasks, t => t.status === 'pending')
  const inProgressCount = tasks.length - completedCount - pendingCount
  // Unresolved tasks (open or in_progress) block dependent tasks
  const unresolvedTaskIds = new Set(
    tasks.filter(t => t.status !== 'completed').map(t => t.id),
  )

  // Check if we need to truncate
  const needsTruncation = tasks.length > maxDisplay

  let visibleTasks: Task[]
  let hiddenTasks: Task[]

  if (needsTruncation) {
    // Prioritize: recently completed (within 30s), in-progress, pending, older completed
    const recentCompleted: Task[] = []
    const olderCompleted: Task[] = []
    for (const task of tasks.filter(t => t.status === 'completed')) {
      const ts = completionTimestampsRef.current.get(task.id)
      if (ts && now - ts < RECENT_COMPLETED_TTL_MS) {
        recentCompleted.push(task)
      } else {
        olderCompleted.push(task)
      }
    }
    recentCompleted.sort(byIdAsc)
    olderCompleted.sort(byIdAsc)
    const inProgress = tasks
      .filter(t => t.status === 'in_progress')
      .sort(byIdAsc)
    const pending = tasks
      .filter(t => t.status === 'pending')
      .sort((a, b) => {
        const aBlocked = a.blockedBy.some(id => unresolvedTaskIds.has(id))
        const bBlocked = b.blockedBy.some(id => unresolvedTaskIds.has(id))
        if (aBlocked !== bBlocked) {
          return aBlocked ? 1 : -1
        }
        return byIdAsc(a, b)
      })

    const prioritized = [
      ...recentCompleted,
      ...inProgress,
      ...pending,
      ...olderCompleted,
    ]
    visibleTasks = prioritized.slice(0, maxDisplay)
    hiddenTasks = prioritized.slice(maxDisplay)
  } else {
    // No truncation needed — sort by ID for stable ordering
    visibleTasks = [...tasks].sort(byIdAsc)
    hiddenTasks = []
  }

  let hiddenSummary = ''
  if (hiddenTasks.length > 0) {
    const parts: string[] = []
    const hiddenPending = count(hiddenTasks, t => t.status === 'pending')
    const hiddenInProgress = count(hiddenTasks, t => t.status === 'in_progress')
    const hiddenCompleted = count(hiddenTasks, t => t.status === 'completed')
    if (hiddenInProgress > 0) {
      parts.push(`${hiddenInProgress} in progress`)
    }
    if (hiddenPending > 0) {
      parts.push(`${hiddenPending} pending`)
    }
    if (hiddenCompleted > 0) {
      parts.push(`${hiddenCompleted} completed`)
    }
    hiddenSummary = ` … +${parts.join(', ')}`
  }

  const content = (
    <>
      {visibleTasks.map(task => (
        <TaskItem
          key={task.id}
          task={task}
          ownerColor={task.owner ? teammateColors[task.owner] : undefined}
          openBlockers={task.blockedBy.filter(id => unresolvedTaskIds.has(id))}
          activity={task.owner ? teammateActivity[task.owner] : undefined}
          ownerActive={task.owner ? activeTeammates.has(task.owner) : false}
          columns={columns}
        />
      ))}
      {maxDisplay > 0 && hiddenSummary && <Text dimColor>{hiddenSummary}</Text>}
    </>
  )

  if (isStandalone) {
    return (
      <Box flexDirection="column" marginTop={1} marginLeft={2}>
        <Box>
          <Text dimColor>
            <Text bold>{tasks.length}</Text>
            {' tasks ('}
            <Text bold>{completedCount}</Text>
            {' done, '}
            {inProgressCount > 0 && (
              <>
                <Text bold>{inProgressCount}</Text>
                {' in progress, '}
              </>
            )}
            <Text bold>{pendingCount}</Text>
            {' open)'}
          </Text>
        </Box>
        {content}
      </Box>
    )
  }

  return <Box flexDirection="column">{content}</Box>
}

type TaskItemProps = {
  task: Task
  ownerColor?: keyof Theme
  openBlockers: string[]
  activity?: string
  ownerActive: boolean
  columns: number
}

function getTaskIcon(status: Task['status']): {
  icon: string
  color: keyof Theme | undefined
} {
  switch (status) {
    case 'completed':
      return { icon: figures.tick, color: 'success' }
    case 'in_progress':
      return { icon: figures.squareSmallFilled, color: 'claude' }
    case 'pending':
      return { icon: figures.squareSmall, color: undefined }
  }
}

function TaskItem({
  task,
  ownerColor,
  openBlockers,
  activity,
  ownerActive,
  columns,
}: TaskItemProps): React.ReactNode {
  const isCompleted = task.status === 'completed'
  const isInProgress = task.status === 'in_progress'
  const isBlocked = openBlockers.length > 0

  const { icon, color } = getTaskIcon(task.status)

  const showActivity = isInProgress && !isBlocked && activity

  // Responsive layout: hide owner on narrow screens (<60 cols)
  // Truncate subject based on available space
  const showOwner = columns >= 60 && task.owner && ownerActive
  const ownerWidth = showOwner ? stringWidth(` (@${task.owner})`) : 0
  // Account for: icon(2) + indentation(~8 when nested under spinner) + owner + safety
  // Use columns - 15 as a conservative estimate for nested layouts
  const maxSubjectWidth = Math.max(15, columns - 15 - ownerWidth)
  const displaySubject = truncateToWidth(task.subject, maxSubjectWidth)

  // Truncate activity for narrow screens
  const maxActivityWidth = Math.max(15, columns - 15)
  const displayActivity = activity
    ? truncateToWidth(activity, maxActivityWidth)
    : undefined

  return (
    <Box flexDirection="column">
      <Box>
        <Text color={color}>{icon} </Text>
        <Text
          bold={isInProgress}
          strikethrough={isCompleted}
          dimColor={isCompleted || isBlocked}
        >
          {displaySubject}
        </Text>
        {showOwner && (
          <Text dimColor>
            {' ('}
            {ownerColor ? (
              <ThemedText color={ownerColor}>@{task.owner}</ThemedText>
            ) : (
              `@${task.owner}`
            )}
            {')'}
          </Text>
        )}
        {isBlocked && (
          <Text dimColor>
            {' '}
            {figures.pointerSmall} blocked by{' '}
            {[...openBlockers]
              .sort((a, b) => parseInt(a, 10) - parseInt(b, 10))
              .map(id => `#${id}`)
              .join(', ')}
          </Text>
        )}
      </Box>
      {showActivity && displayActivity && (
        <Box>
          <Text dimColor>
            {'  '}
            {displayActivity}
            {figures.ellipsis}
          </Text>
        </Box>
      )}
    </Box>
  )
}
