// Attachment actions for R_Request tickets — ZIP download, server-side
// printing (label printer / DIN A4), the 2× DIN A5 split-&-rotate print for
// misformatted FBA label PDFs, the per-file "Erledigt" flag and delete.
//
// Extracted verbatim from components/requests/RequestForm.vue so the SAME
// behaviour drives both ticket surfaces: the edit form (RequestForm — chat
// bubbles + Anhänge card) and the my-tickets split view (MyTicketDetail).
// The card markup lives in components/requests/RequestAttachmentsCard.vue.
//
// `attachment` objects are the ones RequestForm builds from the Strapi files:
// { fileId, processed, name, ext, mime, size, width, height, url }.
import { date } from 'alga-js'
import { toast } from 'bulma-toast'
import { analyzeLabelPdf, splitRotateA4LabelPdf, pdfBytesToBase64 } from '~/utils/labelPdfTools'

export const isImageExt = (ext: string) => ['.jpg', '.jpeg', '.png', '.webp', '.gif', '.bmp', '.svg'].includes((ext || '').toLowerCase())
export const isPdfExt = (ext: string) => (ext || '').toLowerCase() === '.pdf'
export const isVideoExt = (ext: string) => ['.mp4', '.m4v', '.avi', '.mpeg', '.webm', '.mov'].includes((ext || '').toLowerCase())
export const isAudioExt = (ext: string) => ['.mp3', '.wav', '.ogg', '.m4a', '.webm'].includes((ext || '').toLowerCase())

export const formatFileSize = (bytes: number) => {
  if (!bytes) return ''
  const kb = bytes / 1024
  if (kb < 1024) return kb.toFixed(1) + ' KB'
  return (kb / 1024).toFixed(2) + ' MB'
}

// Label-printer picker for attachment printing — same source as the
// commissioning page: CUST_CommissionTable rows (Settings → Commission Tables)
// via the cached lookup, falling back to the built-in Tisch 1/2/3 list when the
// table isn't seeded. Always starts on the default label printer
// ('labelprinter-1'); other printers are a per-session choice and deliberately
// NOT persisted (and NOT shared with the commissioning page's remembered Tisch).
// Held in useState so the Anhänge card's picker and the chat-bubble print
// buttons (two composable instances on the edit page) always agree.
const DEFAULT_LABEL_PRINTER = 'labelprinter-1'
const DEFAULT_PRINTER_OPTIONS = [
  { value: 'labelprinter-1', label: 'Tisch 1' },
  { value: 'labelprinter-4', label: 'Tisch 2' },
  { value: 'labelprinter-5', label: 'Tisch 3' }
]

export interface RequestAttachmentActionsOptions {
  requestId: () => string | number | null | undefined
  documentNo?: () => string | null | undefined
  strapiAttachmentDocId: () => string
  attachments: () => any[]
  isStaffRole: () => boolean
  /** Called after a file was deleted server-side so the owner can drop it from its own list. */
  onDeleted?: (attachment: any) => void
}

