/**
 * Tier 1 — text-layer extraction for digital (non-scanned) PDFs.
 *
 * Uses pdfjs-dist (legacy build runs in Node) to pull text items WITH their
 * positions. Coordinates are kept (PDF points, bottom-left origin, + page size)
 * because the per-vendor learning layer remembers where each field sits.
 *
 * Fail-soft: returns { hasText:false } on any error so the pipeline rasterizes
 * and OCRs instead (Tier 2).
 */

export interface TextItem {
  text: string
  x: number
  y: number
  w: number
  h: number
  /** 1-based page + page-normalized (0..1, top-left origin) box for UI overlays. */
  page?: number
  nx?: number
  ny?: number
  nw?: number
  nh?: number
}

export interface TextPage {
  pageNum: number
  width: number
  height: number
  items: TextItem[]
}

export interface TextLayerResult {
  hasText: boolean
  text: string
  pages: TextPage[]
}

const EMPTY: TextLayerResult = { hasText: false, text: '', pages: [] }

export const extractTextLayer = async (pdfBuffer: Buffer): Promise<TextLayerResult> => {
  let pdfjs: any
  try {
    // Legacy build is the Node-compatible entry point.
    pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs')
  } catch (e: any) {
    // If this fires in prod, pdfjs-dist isn't resolvable from .output → every PDF
    // (even digital ones) falls through to OCR. Promote it to a direct dependency.
    console.warn('[Inbox] Tier 1 disabled — pdfjs-dist failed to load:', e?.message || e)
    return EMPTY
  }

  try {
    const data = new Uint8Array(pdfBuffer)
    const loadingTask = pdfjs.getDocument({
      data,
      isEvalSupported: false,
      useSystemFonts: true,
      disableFontFace: true,
      verbosity: 0
    })
    const pdf = await loadingTask.promise

    const pages: TextPage[] = []
    const textChunks: string[] = []

    for (let p = 1; p <= pdf.numPages; p++) {
      const page = await pdf.getPage(p)
      const viewport = page.getViewport({ scale: 1 })
      const vw = viewport.width || 1
      const vh = viewport.height || 1
      const content = await page.getTextContent()
      const items: TextItem[] = []
      const lineParts: string[] = []
      for (const it of content.items as any[]) {
        const str = it.str ?? ''
        if (!str) continue
        const tr = it.transform || [1, 0, 0, 1, 0, 0]
        const w = it.width || 0
        const h = it.height || Math.hypot(tr[1], tr[3])
        const x = tr[4]
        const y = tr[5] // PDF baseline, bottom-left origin
        items.push({
          text: str, x, y, w, h,
          // page-normalized, top-left origin (for UI overlay positioning)
          page: p,
          nx: Math.max(0, Math.min(1, x / vw)),
          ny: Math.max(0, Math.min(1, (vh - y - h) / vh)),
          nw: Math.max(0, Math.min(1, w / vw)),
          nh: Math.max(0, Math.min(1, h / vh))
        })
        lineParts.push(str)
        if (it.hasEOL) lineParts.push('\n')
      }
      pages.push({ pageNum: p, width: viewport.width, height: viewport.height, items })
      textChunks.push(lineParts.join(' ').replace(/[ \t]*\n[ \t]*/g, '\n'))
    }

    try { await pdf.cleanup() } catch {}
    const text = textChunks.join('\n\n').trim()
    // "Has text" only if there's a meaningful amount — a couple stray glyphs on a
    // scan shouldn't pre-empt OCR.
    const hasText = text.replace(/\s/g, '').length >= 30
    return { hasText, text, pages }
  } catch (e: any) {
    console.warn('[Inbox] Tier 1 text extraction failed (falling back to OCR):', e?.message || e)
    return EMPTY
  }
}

export default extractTextLayer
