import process from 'node:process'
import { Command } from 'commander'
import { logger } from '../../core/logger.ts'
import { DAEMON_BASE_URL } from '../../core/paths.ts'
import { formatCost, formatTable, formatTime, formatTokenBuckets, shortId } from '../tui/table.ts'

type TasksOptions = {
  agent?: string[]
  since?: string
  limit: string
  json: boolean
}

type ThreadListItem = {
  id: string
  agent: string
  title: string | null
  startedAt: number
  endedAt: number | null
  durMs: number | null
  runCount: number
  actionCount: number
  costUsd: number
  tokensIn: number | null
  tokensOut: number | null
  cacheReadTokens: number | null
  cacheWriteTokens: number | null
  outcomeValueUsd: number
  thomas: number
  errored?: boolean
}

export const tasksCommand = new Command('tasks')
  .description('List recent tasks (one per correlated conversation/goal).')
  .option('--agent <agents...>', 'Filter by agent.')
  .option('--since <when>', "'1h' | '24h' | '7d' | ISO date")
  .option('--limit <n>', 'Max rows to return.', '20')
  .option('--json', 'Output JSON for piping.', false)
  .action(async (options: TasksOptions) => {
    const params = new URLSearchParams()
    if (options.agent?.length) params.set('agent', options.agent.join(','))
    if (options.since) {
      try {
        params.set('since', String(parseSince(options.since)))
      } catch (err) {
        logger.print(`error: ${(err as Error).message}`)
        process.exitCode = 2
        return
      }
    }
    if (options.limit) params.set('limit', options.limit)

    const url = `${DAEMON_BASE_URL}/api/threads?${params.toString()}`
    let res: Response
    try {
      res = await fetch(url, { signal: AbortSignal.timeout(3000) })
    } catch {
      logger.print('Daemon not reachable. Start it with `openthomas wire` or `openthomas daemon`.')
      process.exitCode = 1
      return
    }
    if (!res.ok) {
      logger.print(`Daemon error: HTTP ${res.status}`)
      process.exitCode = 1
      return
    }

    const body = (await res.json()) as ThreadListItem[] | { rows: ThreadListItem[] }
    const tasks = Array.isArray(body) ? body : (body.rows ?? [])

    if (options.json) {
      process.stdout.write(`${JSON.stringify(tasks, null, 2)}\n`)
      return
    }

    if (tasks.length === 0) {
      logger.print('(no tasks yet — wire an agent and run it once)')
      return
    }

    const rows = tasks.map((t) => [
      shortId(t.id),
      formatTime(t.startedAt),
      t.agent,
      truncate(t.title ?? '', 40),
      String(t.runCount),
      String(t.actionCount),
      formatCost(t.costUsd),
      formatCost(t.outcomeValueUsd),
      formatThomas(t.thomas),
      formatTokenBuckets(t.tokensIn, t.tokensOut, t.cacheReadTokens, t.cacheWriteTokens),
    ])
    logger.print(
      formatTable(
        [
          'ID',
          'STARTED',
          'AGENT',
          'TITLE',
          'RUNS',
          'ACT',
          'COST',
          'VALUE',
          'T',
          'CACHED/FRESH/OUT',
        ],
        rows,
      ),
    )
  })

function truncate(s: string, max: number): string {
  if (s.length <= max) return s
  return `${s.slice(0, max - 1)}…`
}

function formatThomas(t: number): string {
  if (t === 0) return '0'
  if (t < 1) return t.toFixed(2)
  if (t < 100) return t.toFixed(1)
  return Math.round(t).toLocaleString()
}

function parseSince(s: string): number {
  const m = /^(\d+)([hd])$/.exec(s)
  if (m?.[1] && m[2]) {
    const n = Number.parseInt(m[1], 10)
    const ms = m[2] === 'h' ? n * 3_600_000 : n * 86_400_000
    return Date.now() - ms
  }
  const t = Date.parse(s)
  if (Number.isNaN(t))
    throw new Error(`invalid --since: ${s} (use '1h', '24h', '7d', or an ISO date)`)
  return t
}
