/**
 * GET /api/print-agent/jobs/next?wait=25&max=50  (Bearer agent token)
 *
 * Long-poll: returns immediately when queued jobs (or commands) exist, otherwise re-reads the
 * SQLite spool every 200 ms for up to `wait` seconds. The spool file is shared by all PM2
 * workers, so a job inserted by another worker is picked up within ~200 ms.
 *
 * Jobs are claimed atomically (status → sent). Files ≤ 300 KB travel inline as base64
 * (one HTTP exchange per label); larger ones are fetched from jobs/:id/file.
 * If the agent disconnects while we hold claimed jobs, they go straight back to 'queued'.
 */
import { readFile } from 'node:fs/promises'
import { requireAgent } from '../../../utils/printAgentAuth'
import { claimJobs, claimCommands, retryJob, touchAgent, requeueSentJobsForAgent, listUnconfirmedSpooledJobs, type PrintJobRow } from '../../../utils/printSpoolDb'

const INLINE_LIMIT = 300 * 1024
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms))
const clamp = (v: number, min: number, max: number) => Math.max(min, Math.min(max, v))

export default defineEventHandler(async (event) => {
  const agent = requireAgent(event)
  const q = getQuery(event)
  const wait = clamp(Number(q.wait) || 0, 0, 25)
  const max = clamp(Number(q.max) || 50, 1, 100)

  touchAgent(agent.id)

  // `fresh=1` = the agent's first poll after a (re)start: whatever it had claimed before is gone
  // from its memory, so give those jobs back immediately instead of waiting for the 3-min stale
  // re-queue. Jobs that already reached lp are protected by the agent's ledger (idempotent ack).
  let reconcile: { id: number; cupsJobId: string | null; queue: string }[] | undefined
  if (String(q.fresh || '') === '1') {
    const n = requeueSentJobsForAgent(agent.id)
    if (n) console.log(`[print-agent] agent #${agent.id} "${agent.name}" restarted — re-queued ${n} claimed job(s)`)
    // jobs CUPS accepted whose completion we never heard about: the agent checks them against its CUPS history
    reconcile = listUnconfirmedSpooledJobs(agent.id).map(j => ({ id: j.id, cupsJobId: j.cups_job_id, queue: j.queue }))
    if (reconcile.length) console.log(`[print-agent] agent #${agent.id} gets ${reconcile.length} unconfirmed spooled job(s) to reconcile`)
  }

  let closed = false
  event.node.req.on('close', () => { closed = true })

  const deadline = Date.now() + wait * 1000
  let jobs: PrintJobRow[] = []
  let commands: any[] = []
  for (;;) {
    jobs = claimJobs(agent.id, max)
    commands = claimCommands(agent.id)
    if (jobs.length || commands.length || closed || Date.now() >= deadline) break
    await sleep(200)
  }

  if (closed) {
    // the agent is gone — hand the claimed jobs back immediately
    for (const j of jobs) retryJob(j.id)
    return { jobs: [], commands, reconcile }
  }

  const out: any[] = []
  for (const j of jobs) {
    let content: string | null = null
    if (j.file_size > 0 && j.file_size <= INLINE_LIMIT) {
      try { content = (await readFile(j.file_path)).toString('base64') } catch { content = null }
    }
    out.push({
      id: j.id,
      batchId: j.batch_id,
      queue: j.queue,
      options: j.options,
      fileName: j.file_name,
      fileSize: j.file_size,
      source: j.source,
      createdAt: j.created_at,
      attempt: j.attempts,
      content,
      fileUrl: content ? null : `/api/print-agent/jobs/${j.id}/file`
    })
  }
  if (out.length) console.log(`[print-agent] agent #${agent.id} "${agent.name}" picked up ${out.length} job(s)`)
  return { jobs: out, commands, reconcile, serverTime: new Date().toISOString() }
})
