// Tools for mis-formatted label PDFs (e.g. Amazon FBA carton label + DHL
// Retoure label): ONE DIN-A4 PORTRAIT page carrying TWO labels that lie
// SIDEWAYS on it (content rotated 90° counter-clockwise, one label in the top
// half, one in the bottom half). Printing such a page on the label printer
// squeezes both labels unreadably onto one label.
//
// splitRotateA4LabelPdf() cuts every A4-portrait page at the horizontal
// midline into two DIN-A5 pages (top half first) and bakes a 90° CLOCKWISE
// rotation into the content so both labels come out upright — safe for any
// renderer/printer (no reliance on the /Rotate flag). Non-A4-portrait pages
// are copied through unchanged.
//
// analyzeLabelPdf() detects such files: page size ≈ A4 portrait, plus a
// content-stream heuristic that looks for 90°-rotated placement matrices
// (`0 1 -1 0 … cm` style) — the signature of labels lying sideways. The
// heuristic is fail-soft: if the streams can't be decoded we still report the
// size-based candidate so the (manual) split button stays available.
//
// pdf-lib is imported dynamically so it stays out of the main bundle.

const A4_PT = { w: 595.28, h: 841.89 }
const SIZE_TOLERANCE_PT = 30

export const isA4PortraitPt = (w: number, h: number): boolean =>
  Math.abs(w - A4_PT.w) <= SIZE_TOLERANCE_PT && Math.abs(h - A4_PT.h) <= SIZE_TOLERANCE_PT

export interface LabelPdfAnalysis {
  pageCount: number
  a4PortraitPages: number
  sidewaysPages: number
  /** ≥1 A4-portrait page → the split & rotate action makes sense */
  splitCandidate: boolean
  /** ≥1 A4-portrait page whose content stream shows 90°-rotated placements */
  sidewaysDetected: boolean
}

// Decode a page's (possibly flate-compressed, possibly array-of-streams)
// content into latin1 text. Returns '' on any failure.
const pageContentText = (pdfLib: any, page: any): string => {
  try {
    const { PDFArray, PDFRawStream, decodePDFRawStream } = pdfLib
    const contents = page.node.Contents()
    if (!contents) return ''
    const streams: any[] = []
    if (contents instanceof PDFArray) {
      for (let i = 0; i < contents.size(); i++) {
        const s = page.node.context.lookup(contents.get(i))
        if (s instanceof PDFRawStream) streams.push(s)
      }
    } else if (contents instanceof PDFRawStream) {
      streams.push(contents)
    }
    let text = ''
    for (const s of streams) {
      const bytes = decodePDFRawStream(s).decode()
      let chunk = ''
      for (let i = 0; i < bytes.length; i++) chunk += String.fromCharCode(bytes[i])
      text += chunk + '\n'
    }
    return text
  } catch {
    return ''
  }
}

// True when the content stream contains at least one 90°-rotation placement
// matrix (a≈0, d≈0, b/c opposite-signed) — labels drawn sideways.
const hasSidewaysPlacement = (content: string): boolean => {
  if (!content) return false
  const reCm = /(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+cm\b/g
  let m: RegExpExecArray | null
  while ((m = reCm.exec(content)) !== null) {
    const a = Number(m[1]); const b = Number(m[2]); const c = Number(m[3]); const d = Number(m[4])
    if (Math.abs(a) < 0.01 && Math.abs(d) < 0.01 && Math.abs(b) > 0.5 && Math.abs(c) > 0.5 && b * c < 0) {
      return true
    }
  }
  return false
}

export const analyzeLabelPdf = async (input: ArrayBuffer | Uint8Array): Promise<LabelPdfAnalysis> => {
  const pdfLib: any = await import('pdf-lib')
  const doc = await pdfLib.PDFDocument.load(input, { ignoreEncryption: true })
  const result: LabelPdfAnalysis = {
    pageCount: doc.getPageCount(),
    a4PortraitPages: 0,
    sidewaysPages: 0,
    splitCandidate: false,
    sidewaysDetected: false,
  }
  for (let i = 0; i < result.pageCount; i++) {
    const page = doc.getPage(i)
    const { width, height } = page.getSize()
    if (page.getRotation().angle % 360 !== 0) continue
    if (!isA4PortraitPt(width, height)) continue
    result.a4PortraitPages++
    if (hasSidewaysPlacement(pageContentText(pdfLib, page))) result.sidewaysPages++
  }
  result.splitCandidate = result.a4PortraitPages > 0
  result.sidewaysDetected = result.sidewaysPages > 0
  return result
}

export const splitRotateA4LabelPdf = async (input: ArrayBuffer | Uint8Array): Promise<Uint8Array> => {
  const { PDFDocument, degrees } = await import('pdf-lib')
  const src = await PDFDocument.load(input, { ignoreEncryption: true })
  const out = await PDFDocument.create()
  const pageCount = src.getPageCount()
  for (let i = 0; i < pageCount; i++) {
    const page = src.getPage(i)
    const { width: w, height: h } = page.getSize()
    if (page.getRotation().angle % 360 !== 0 || !isA4PortraitPt(w, h)) {
      const [copied] = await out.copyPages(src, [i])
      out.addPage(copied)
      continue
    }
    const halfH = h / 2
    // Top half first, then bottom half. The transformationMatrix shifts the
    // half down to the origin — drawing with a negative x/y offset instead
    // renders blank in some viewers, so keep the normalization in the matrix.
    for (const bottom of [halfH, 0]) {
      const emb = await out.embedPage(
        page,
        { left: 0, bottom, right: w, top: bottom + halfH },
        [1, 0, 0, 1, 0, -bottom]
      )
      const a5 = out.addPage([halfH, w]) // DIN A5 portrait (420.9 × 595.3 pt)
      // rotate -90 at (0, w): (u,v) → (v, w-u) — bakes the clockwise turn
      a5.drawPage(emb, { x: 0, y: w, rotate: degrees(-90) })
    }
  }
  return await out.save()
}

// Uint8Array → base64 (chunked; label PDFs are small but stay stack-safe)
export const pdfBytesToBase64 = (bytes: Uint8Array): string => {
  let binary = ''
  const chunk = 0x8000
  for (let i = 0; i < bytes.length; i += chunk) {
    binary += String.fromCharCode.apply(null, bytes.subarray(i, i + chunk) as unknown as number[])
  }
  return btoa(binary)
}
