/**
 * Shared logic for the FBA-relabel print surfaces on the sales-order edit page
 * (label print modal + DIN-A4 report modal).
 *
 * Rows come from the order's lines: every active product line with its
 * printLabelCode (the FNSKU / alternative barcode Amazon requires), SKU, name
 * and qty. Barcodes are generated CLIENT-side as PNG data URLs (bwip-js,
 * CODE128) and embedded by the server / pdfmake — the server never generates
 * barcodes (same split as the receipt label printing).
 *
 * SEQUENCE: when the order already has a shipment, the rows follow the
 * SHIPMENT positions (m_inoutline.Line) — that is the order in which the goods
 * are physically handled while relabeling. The FNSKU only lives on the ORDER
 * line, so each shipment line is matched back to its order line to pick it up.
 * Without a shipment (or if the lookup fails) the order-line sequence stands.
 */

import { loadPdfMake } from '~/utils/loadPdfMake'

export interface RelabelRow {
  line: string | number
  sku: string
  upc: string
  product: string
  code: string
  qty: number
  orderLineId?: string | number
  productId?: string | number
}

export const useRelabelDocs = () => {
  /**
   * Order-line ids in shipment sequence: every shipment of the order (oldest
   * first), each one's lines by m_inoutline.Line. Returns [] when the order has
   * no shipment yet.
   */
  const fetchShipmentSequence = async (orderId: any) => {
    const res: any = await $fetch(`/api/orders/${orderId}/shipments`, {
      headers: useRequestHeaders(['cookie'])
    })
    const shipments = (res?.records || [])
      // Only outgoing customer shipments — a customer RETURN (C+) booked against
      // the same order must not reorder the labels.
      .filter((s: any) => {
        const type = String(s?.MovementType?.id ?? s?.MovementType ?? '')
        const status = String(s?.DocStatus?.id ?? s?.DocStatus ?? '')
        return (!type || type.startsWith('C-')) && !['VO', 'RE'].includes(status)
      })
      // The endpoint sorts newest-first; relabel in the order they were shipped.
      .reverse()

    const sequence: any[] = []
    for (const shipment of shipments) {
      const lineRes: any = await $fetch(`/api/inouts/${shipment.id}/inout-line`, {
        headers: useRequestHeaders(['cookie'])
      })
      const lines = [...(lineRes?.records || [])]
        .sort((a: any, b: any) => Number(a?.Line || 0) - Number(b?.Line || 0))
      for (const line of lines) {
        sequence.push({
          orderLineId: line?.C_OrderLine_ID?.id ?? null,
          productId: line?.M_Product_ID?.id ?? null
        })
      }
    }
    return sequence
  }

  const fetchRelabelRows = async (orderId: any): Promise<RelabelRow[]> => {
    const res: any = await $fetch(`/api/orders/${orderId}/order-line`, {
      headers: useRequestHeaders(['cookie'])
    })
    const orderRows: RelabelRow[] = (res?.records || [])
      .filter((item: any) => item?.IsActive !== false && item?.M_Product_ID?.id && Number(item?.QtyEntered || 0) > 0)
      .map((item: any) => ({
        line: item?.Line || '',
        sku: item?.M_Product_ID?.SKU || item?.M_Product_ID?.Value || '',
        // iDempiere stores UPC and EAN in one field (UPC, labeled "UPC/EAN"); UPC2 = second barcode
        upc: item?.M_Product_ID?.UPC || item?.M_Product_ID?.UPC2 || '',
        product: item?.M_Product_ID?.Name || item?.M_Product_ID?.identifier || '',
        // The FNSKU entered during relabel capture — the whole point of the join below.
        code: String(item?.printLabelCode ?? item?.PrintLabelCode ?? '').trim(),
        qty: Number(item?.QtyEntered || 0),
        orderLineId: item?.id ?? '',
        productId: item?.M_Product_ID?.id ?? ''
      }))
      .sort((a: any, b: any) => Number(a.line) - Number(b.line))

    // Fail-soft: no shipment, or the lookup broke → keep the order-line sequence.
    let sequence: any[] = []
    try {
      sequence = await fetchShipmentSequence(orderId)
    } catch (e) {
      console.error('Shipment sequence lookup failed, falling back to order lines:', e)
    }
    if (!sequence.length) return orderRows

    const byOrderLine = new Map<string, RelabelRow>(orderRows.map(r => [String(r.orderLineId), r] as [string, RelabelRow]))
    const used = new Set<string>()
    const ordered: RelabelRow[] = []

    for (const entry of sequence) {
      // A shipment line carries the order-line FK; fall back to the product when
      // it doesn't (manually added shipment line).
      let row = entry.orderLineId ? byOrderLine.get(String(entry.orderLineId)) : undefined
      if (!row && entry.productId) {
        row = orderRows.find(r => !used.has(String(r.orderLineId)) && String(r.productId) === String(entry.productId))
      }
      // Skipped here: one order line split over several shipment lines (locators)
      // keeps ONE label row at its first appearance, and a shipment line with no
      // order line behind it has no FNSKU, so it can't be labeled anyway.
      if (!row || used.has(String(row.orderLineId))) continue
      used.add(String(row.orderLineId))
      ordered.push(row)
    }

    // Positions no shipment covers are still printable — append them in their
    // order-line sequence rather than dropping them.
    for (const row of orderRows) {
      if (!used.has(String(row.orderLineId))) ordered.push(row)
    }
    return ordered
  }

  const generateBarcodeDataUrl = async (code: string): Promise<string> => {
    // bwip-js browser build at high module scale — the same rasteriser the
    // Label Designer uses (proven readable on the thermal label printers).
    // The previous jsbarcode default rendered ~2px bars that had to be
    // UPSCALED onto the label → blurry, unreadable bars on small labels
    // (e.g. labelprinter-2). A high-res PNG lets the printer DOWNSAMPLE,
    // which keeps the bar edges crisp. No internal padding — the label
    // layout itself insets the barcode ≥3 mm on both sides (quiet zone).
    const mod: any = await import('bwip-js/browser')
    const bwip = (mod && typeof mod.toCanvas === 'function') ? mod : (mod?.default ?? mod)
    const canvas = document.createElement('canvas')
    bwip.toCanvas(canvas, {
      bcid: 'code128',
      text: String(code),
      scale: 6,
      height: 12,
      includetext: false
    })
    return canvas.toDataURL('image/png')
  }

  /**
   * DIN-A4 report: which label (barcode + code) goes onto which article (SKU).
   * Rows must already carry `barcode` (PNG data URL from generateBarcodeDataUrl).
   */
  // Article names are capped at ~5 wrapped lines in the Artikel column
  // (~14 chars/line at 9pt in the current column width); longer names end
  // with "…" so a single position can't grow arbitrarily tall.
  const truncateProductName = (name: any, maxChars = 70) => {
    const s = String(name || '')
    if (s.length <= maxChars) return s
    return s.slice(0, maxChars - 1).trimEnd() + '…'
  }

  const buildRelabelReportDoc = async (rows: Array<RelabelRow & { barcode: string }>, documentNo: string) => {
    const pdfMake = await loadPdfMake()

    const tableBody: any[] = [[
      { text: 'Pos.', style: 'th' },
      { text: 'SKU', style: 'th' },
      { text: 'EAN/UPC', style: 'th' },
      { text: 'Artikel', style: 'th' },
      { text: 'Etiketten-Code (FBA)', style: 'th' },
      { text: 'Menge', style: 'th', alignment: 'right' },
      // Empty column for the operator to WRITE DOWN which parcel (Paket) the
      // position was finally packed into — intentionally blank on print.
      { text: 'Paket-Nr.', style: 'th', alignment: 'center' }
    ]]
    for (const r of rows) {
      tableBody.push([
        { text: String(r.line), margin: [0, 8, 0, 0] },
        { text: r.sku, bold: true, margin: [0, 8, 0, 0] },
        { text: r.upc || '-', margin: [0, 8, 0, 0] },
        { text: truncateProductName(r.product), margin: [0, 8, 0, 0] },
        {
          stack: [
            { image: r.barcode, width: 150, height: 34 },
            { text: r.code, bold: true, fontSize: 9, margin: [0, 2, 0, 0] }
          ],
          margin: [0, 4, 0, 4]
        },
        { text: String(r.qty), alignment: 'right', bold: true, margin: [0, 8, 0, 0] },
        { text: '' }
      ])
    }
    const totalQty = rows.reduce((acc, r) => acc + Number(r.qty || 0), 0)
    tableBody.push([
      { text: '', colSpan: 4 }, {}, {}, {},
      { text: 'Etiketten gesamt', bold: true, alignment: 'right', margin: [0, 4, 0, 0] },
      { text: String(totalQty), bold: true, alignment: 'right', margin: [0, 4, 0, 0] },
      { text: '' }
    ])

    const docDefinition: any = {
      pageSize: 'A4',
      pageMargins: [40, 50, 40, 50],
      // Page numbers (with the order no.) so multi-page printouts can be related
      // to each other when the sheets get separated.
      footer: (currentPage: number, pageCount: number) => ({
        columns: [
          { text: `FBA-Relabel – Auftrag ${documentNo || ''}`, fontSize: 8, color: '#777777' },
          { text: `Seite ${currentPage} von ${pageCount}`, fontSize: 8, color: '#777777', alignment: 'right' }
        ],
        margin: [40, 15, 40, 0]
      }),
      content: [
        { text: `FBA-Relabel – Auftrag ${documentNo || ''}`, fontSize: 15, bold: true },
        { text: 'Übersicht: welches Etikett auf welchen Artikel (SKU) geklebt wird. Paket-Nr.: hier eintragen, in welches Paket die Position gepackt wurde.', fontSize: 9, color: '#555555', margin: [0, 3, 0, 4] },
        // Amazon FBA labeling rules (official "How to label products" guide)
        { text: 'Wichtig (Amazon-Vorgaben): Original-Barcode (EAN/UPC) vollständig überkleben. Label nicht auf Kanten, Ecken oder Rundungen kleben — ca. 6 mm Abstand zum Verpackungsrand einhalten.', fontSize: 9, bold: true, color: '#b45309', margin: [0, 0, 0, 12] },
        {
          table: {
            headerRows: 1,
            // dontBreakRows: a position (SKU/EAN/name/barcode) must never be
            // split across a page break — the whole row moves to the next page.
            dontBreakRows: true,
            widths: [24, 70, 68, '*', 152, 30, 44],
            body: tableBody
          },
          layout: {
            hLineColor: () => '#cccccc',
            vLineColor: () => '#cccccc',
            hLineWidth: () => 0.5,
            vLineWidth: () => 0.5
          }
        },
        { text: `Erstellt: ${new Date().toLocaleString('de-DE', { timeZone: 'Europe/Berlin' })}`, fontSize: 8, color: '#777777', margin: [0, 10, 0, 0] }
      ],
      styles: {
        th: { bold: true, fontSize: 9, fillColor: '#f0f0f0', margin: [0, 2, 0, 2] }
      },
      defaultStyle: { fontSize: 9 }
    }

    return pdfMake.createPdf(docDefinition)
  }

  return { fetchRelabelRows, generateBarcodeDataUrl, buildRelabelReportDoc }
}
