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,
  formatDuration,
  formatTable,
  formatTime,
  formatTokenBuckets,
  shortId,
} from '../tui/table.ts'

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

type RunListItem = {
  id: string
  threadId: string
  agent: string
  status: string
  startedAt: number
  endedAt: number | null
  durMs: number | null
  costUsd: number
  tokensIn: number | null
  tokensOut: number | null
  cacheReadTokens: number | null
  cacheWriteTokens: number | null
  actionCount: number
}

export const listCommand = new Command('list')
  .description('List recent runs.')
  .option('--agent <agents...>', 'Filter by agent.')
  .option('--since <when>', "'1h' | '24h' | '7d' | ISO date")
  .option('--status <statuses...>', "'running' | 'done' | 'error'")
  .option('--limit <n>', 'Max rows to return.', '20')
  .option('--json', 'Output JSON for piping.', false)
  .action(async (options: ListOptions) => {
    const params = new URLSearchParams()
    if (options.agent?.length) params.set('agent', options.agent.join(','))
    if (options.status?.length) params.set('status', options.status.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/runs?${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 runs = (await res.json()) as RunListItem[]

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

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

    const rows = runs.map((r) => [
      shortId(r.id),
      formatTime(r.startedAt),
      r.agent,
      r.status,
      formatDuration(r.durMs),
      String(r.actionCount),
      formatCost(r.costUsd),
      formatTokenBuckets(
        r.tokensIn,
        r.tokensOut,
        r.cacheReadTokens,
        r.cacheWriteTokens,
      ),
    ])
    logger.print(
      formatTable(
        ['ID', 'STARTED', 'AGENT', 'STATUS', 'DUR', 'ACT', 'COST', 'CACHED/FRESH/OUT'],
        rows,
      ),
    )
  })

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
}
