/**
 * Print-job feedback for printers that are bound to a LogShip Print Agent (tenant-LAN box).
 *
 * Those prints come back from /api/print/* as `printStatus: 'queued'` instead of 'printed';
 * the physical outcome arrives later from the agent (spooled → completed | failed). This plugin
 * turns that into toasts:
 *   - single job     → one toast on 'spooled' (success) or 'failed' (error)
 *   - batch (batchId)→ ONE progress toast per batch ("Etiketten 312 / 800 gesendet"), updated in
 *                      place, red when something failed — never 800 toasts
 *
 * Sources, both used: the in-app SSE bus (plugins/03.notifications-stream.client.ts re-dispatches
 * `type: 'print-job'` events as a window event — instant, but per PM2 worker) and a light poll of
 * /api/settings/print-agents/jobs/mine (cross-worker safety net: 2 s while own jobs are open,
 * 15 s idle). Nothing runs at all on an instance without agents (status endpoint says so).
 */

type JobEvt = { id: number; batchId?: string | null; status: string; queue?: string; error?: string | null; userId?: number | null; source?: string }

const OPEN = new Set(['queued', 'sent'])

export default defineNuxtPlugin((nuxtApp) => {
  if (!process.client) return
  const nuxtToast: any = useToast()

  const cookie = (name: string) => {
    const hit = document.cookie.split('; ').find(c => c.startsWith(name + '='))
    return hit ? decodeURIComponent(hit.slice(name.length + 1)) : ''
  }
  const isLoggedIn = () => !!cookie('logship_user_id')

  let enabled = false
  let cursor = ''
  let pollTimer: ReturnType<typeof setTimeout> | null = null
  let statusTimer: ReturnType<typeof setInterval> | null = null
  const known = new Map<number, string>()                 // jobId → last status seen
  const batches = new Map<string, { toastId: string; ids: Set<number>; spooled: number; failed: number; queue: string; closed: boolean }>()
  let anyOpen = false

  const addToast = (t: any) => {
    let id = ''
    nuxtApp.runWithContext(() => {
      const res = nuxtToast.add({ duration: t.duration ?? 5000, timeout: t.duration ?? 5000, color: 'primary', ...t } as any)
      id = res?.id || ''
    })
    return id
  }
  const updateToast = (id: string, t: any) => {
    nuxtApp.runWithContext(() => {
      try {
        if (typeof nuxtToast.update === 'function') nuxtToast.update(id, t)
        else { nuxtToast.remove?.(id); nuxtToast.add({ ...t, id } as any) }
      } catch { /* toast gone */ }
    })
  }

  const batchText = (b: { ids: Set<number>; spooled: number; failed: number; queue: string }) => {
    const total = b.ids.size
    const done = b.spooled + b.failed
    return `${done} / ${total} an ${b.queue || 'Drucker'} gesendet${b.failed ? ` · ${b.failed} fehlgeschlagen` : ''}`
  }

  const handle = (j: JobEvt) => {
    if (!j || !Number.isFinite(Number(j.id))) return
    const me = cookie('logship_user_id')
    if (j.userId && me && String(j.userId) !== me) return
    const id = Number(j.id)
    const prev = known.get(id)
    known.set(id, j.status)

    if (j.batchId) {
      let b = batches.get(j.batchId)
      if (!b) {
        b = { toastId: '', ids: new Set(), spooled: 0, failed: 0, queue: j.queue || '', closed: false }
        batches.set(j.batchId, b)
      }
      b.ids.add(id)
      const wasCounted = prev === 'spooled' || prev === 'completed' || prev === 'failed'
      if (!wasCounted && (j.status === 'spooled' || j.status === 'completed')) b.spooled++
      else if (!wasCounted && j.status === 'failed') b.failed++
      else if (prev === 'spooled' && j.status === 'failed') { b.spooled--; b.failed++ }
      const finished = b.spooled + b.failed >= b.ids.size
      const payload = {
        title: finished ? (b.failed ? 'Druckauftrag mit Fehlern' : 'Druckauftrag gesendet') : 'Druckauftrag läuft',
        description: batchText(b),
        icon: b.failed ? 'i-mdi-printer-alert' : 'i-mdi-printer',
        color: b.failed ? 'error' : (finished ? 'success' : 'primary'),
        duration: finished ? 8000 : 120000,
        timeout: finished ? 8000 : 120000
      }
      if (!b.toastId) b.toastId = addToast(payload)
      else updateToast(b.toastId, payload)
      if (finished) b.closed = true
      return
    }

    if (j.status === 'spooled' && prev !== 'spooled' && prev !== 'completed') {
      addToast({ title: 'An Drucker gesendet', description: j.queue || '', icon: 'i-mdi-printer-check', color: 'success', duration: 4000 })
    } else if (j.status === 'failed' && prev !== 'failed') {
      addToast({ title: 'Druck fehlgeschlagen', description: `${j.queue || ''}${j.error ? ' – ' + j.error : ''}`.trim(), icon: 'i-mdi-printer-alert', color: 'error', duration: 12000 })
    }
  }

  const poll = async () => {
    pollTimer = null
    if (!enabled || !isLoggedIn()) return
    try {
      const res: any = await $fetch('/api/settings/print-agents/jobs/mine', { params: cursor ? { since: cursor } : {} })
      const jobs: JobEvt[] = Array.isArray(res?.jobs) ? res.jobs : []
      // oldest first so batch counters build up in order
      jobs.sort((a: any, b: any) => Number(a.id) - Number(b.id))
      for (const j of jobs) handle(j)
      anyOpen = jobs.some(j => OPEN.has(j.status) || j.status === 'spooled')
      if (res?.serverTime) cursor = res.serverTime
    } catch { /* keep polling */ }
    pollTimer = setTimeout(poll, anyOpen ? 2000 : 15000)
  }
  const kick = () => { if (!pollTimer) pollTimer = setTimeout(poll, 300) }

  const checkEnabled = async () => {
    if (!isLoggedIn()) { enabled = false; return }
    try {
      const res: any = await $fetch('/api/settings/print-agents/status')
      const now = !!res?.enabled
      if (now && !enabled) { enabled = true; kick() }
      else if (!now) enabled = false
    } catch { /* leave as is */ }
  }

  // SSE relay from the notifications plugin (same PM2 worker → instant)
  window.addEventListener('logship:print-job', (e: any) => {
    const d = e?.detail || {}
    handle({ id: d.jobId, batchId: d.batchId, status: d.status, queue: d.queue, error: d.error, userId: d.userId, source: d.source })
    if (!enabled) { enabled = true; kick() }
    anyOpen = true
    if (pollTimer) { clearTimeout(pollTimer); pollTimer = null }
    kick()
  })

  useRouter().afterEach(() => { if (isLoggedIn() && !statusTimer) { checkEnabled(); statusTimer = setInterval(checkEnabled, 5 * 60_000) } })
  setTimeout(() => { if (isLoggedIn()) { checkEnabled(); if (!statusTimer) statusTimer = setInterval(checkEnabled, 5 * 60_000) } }, 2500)
})
