// PDF page-size detection + paper classification for the CUPS print routes.
//
// Why: the Jasper "Shipment A5" packing slip (420 × 595 pt) used to be treated as a
// shipping label by the smart print routes (anything narrower than 500 pt) and was
// sent to the Zebra label printer with fit-to-page. An A5 page is paper, not a label:
// it belongs on the A4/paper queue with an explicit `-o media=A5`, so CUPS tells the
// printer to pull A5 from the tray instead of scaling onto the queue's default size.
//
// Used by: print/attachment-smart, print/order-attachments, print/picklist, print/index.

import { promisify } from 'node:util'
import child_process from 'node:child_process'

const exec = promisify(child_process.exec)

export type PdfPageKind = 'label' | 'a5' | 'a4' | 'other'

export interface PdfPageInfo {
  width: number
  height: number
  /** false when neither pdfinfo nor ghostscript could read the page box (A4 assumed) */
  detected: boolean
  kind: PdfPageKind
  /** `-o media=…` for lp, or '' when the queue default should be used */
  mediaOption: string
}

const near = (value: number, target: number, tolerance = 8) => Math.abs(value - target) <= tolerance

/** Classifies a page box (points) — orientation-agnostic. */
export const classifyPdfPage = (width: number, height: number): PdfPageKind => {
  if (!width || !height) return 'other'
  const short = Math.min(width, height)
  const long = Math.max(width, height)
  if (near(short, 420, 8) && near(long, 595, 8)) return 'a5'
  if (near(short, 595, 8) && near(long, 842, 8)) return 'a4'
  // Letter / Legal and other paper sizes: printed as-is on the paper queue.
  if (short >= 500) return 'other'
  return 'label'
}

/** Explicit CUPS media only for the sizes the paper queues know by name. */
export const mediaOptionForKind = (kind: PdfPageKind): string => {
  if (kind === 'a5') return '-o media=A5'
  if (kind === 'a4') return '-o media=A4'
  return ''
}

/**
 * Reads the first page's size with `pdfinfo`, falling back to ghostscript.
 * Never throws — an unreadable file is reported as A4 (`detected: false`).
 */
export const detectPdfPage = async (filePath: string): Promise<PdfPageInfo> => {
  let width = 0
  let height = 0
  let detected = false

  try {
    const { stdout } = await exec(`pdfinfo "${filePath}" 2>/dev/null | grep -i "Page size"`)
    const match = stdout.match(/Page size:\s+([\d.]+)\s*x\s*([\d.]+)/)
    if (match) {
      width = parseFloat(match[1])
      height = parseFloat(match[2])
      detected = true
    }
  } catch {
    // pdfinfo missing or file unreadable — try ghostscript below
  }

  if (!detected) {
    try {
      const { stdout } = await exec(`gs -q -dNODISPLAY -dBATCH -dNOPAUSE -c "(${filePath}) (r) file runpdfbegin 1 pdfgetpage /MediaBox get == quit" 2>/dev/null`)
      const match = stdout.match(/\[\s*[\d.]+\s+[\d.]+\s+([\d.]+)\s+([\d.]+)\s*\]/)
      if (match) {
        width = parseFloat(match[1])
        height = parseFloat(match[2])
        detected = true
      }
    } catch {
      // fall through to the A4 default
    }
  }

  if (!detected) {
    width = 595
    height = 842
  }

  const kind = classifyPdfPage(width, height)
  return { width, height, detected, kind, mediaOption: mediaOptionForKind(kind) }
}

/** Appends the media option unless the caller already set one. */
export const withMediaOption = (options: string, page: PdfPageInfo): string => {
  const base = (options || '').trim()
  if (!page.mediaOption || /media=/.test(base)) return base
  return `${base} ${page.mediaOption}`.trim()
}
