import { Box, Text } from '@hermes/ink'
import { memo, type ReactNode, useEffect, useState } from 'react'
import spinners, { type BrailleSpinnerName } from 'unicode-animations'

import { FACES, VERBS } from '../constants.js'
import {
  formatToolCall,
  parseToolTrailResultLine,
  pick,
  THINKING_COT_MAX,
  thinkingPreview,
  toolTrailLabel
} from '../lib/text.js'
import type { Theme } from '../theme.js'
import type { ActiveTool, ActivityItem, ThinkingMode } from '../types.js'

const THINK: BrailleSpinnerName[] = ['helix', 'breathe', 'orbit', 'dna', 'waverows', 'snake', 'pulse']
const TOOL: BrailleSpinnerName[] = ['cascade', 'scan', 'diagswipe', 'fillsweep', 'rain', 'columns', 'sparkle']

const fmtElapsed = (ms: number) => {
  const sec = Math.max(0, ms) / 1000

  return sec < 10 ? `${sec.toFixed(1)}s` : `${Math.round(sec)}s`
}

// ── Spinner ──────────────────────────────────────────────────────────

export function Spinner({ color, variant = 'think' }: { color: string; variant?: 'think' | 'tool' }) {
  const [spin] = useState(() => {
    const raw = spinners[pick(variant === 'tool' ? TOOL : THINK)]

    return { ...raw, frames: raw.frames.map(f => [...f][0] ?? '⠀') }
  })

  const [frame, setFrame] = useState(0)

  useEffect(() => {
    const id = setInterval(() => setFrame(f => (f + 1) % spin.frames.length), spin.interval)

    return () => clearInterval(id)
  }, [spin])

  return <Text color={color}>{spin.frames[frame]}</Text>
}

// ── Detail row ───────────────────────────────────────────────────────

type DetailRow = { color: string; content: ReactNode; dimColor?: boolean; key: string }

function Detail({ color, content, dimColor }: DetailRow) {
  return (
    <Text color={color} dimColor={dimColor}>
      <Text dimColor>└ </Text>
      {content}
    </Text>
  )
}

// ── Streaming cursor ─────────────────────────────────────────────────

function StreamCursor({ active = false, color, dimColor }: { active?: boolean; color: string; dimColor?: boolean }) {
  const [on, setOn] = useState(true)

  useEffect(() => {
    const id = setInterval(() => setOn(v => !v), 420)

    return () => clearInterval(id)
  }, [])

  return <Text color={color} dimColor={dimColor}>{active && on ? '▍' : ' '}</Text>
}

// ── Thinking (pre-tool fallback) ─────────────────────────────────────

export const Thinking = memo(function Thinking({
  mode = 'truncated',
  reasoning,
  streaming = false,
  t
}: {
  mode?: ThinkingMode
  reasoning: string
  streaming?: boolean
  t: Theme
}) {
  const [tick, setTick] = useState(0)

  useEffect(() => {
    const id = setInterval(() => setTick(v => v + 1), 1100)

    return () => clearInterval(id)
  }, [])

  const preview = thinkingPreview(reasoning, mode, THINKING_COT_MAX)

  return (
    <Box flexDirection="column">
      <Text color={t.color.dim}>
        <Spinner color={t.color.dim} /> {FACES[tick % FACES.length] ?? '(•_•)'}{' '}
        {VERBS[tick % VERBS.length] ?? 'thinking'}…
      </Text>

      {preview ? (
        <Text color={t.color.dim} dimColor {...(mode !== 'full' ? { wrap: 'truncate-end' as const } : {})}>
          <Text dimColor>└ </Text>
          {preview}
          <StreamCursor active={streaming} color={t.color.dim} dimColor />
        </Text>
      ) : streaming ? (
        <Text color={t.color.dim} dimColor>
          <Text dimColor>└ </Text>
          <StreamCursor active={streaming} color={t.color.dim} dimColor />
        </Text>
      ) : null}
    </Box>
  )
})

// ── ToolTrail (canonical progress block) ─────────────────────────────

type Group = { color: string; content: ReactNode; details: DetailRow[]; key: string }

