/**
 * Print servers (LogShip Print Agents) — shared state + actions for /settings/printers.
 *
 * One place talks to /api/settings/print-agents/*: the agents (boxes) with their inventory,
 * the per-printer bindings, open enrolment codes, commands (with result polling) and the job
 * list. The page, the server cards and the modals all consume this composable, so a change made
 * in a modal is visible everywhere after `load()`.
 */
export interface PrintAgent {
  id: number; name: string; orgId: number; online: boolean; lastSeen: string | null; createdAt: string
  version: string | null; hostname: string | null; platform: string | null
  printers: { queue: string; state?: string; accepting?: boolean; deviceUri?: string; makeModel?: string; media?: { w: number; h: number } | null; jobs?: number }[]
  queueDepth: Record<string, number>; health: any; stats: Record<string, number>
  supportUntil: string | null; allowCupsRelay: boolean; allowSupport: boolean
}
export interface PrintBinding { custPrinterId: number; agentId: number; cupsName: string; orgId: number; updatedAt: string }
export interface PrintJob {
  id: number; batchId: string | null; agentId: number; queue: string; options: string; fileName: string; fileSize: number
  source: string; status: string; cupsJobId: string | null; attempts: number; lpOutput: string | null; error: string | null
  createdByUser: number | null; createdAt: string; sentAt: string | null; spooledAt: string | null; completedAt: string | null; updatedAt: string
}

const hdr = () => ({ headers: useRequestHeaders(['cookie']) })