export const useRequestAttachmentActions = (opts: RequestAttachmentActionsOptions) => {
  // Not awaited on purpose: keeps the calling component's setup synchronous.
  const { data: commissionTablesData } = useLookup('/api/commission/tables')
  const printerOptions = computed(() => {
    const rows = (commissionTablesData.value as any)?.records || []
    return rows.length
      ? rows.map((t: any) => ({ value: t.shippingPrinter, label: t.name || ('Tisch ' + (t.number ?? '')) }))
      : DEFAULT_PRINTER_OPTIONS
  })
  const labelPrinter = useState<string>('requestAttachmentLabelPrinter', () => DEFAULT_LABEL_PRINTER)
  // If the default printer isn't among the configured tables (e.g. renamed in
  // Settings), snap to the first available option so a valid printer is selected.
  watch(printerOptions, (options) => {
    if (options.length && !options.some((o: any) => o.value === labelPrinter.value)) {
      labelPrinter.value = options[0].value
    }
  }, { immediate: true })
  const printerLabelFor = (value: string) => printerOptions.value.find((o: any) => o.value === value)?.label || value

  const requestTag = () => `REQUEST-${opts.requestId() || ''}`

  // --- Bulk ZIP download -----------------------------------------------------
  const downloadingAll = ref(false)
  const downloadAllAttachments = async () => {
    const docId = opts.strapiAttachmentDocId()
    if (!docId || downloadingAll.value) return
    downloadingAll.value = true
    try {
      const zipName = `Ticket-${opts.documentNo?.() || opts.requestId() || 'request'}-Anhaenge.zip`.replace(/[^\w.\-]+/g, '_')
      const blob: any = await $fetch(`/api/attachments/R_Request/${docId}/download-all`, {
        responseType: 'blob',
        query: { name: zipName },
        headers: useRequestHeaders(['cookie'])
      })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = zipName
      document.body.appendChild(a)
      a.click()
      a.remove()
      setTimeout(() => URL.revokeObjectURL(url), 10000)
    } catch (err) {
      console.error('Download all attachments failed:', err)
      toast({ message: 'Anhänge konnten nicht heruntergeladen werden', type: 'is-danger', duration: 2500 })
    } finally {
      downloadingAll.value = false
    }
  }

  // --- Print a single attachment server-side (staff only) --------------------
  // Same flow as AttachmentModal: fetch the file, base64 it, POST to
  // /api/print/attachment-labels (label printer) or /api/print/ (DIN-A4 default printer).
  const printingAtt = ref<Record<string, boolean>>({})
  const printAttachment = async (attachment: any, type: 'label' | 'normal') => {
    const key = attachment.url + '|' + type
    if (printingAtt.value[key]) return
    printingAtt.value[key] = true
    try {
      const response = await fetch(attachment.url)
      if (!response.ok) throw new Error('Attachment download failed')
      const blob = await response.blob()
      const fileContent = await new Promise<string>((resolve, reject) => {
        const reader = new FileReader()
        reader.onloadend = () => resolve(String(reader.result).split(',')[1])
        reader.onerror = () => reject(reader.error)
        reader.readAsDataURL(blob)
      })
      // fileName lands unquoted in an `lp` shell command server-side — keep it shell-safe
      const safeName = String(attachment.name || 'attachment').replace(/[^\w.\-]+/g, '_')
      const res: any = await $fetch(type === 'label' ? '/api/print/attachment-labels' : '/api/print/', {
        method: 'POST',
        body: {
          pathName: `attachments/${new Date().getFullYear()}`,
          fileName: `${date.now().replace(/\-|\:/g, '_').replace(' ', '-')}-${requestTag()}-${safeName}`,
          fileContent,
          ...(type === 'label' ? { shippingPrinter: labelPrinter.value } : {})
        }
      })
      if (res?.stdout) {
        toast({
          message: type === 'label' ? `Anhang wird auf dem Labeldrucker (${printerLabelFor(labelPrinter.value)}) gedruckt` : 'Anhang wird auf dem DIN-A4-Drucker gedruckt',
          type: 'is-success',
          duration: 2000
        })
      } else {
        toast({ message: 'Drucken fehlgeschlagen' + (res?.stderr ? ': ' + res.stderr : ''), type: 'is-danger', duration: 2500 })
      }
    } catch (err) {
      console.error('Print attachment failed:', err)
      toast({ message: 'Drucken fehlgeschlagen', type: 'is-danger', duration: 2500 })
    } finally {
      printingAtt.value[key] = false
    }
  }

  // --- Misformatted FBA label PDFs ------------------------------------------
  // ONE A4-portrait page carrying TWO labels lying sideways (90° CCW) on it.
  // detectSplitCandidates() inspects every PDF attachment (page size +
  // rotated-content heuristic, see utils/labelPdfTools); printAttachmentSplit()
  // cuts such a file into 2× DIN A5, rotates each half 90° right and sends the
  // result to the label printer. The normal Label / DIN-A4 buttons stay untouched.
  const attPdfInfo = ref<Record<string, { checked: boolean; splitCandidate: boolean; sideways: boolean }>>({})
  const splitInfoFor = (a: any) => attPdfInfo.value[a?.url] || ({} as any)

  const detectSplitCandidates = async () => {
    if (!opts.isStaffRole()) return
    const pdfs = opts.attachments()
      .filter(a => a && isPdfExt(a.ext) && !(a.url in attPdfInfo.value) && (!a.size || a.size < 20 * 1024 * 1024))
    for (const a of pdfs) {
      attPdfInfo.value[a.url] = { checked: false, splitCandidate: false, sideways: false }
      try {
        const resp = await fetch(a.url)
        if (!resp.ok) continue
        const info = await analyzeLabelPdf(await resp.arrayBuffer())
        attPdfInfo.value[a.url] = { checked: true, splitCandidate: info.splitCandidate, sideways: info.sidewaysDetected }
      } catch (e) {
        attPdfInfo.value[a.url] = { checked: true, splitCandidate: false, sideways: false }
      }
    }
  }

  const printAttachmentSplit = async (attachment: any) => {
    const key = attachment.url + '|split'
    if (printingAtt.value[key]) return
    printingAtt.value[key] = true
    try {
      const response = await fetch(attachment.url)
      if (!response.ok) throw new Error('Attachment download failed')
      const splitBytes = await splitRotateA4LabelPdf(await response.arrayBuffer())
      const fileContent = pdfBytesToBase64(splitBytes)
      // fileName lands unquoted in an `lp` shell command server-side — keep it shell-safe
      const safeName = String(attachment.name || 'attachment').replace(/\.pdf$/i, '').replace(/[^\w.\-]+/g, '_')
      const res: any = await $fetch('/api/print/attachment-labels', {
        method: 'POST',
        body: {
          pathName: `attachments/${new Date().getFullYear()}`,
          fileName: `${date.now().replace(/\-|\:/g, '_').replace(' ', '-')}-${requestTag()}-${safeName}-SPLIT-A5.pdf`,
          fileContent,
          shippingPrinter: labelPrinter.value
        }
      })
      if (res?.stdout) {
        toast({ message: `Anhang wird geteilt (2× DIN A5, 90° gedreht) auf dem Labeldrucker (${printerLabelFor(labelPrinter.value)}) gedruckt`, type: 'is-success', duration: 2000 })
      } else {
        toast({ message: 'Drucken fehlgeschlagen' + (res?.stderr ? ': ' + res.stderr : ''), type: 'is-danger', duration: 2500 })
      }
    } catch (err) {
      console.error('Split print attachment failed:', err)
      toast({ message: 'Drucken fehlgeschlagen', type: 'is-danger', duration: 2500 })
    } finally {
      printingAtt.value[key] = false
    }
  }

  // --- Per-file "Erledigt" flag ---------------------------------------------
  // Persisted in the Strapi ad-attachment record's Processed_Files json column
  // (array of file ids); greys out the row in the Anhänge card. Optimistic
  // toggle with revert on failure.
  const togglingAtt = ref<Record<string, boolean>>({})
  const toggleAttachmentProcessed = async (attachment: any) => {
    const docId = opts.strapiAttachmentDocId()
    if (!attachment.fileId || !docId || togglingAtt.value[attachment.fileId]) return
    togglingAtt.value[attachment.fileId] = true
    const newVal = !attachment.processed
    attachment.processed = newVal
    try {
      const res: any = await $fetch(`/api/attachments/R_Request/${docId}/processed`, {
        method: 'PUT',
        headers: useRequestHeaders(['cookie']),
        body: { fileId: attachment.fileId, processed: newVal }
      })
      if (Number(res?.status) !== 200) throw new Error(res?.message || 'processed toggle failed')
    } catch (err) {
      console.error('Toggle attachment processed failed:', err)
      attachment.processed = !newVal
      toast({ message: 'Status konnte nicht gespeichert werden', type: 'is-danger', duration: 2500 })
    } finally {
      togglingAtt.value[attachment.fileId] = false
    }
  }

  // --- Delete a single attachment file (Strapi upload file) after confirmation
  const deletingAtt = ref<Record<string, boolean>>({})
  const deleteAttachment = async (attachment: any) => {
    if (!attachment.fileId || deletingAtt.value[attachment.fileId]) return
    if (!window.confirm(`Anhang "${attachment.name}" wirklich löschen?`)) return
    deletingAtt.value[attachment.fileId] = true
    try {
      const res: any = await $fetch(`/api/attachments/R_Request/${attachment.fileId}`, {
        method: 'DELETE',
        headers: useRequestHeaders(['cookie'])
      })
      // the route returns {} on success and { status, message } on failure
      if (res?.status && Number(res.status) >= 400) throw new Error(res?.message || 'delete failed')
      opts.onDeleted?.(attachment)
      toast({ message: 'Anhang gelöscht', type: 'is-success', duration: 2000 })
    } catch (err) {
      console.error('Delete attachment failed:', err)
      toast({ message: 'Anhang konnte nicht gelöscht werden', type: 'is-danger', duration: 2500 })
    } finally {
      deletingAtt.value[attachment.fileId] = false
    }
  }

  return {
    printerOptions, labelPrinter, printerLabelFor,
    downloadingAll, downloadAllAttachments,
    printingAtt, printAttachment, printAttachmentSplit,
    attPdfInfo, splitInfoFor, detectSplitCandidates,
    togglingAtt, toggleAttachmentProcessed,
    deletingAtt, deleteAttachment
  }
}