export const ToolTrail = memo(function ToolTrail({
  busy = false,
  thinkingMode = 'truncated',
  reasoning = '',
  reasoningStreaming = false,
  t,
  tools = [],
  trail = [],
  activity = []
}: {
  busy?: boolean
  thinkingMode?: ThinkingMode
  reasoning?: string
  reasoningStreaming?: boolean
  t: Theme
  tools?: ActiveTool[]
  trail?: string[]
  activity?: ActivityItem[]
}) {
  const [now, setNow] = useState(() => Date.now())

  useEffect(() => {
    if (!tools.length) {
      return
    }

    const id = setInterval(() => setNow(Date.now()), 200)

    return () => clearInterval(id)
  }, [tools.length])

  const reasoningTail = thinkingPreview(reasoning, thinkingMode, THINKING_COT_MAX)

  if (!busy && !trail.length && !tools.length && !activity.length && !reasoningTail) {
    return null
  }

  const groups: Group[] = []
  const meta: DetailRow[] = []

  const detail = (row: DetailRow) => {
    const g = groups.at(-1)
    g ? g.details.push(row) : meta.push(row)
  }

  // ── trail → groups + details ────────────────────────────────────

  for (const [i, line] of trail.entries()) {
    const parsed = parseToolTrailResultLine(line)

    if (parsed) {
      groups.push({
        color: parsed.mark === '✗' ? t.color.error : t.color.cornsilk,
        content: parsed.detail ? parsed.call : `${parsed.call} ${parsed.mark}`,
        details: [],
        key: `tr-${i}`
      })

      if (parsed.detail) {
        detail({
          color: parsed.mark === '✗' ? t.color.error : t.color.dim,
          content: parsed.detail,
          dimColor: parsed.mark !== '✗',
          key: `tr-${i}-d`
        })
      }

      continue
    }

    if (line.startsWith('drafting ')) {
      groups.push({
        color: t.color.cornsilk,
        content: toolTrailLabel(line.slice(9).replace(/…$/, '').trim()),
        details: [{ color: t.color.dim, content: 'drafting...', dimColor: true, key: `tr-${i}-d` }],
        key: `tr-${i}`
      })

      continue
    }

    if (line === 'analyzing tool output…') {
      detail({
        color: t.color.dim,
        content: groups.length ? (
          <>
            <Spinner color={t.color.amber} variant="think" /> {line}
          </>
        ) : (
          line
        ),
        dimColor: true,
        key: `tr-${i}`
      })

      continue
    }

    meta.push({ color: t.color.dim, content: line, dimColor: true, key: `tr-${i}` })
  }

  // ── live tools → groups ─────────────────────────────────────────

  for (const tool of tools) {
    groups.push({
      color: t.color.cornsilk,
      content: (
        <>
          <Spinner color={t.color.amber} variant="tool" /> {formatToolCall(tool.name, tool.context || '')}
          {tool.startedAt ? ` (${fmtElapsed(now - tool.startedAt)})` : ''}
        </>
      ),
      details: [],
      key: tool.id
    })
  }

  if (reasoningTail && groups.length) {
    detail({
      color: t.color.dim,
      content: (
        <>
          {reasoningTail}
          <StreamCursor active={reasoningStreaming} color={t.color.dim} dimColor />
        </>
      ),
      dimColor: true,
      key: 'cot'
    })
  } else if (reasoningStreaming && groups.length && thinkingMode === 'collapsed') {
    detail({
      color: t.color.dim,
      content: <StreamCursor active={reasoningStreaming} color={t.color.dim} dimColor />,
      dimColor: true,
      key: 'cot'
    })
  }

  // ── activity → meta ─────────────────────────────────────────────

  for (const item of activity.slice(-4)) {
    const glyph = item.tone === 'error' ? '✗' : item.tone === 'warn' ? '!' : '·'
    const color = item.tone === 'error' ? t.color.error : item.tone === 'warn' ? t.color.warn : t.color.dim

    meta.push({ color, content: `${glyph} ${item.text}`, dimColor: item.tone === 'info', key: `a-${item.id}` })
  }

  // ── render ──────────────────────────────────────────────────────

  return (
    <Box flexDirection="column">
      {busy && !groups.length && <Thinking mode={thinkingMode} reasoning={reasoning} streaming={reasoningStreaming} t={t} />}
      {!busy && !groups.length && reasoningTail && (
        <Detail color={t.color.dim} content={reasoningTail} dimColor key="cot" />
      )}

      {groups.map(g => (
        <Box flexDirection="column" key={g.key}>
          <Text color={g.color}>
            <Text color={t.color.amber}>● </Text>
            {g.content}
          </Text>

          {g.details.map(d => (
            <Detail {...d} key={d.key} />
          ))}
        </Box>
      ))}

      {meta.map((row, i) => (
        <Text color={row.color} dimColor={row.dimColor} key={row.key}>
          <Text dimColor>{i === meta.length - 1 ? '└ ' : '├ '}</Text>
          {row.content}
        </Text>
      ))}
    </Box>
  )
})