export const usePrintAgents = () => {
  const data = useState<{ agents: PrintAgent[]; bindings: PrintBinding[]; codes: any[]; enabled: boolean }>('printAgents', () => ({ agents: [], bindings: [], codes: [], enabled: false }))
  const loading = useState('printAgentsLoading', () => false)
  const error = useState('printAgentsError', () => '')
  const loadedOnce = useState('printAgentsLoaded', () => false)

  const jobs = useState<PrintJob[]>('printAgentJobs', () => [])
  const jobsLoading = useState('printAgentJobsLoading', () => false)

  const { data: orgLookup } = useLookup('/api/models/ad_org')
  const orgs = computed(() => ((orgLookup.value as any)?.records || []).map((o: any) => ({ id: Number(o.id), name: o.Name || o.name || String(o.id) })).sort((a: any, b: any) => a.id - b.id))
  const orgName = (id: any) => Number(id) === 0 ? '*' : (orgs.value.find((o: any) => o.id === Number(id))?.name || `Org ${id}`)

  const load = async () => {
    loading.value = true
    error.value = ''
    try {
      data.value = await $fetch('/api/settings/print-agents', hdr()) as any
      loadedOnce.value = true
    } catch (e: any) {
      error.value = e?.data?.statusMessage || e?.message || 'Print servers could not be loaded.'
    }
    loading.value = false
  }

  const agents = computed(() => data.value?.agents || [])
  const bindings = computed(() => data.value?.bindings || [])
  const codes = computed(() => data.value?.codes || [])
  const agentById = (id: any) => agents.value.find(a => a.id === Number(id)) || null
  const bindingForPrinter = (printerId: any) => bindings.value.find(b => Number(b.custPrinterId) === Number(printerId)) || null
  const agentForPrinter = (printerId: any) => { const b = bindingForPrinter(printerId); return b ? agentById(b.agentId) : null }
  const bindingsOf = (agentId: number) => bindings.value.filter(b => b.agentId === agentId)
  const queueBinding = (agentId: number, queue: string) => bindings.value.find(b => b.agentId === agentId && b.cupsName === queue) || null

  // ── commands (delivered with the agent's next poll, usually < 1 s) ───────────
  const waitCommand = async (agentId: number, commandId: number, timeoutMs = 60000) => {
    const t0 = Date.now()
    while (Date.now() - t0 < timeoutMs) {
      await new Promise(r => setTimeout(r, 1000))
      const res: any = await $fetch(`/api/settings/print-agents/${agentId}/commands`, hdr())
      const c = (res?.commands || []).find((x: any) => x.id === commandId)
      if (c && (c.status === 'done' || c.status === 'failed')) return c
    }
    return { status: 'failed', result: 'Timeout – the device did not answer (offline?).' }
  }
  const sendCommand = async (agentId: number, command: string, payload: any = null) => {
    const res: any = await $fetch(`/api/settings/print-agents/${agentId}/command`, { method: 'POST', ...hdr(), body: { command, payload } })
    return waitCommand(agentId, res.commandId)
  }
  const listCommands = async (agentId: number) => ((await $fetch(`/api/settings/print-agents/${agentId}/commands`, hdr()) as any)?.commands || [])

  // ── agent administration ─────────────────────────────────────────────────────
  const createCode = async (name: string, orgId: number | string) =>
    $fetch('/api/settings/print-agents/enroll-code', { method: 'POST', ...hdr(), body: { name, orgId } }) as Promise<any>
  const deleteCode = async (code: string) => { await $fetch(`/api/settings/print-agents/enroll-code/${encodeURIComponent(code)}`, { method: 'DELETE', ...hdr() }); await load() }
  const patchAgent = async (agentId: number, body: any) => { const r = await $fetch(`/api/settings/print-agents/${agentId}`, { method: 'PUT', ...hdr(), body }); await load(); return r }
  const revoke = async (agentId: number) => { await $fetch(`/api/settings/print-agents/${agentId}`, { method: 'DELETE', ...hdr() }); await load() }
  const setBinding = async (printerId: number, agentId: number | null, cupsName: string, orgId: number) => {
    const r = await $fetch(`/api/settings/print-agents/bindings/${printerId}`, { method: 'PUT', ...hdr(), body: { agentId, cupsName, orgId } })
    await load()
    return r
  }

  // ── jobs ─────────────────────────────────────────────────────────────────────
  const loadJobs = async (filter: { status?: string; agentId?: number | string; limit?: number } = {}) => {
    jobsLoading.value = true
    try {
      const res: any = await $fetch('/api/settings/print-agents/jobs', { ...hdr(), params: { status: filter.status || undefined, agentId: filter.agentId || undefined, limit: filter.limit || 150 } })
      jobs.value = res?.jobs || []
    } catch { /* keep the old list */ }
    jobsLoading.value = false
  }
  const jobAction = async (jobId: number, action: 'retry' | 'cancel') => { await $fetch(`/api/settings/print-agents/jobs/${jobId}/${action}`, { method: 'POST', ...hdr() }) }

  // ── formatting helpers shared by the panels ──────────────────────────────────
  const fmtTime = (s: any) => {
    if (!s) return '–'
    const d = new Date(String(s).replace(' ', 'T') + (String(s).endsWith('Z') ? '' : 'Z'))
    return isNaN(d.getTime()) ? String(s) : d.toLocaleString('de-DE', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' })
  }
  const ago = (s: any, de = true) => {
    if (!s) return de ? 'nie' : 'never'
    const ms = Date.now() - new Date(String(s).replace(' ', 'T') + 'Z').getTime()
    if (ms < 90_000) return de ? 'gerade eben' : 'just now'
    if (ms < 3_600_000) return (de ? 'vor ' : '') + Math.round(ms / 60_000) + (de ? ' min' : ' min ago')
    if (ms < 86_400_000) return (de ? 'vor ' : '') + Math.round(ms / 3_600_000) + (de ? ' h' : ' h ago')
    return (de ? 'vor ' : '') + Math.round(ms / 86_400_000) + (de ? ' d' : ' d ago')
  }
  const healthParts = (h: any, de = true) => {
    if (!h) return [] as string[]
    const p: string[] = []
    if (h.diskFreeMb != null) p.push(`${Math.round(h.diskFreeMb / 1024)} GB ${de ? 'frei' : 'free'}`)
    if (h.smartWear != null) p.push(`SSD ${h.smartWear} %`)
    if (h.load != null) p.push(`Load ${h.load}`)
    if (h.temp != null) p.push(`${h.temp} °C`)
    if (h.uptimeHours != null) p.push(`${Math.round(h.uptimeHours)} h`)
    return p
  }

  return {
    data, loading, error, loadedOnce, load,
    agents, bindings, codes, orgs, orgName,
    agentById, bindingForPrinter, agentForPrinter, bindingsOf, queueBinding,
    sendCommand, waitCommand, listCommands,
    createCode, deleteCode, patchAgent, revoke, setBinding,
    jobs, jobsLoading, loadJobs, jobAction,
    fmtTime, ago, healthParts
  }
}
