/**
 * The OCR seam. Everything above this interface works in terms of page text and
 * word boxes, so the concrete engine is swappable without touching the parsers,
 * the matcher, or the schema.
 *
 * The shipped implementation is self-hosted Tesseract (see tesseract.provider).
 * That choice is evidence-based rather than assumed: run against 46 pages of
 * real scanned CFE, CESPT and Telnor statements, it identified the provider on
 * 46/46 and extracted a usable account reference on 43/46, and on a later
 * corpus of 19 scanned municipal predial receipts it read the provider on
 * 19/19 and an identifier on 18/19 — well past the bar for a queue whose whole
 * point is that a human confirms every row. A
 * managed document-extraction API (Textract, Document Intelligence, Document
 * AI) fits behind this same interface if per-page accuracy ever proves
 * insufficient, with no schema change — but at 300+ pages/month/company it
 * would carry a real recurring cost for accuracy that is not currently the
 * bottleneck.
 */

/** One OCR'd word, with where it sits on the page. */
export interface OcrWord {
  text: string;
  /** Pixel box in the rendered page image. */
  left: number;
  top: number;
  width: number;
  height: number;
  /** Engine confidence for this word, 0..1. */
  confidence: number;
}

export interface OcrPage {
  /** Full page text, reading order, newline-separated. */
  text: string;
  /**
   * Word boxes. Needed because several of the real layouts are *tables* — the
   * CESPT "RECIBO" prints `No. DE CUENTA` as a column header with the value in
   * the row beneath it, which line-oriented text cannot associate. Parsers fall
   * back to geometry for exactly those fields.
   */
  words: OcrWord[];
  /** Mean word confidence across the page, 0..1. */
  confidence: number;
}

export interface OcrProvider {
  /** True when the engine is actually usable in this deployment. */
  available(): Promise<boolean>;
  /** Split a PDF into one rendered page image per page. */
  renderPages(pdf: Buffer): Promise<Buffer[]>;
  /** OCR a single rendered page image. */
  recognize(pageImage: Buffer): Promise<OcrPage>;
  /**
   * Read a PDF's own text layer, one entry per page, `null` where the page has
   * none worth using.
   *
   * Not every statement is a scan. The gas company e-mails born-digital CFDI
   * invoices whose text is already exact and already positioned — running those
   * through a rasteriser and a character recogniser can only lose information
   * (one sample turned `MEDIDOR: VM01014426` into `ar (LTR): 014420`) while
   * costing about a minute of CPU per page for the privilege. Where the layer
   * exists it is strictly better input for the same parsers, so it is tried
   * first and OCR remains the fallback for genuine scans.
   *
   * Positions are reported in the same pixel space `recognize` uses, so the
   * geometric helpers in the parsers work unchanged on either source.
   */
  textPages(pdf: Buffer): Promise<(OcrPage | null)[]>;
}

export const OCR_PROVIDER = Symbol("OCR_PROVIDER");
