/**
 * Task Queue Manager — lifecycle management for background tasks.
 */

import { readFile, writeFile, mkdir } from 'fs/promises'
import { randomUUID } from 'crypto'
import { join } from 'path'
import { getCwd } from '../../utils/cwd.js'
import { logForDebugging } from '../../utils/debug.js'
import {
  type QueuedTask,
  type QueueState,
  createFreshQueueState,
} from './types.js'

const QUEUE_FILE = '.sandcode/task-queue.json'

function getQueuePath(): string {
  return join(getCwd(), QUEUE_FILE)
}

/**
 * Load the task queue from disk.
 */
export async function loadQueue(): Promise<QueueState> {
  try {
    const content = await readFile(getQueuePath(), 'utf-8')
    const parsed = JSON.parse(content) as QueueState
    if (parsed.version === 1) return parsed
  } catch {
    // File doesn't exist
  }
  return createFreshQueueState()
}

/**
 * Save the task queue to disk.
 */
export async function saveQueue(state: QueueState): Promise<void> {
  await mkdir(join(getCwd(), '.sandcode'), { recursive: true })
  await writeFile(getQueuePath(), JSON.stringify(state, null, 2), 'utf-8')
}

/**
 * Add a new task to the queue.
 */
export async function enqueueTask(
  prompt: string,
  tokenBudget?: number,
): Promise<QueuedTask> {
  const state = await loadQueue()

  const task: QueuedTask = {
    id: randomUUID().slice(0, 8),
    prompt,
    status: 'pending',
    queuedAt: Date.now(),
    tokenBudget: tokenBudget ?? state.defaultTokenBudget,
    tokensUsed: 0,
  }

  state.tasks.push(task)
  await saveQueue(state)
  logForDebugging(`Task queued: ${task.id} — "${prompt.slice(0, 60)}"`)
  return task
}

/**
 * Update a task's status and data.
 */
export async function updateTask(
  id: string,
  updates: Partial<QueuedTask>,
): Promise<QueuedTask | null> {
  const state = await loadQueue()
  const task = state.tasks.find((t) => t.id === id)
  if (!task) return null

  Object.assign(task, updates)
  await saveQueue(state)
  return task
}

/**
 * Cancel a queued or running task.
 */
export async function cancelTask(id: string): Promise<boolean> {
  const state = await loadQueue()
  const task = state.tasks.find((t) => t.id === id)
  if (!task || task.status === 'completed' || task.status === 'cancelled') {
    return false
  }

  task.status = 'cancelled'
  task.completedAt = Date.now()
  await saveQueue(state)
  logForDebugging(`Task cancelled: ${id}`)
  return true
}

/**
 * Get the next pending task to execute.
 */
export async function getNextPendingTask(): Promise<QueuedTask | null> {
  const state = await loadQueue()
  const runningCount = state.tasks.filter((t) => t.status === 'running').length

  if (runningCount >= state.maxConcurrent) return null

  return state.tasks.find((t) => t.status === 'pending') ?? null
}

/**
 * Get all tasks in the queue.
 */
export async function listTasks(): Promise<QueuedTask[]> {
  const state = await loadQueue()
  return state.tasks
}

/**
 * Remove completed/cancelled tasks older than 24 hours.
 */
export async function pruneOldTasks(): Promise<number> {
  const state = await loadQueue()
  const cutoff = Date.now() - 24 * 60 * 60 * 1000
  const before = state.tasks.length

  state.tasks = state.tasks.filter(
    (t) =>
      t.status === 'pending' ||
      t.status === 'running' ||
      (t.completedAt && t.completedAt > cutoff),
  )

  const pruned = before - state.tasks.length
  if (pruned > 0) {
    await saveQueue(state)
    logForDebugging(`Pruned ${pruned} old tasks`)
  }

  return pruned
}

/**
 * Format the queue status for display.
 */
export async function formatQueueStatus(): Promise<string> {
  const tasks = await listTasks()

  if (tasks.length === 0) {
    return 'Task queue is empty. Use /queue "task description" to add tasks.'
  }

  const lines = ['Task Queue', '']

  const statusIcons: Record<string, string> = {
    pending: '⏳',
    running: '🔄',
    completed: '✅',
    failed: '❌',
    cancelled: '⛔',
  }

  for (const task of tasks) {
    const icon = statusIcons[task.status] ?? '?'
    const elapsed = task.startedAt
      ? `${Math.round((Date.now() - task.startedAt) / 1000)}s`
      : ''
    const tokens = task.tokensUsed > 0
      ? ` (${Math.round(task.tokensUsed / 1000)}k tokens)`
      : ''

    lines.push(
      `  ${icon} [${task.id}] ${task.prompt.slice(0, 50)}${task.prompt.length > 50 ? '...' : ''} ${elapsed}${tokens}`,
    )

    if (task.summary?.text) {
      lines.push(`    → ${task.summary.text.slice(0, 80)}`)
    }
    if (task.error) {
      lines.push(`    ✗ ${task.error.slice(0, 80)}`)
    }
  }

  return lines.join('\n')
}
