"""Ground-truth table regions for the QUE-245 spike.

A table's region is its FULL anatomy: title/caption + units line + every
column-header row (including multi-level/spanning headers) + the row-label
(stub) column + all body/subtotal/total rows + any footnotes attached to
the table. Free-standing prose, page headers/footers (timestamp, URL, page
number) are NOT part of any table.

Ground truth is expressed per page as a list of vertical bands in PDF
points (top-left origin). The box for a band is computed as the tight union
of every text-layer word whose vertical center falls inside the band — so
the left/right edges are the true label and number extents, not guesses.
This keeps the answer key reproducible and tied to the actual document,
while the human judgement (where each table starts/ends, how many) lives in
the band list. Bands were read off the text-layer dumps in inspect output.
"""

from __future__ import annotations

from pathlib import Path
from typing import Dict, List, Tuple

import fitz

# (y_top, y_bottom) bands in PDF points, per 1-indexed page. Empty list = a
# page with no tables (negative control).
BHE_991_BANDS: Dict[int, List[Tuple[float, float]]] = {
    1: [(422.0, 508.0), (533.0, 648.0)],  # Summary GAAP; Summary Non-GAAP (+footnote)
    2: [(76.0, 200.0), (211.0, 333.0)],  # Industry Sector Update; Cash Conversion Cycle
    3: [],  # all prose
    4: [(76.0, 419.0)],  # Statements of Income (double spanning header)
    5: [(76.0, 494.0)],  # Balance Sheets
    6: [(76.0, 483.0)],  # Cash Flows (internal blank-line gaps, one table)
    7: [(76.0, 708.0)],  # GAAP->Non-GAAP Reconciliation (+ two footnotes)
    8: [],  # blank
}

# Page furniture excluded from any table's word-union (running header/footer).
FURNITURE_Y_TOP = 30.0
FURNITURE_Y_BOTTOM = 760.0


def box_from_band(
    page: fitz.Page, y_top: float, y_bottom: float
) -> Tuple[float, float, float, float]:
    """Tight normalized (0..1, top-left) word-union box within a y-band."""
    w, h = page.rect.width, page.rect.height
    sel = [
        word
        for word in page.get_text("words")
        if y_top <= (word[1] + word[3]) / 2.0 <= y_bottom
        and FURNITURE_Y_TOP <= (word[1] + word[3]) / 2.0 <= FURNITURE_Y_BOTTOM
        and word[4].strip()
    ]
    if not sel:
        return (0.0, 0.0, 0.0, 0.0)
    x0 = min(s[0] for s in sel)
    y0 = min(s[1] for s in sel)
    x1 = max(s[2] for s in sel)
    y1 = max(s[3] for s in sel)
    return (x0 / w, y0 / h, x1 / w, y1 / h)


def ground_truth(pdf: Path, bands: Dict[int, List[Tuple[float, float]]]):
    """Yield (page, [normalized boxes]) for every page in the band spec."""
    doc = fitz.open(str(pdf))
    out: Dict[int, List[Tuple[float, float, float, float]]] = {}
    for pno, blist in bands.items():
        page = doc[pno - 1]
        out[pno] = [box_from_band(page, a, b) for (a, b) in blist]
    return out


if __name__ == "__main__":
    gt = ground_truth(Path("documents/BHE_991.pdf"), BHE_991_BANDS)
    for pno in sorted(gt):
        print(f"page {pno}: {len(gt[pno])} tables")
        for b in gt[pno]:
            print(f"   ({b[0]:.3f},{b[1]:.3f},{b[2]:.3f},{b[3]:.3f})")
