"""Regression harness for drop_masthead_regions.

drop_masthead_regions runs inside main() BEFORE political_result.json is saved, so
the saved fixtures never capture its effect. This harness replays it directly over
every cached page's regions.json, mirroring the real call
(drop_masthead_regions(regions, paper, page_num, page_img_path)).

The cached fixtures carry no meta.json, so the paper is recovered from the page-1
image height (verified against the printed masthead nameplate):
    7053/7055 -> sakshi   6833/6549 -> andhra_jyothi   6422 -> namaste_telangana

Two views:
  • SHIPPED (gated): what the function actually drops in production. Expect the
    Namaste Telangana page-1 masthead to be dropped (incl. a doc_title nameplate,
    the mis-group seed) and NOTHING dropped on inner pages or other papers.
  • UNGATED PROBE: the same geometric rule with the paper/page gate removed, run on
    every page-1. Confirms the gate is the only thing keeping the rule off other
    papers AND that the rule would never strip a genuine page-1 headline.
"""
import glob
import json
import sys
from collections import Counter
from pathlib import Path

from PIL import Image

import smart_extractor
from smart_extractor import drop_masthead_regions

OUT = Path("output")


def paper_of(run_dir):
    png = run_dir / "pages" / "page_001.png"
    if png.exists():
        try:
            h = Image.open(png).height
            if 7040 <= h <= 7060:
                return "sakshi"
            if h in (6833, 6549):
                return "andhra_jyothi"
            if h == 6422:
                return "namaste_telangana"
        except Exception:
            pass
    return "unknown"


def page_num_of(reg_path):
    # page_001.regions.json -> 1
    stem = reg_path.name.split(".")[0]      # page_001
    return int(stem.split("_")[1])


def types_of(regions, ids):
    m = {r["id"]: r.get("type") for r in regions}
    return Counter(m.get(i) for i in ids)


def run():
    dirs = sorted([d for d in OUT.iterdir() if d.is_dir()])
    shipped = []     # rows: (paper, page, run, dropped_ids, dropped_types)
    probe = []       # ungated, page-1 only
    fired = Counter()        # paper -> #page-1 runs where shipped drop fired
    p1_total = Counter()     # paper -> #page-1 runs
    false_pos = []           # shipped drops on non-NT or page>1 (should be empty)

    for d in dirs:
        pages_dir = d / "pages"
        if not pages_dir.exists():
            continue
        paper = paper_of(d)
        for reg_path in sorted(pages_dir.glob("page_*.regions.json")):
            page_num = page_num_of(reg_path)
            png = pages_dir / reg_path.name.replace(".regions.json", ".png")
            regions = json.loads(reg_path.read_text())["regions"]

            # ---- SHIPPED (gated) ----
            kept = drop_masthead_regions(list(regions), paper, page_num, str(png))
            kept_ids = {r["id"] for r in kept}
            dropped = sorted(r["id"] for r in regions if r["id"] not in kept_ids)
            if dropped:
                shipped.append((paper, page_num, d.name, dropped, types_of(regions, dropped)))
                if not (paper == "namaste_telangana" and page_num == 1):
                    false_pos.append((paper, page_num, d.name, dropped))

            if page_num == 1:
                p1_total[paper] += 1
                if paper == "namaste_telangana" and dropped:
                    fired[paper] += 1

                # ---- UNGATED PROBE (force the rule to run) ----
                probe_kept = drop_masthead_regions(list(regions), "namaste_telangana", 1, str(png))
                probe_kept_ids = {r["id"] for r in probe_kept}
                pdropped = sorted(r["id"] for r in regions if r["id"] not in probe_kept_ids)
                if pdropped:
                    probe.append((paper, d.name, pdropped, types_of(regions, pdropped)))

    return shipped, probe, fired, p1_total, false_pos


def report(shipped, probe, fired, p1_total, false_pos):
    print("\n=== SHIPPED (gated) — Namaste Telangana page-1 masthead drop ===")
    nt = [s for s in shipped if s[0] == "namaste_telangana" and s[1] == 1]
    print(f"NT page-1 runs: {p1_total['namaste_telangana']}  |  masthead fired in: {fired['namaste_telangana']}")
    seedless = []
    for paper, page, run, dropped, types in sorted(nt, key=lambda x: x[2]):
        has_title = types.get("doc_title", 0) > 0
        flag = "" if has_title else "   ⚠️ no doc_title dropped (seed not removed?)"
        if not has_title:
            seedless.append(run)
        print(f"  {run}: dropped {dropped}  types={dict(types)}{flag}")

    misses = p1_total['namaste_telangana'] - fired['namaste_telangana']
    print(f"\n  NT page-1 misses (nothing dropped): {misses}")
    print(f"  NT page-1 drops WITHOUT a doc_title nameplate seed: {len(seedless)} {seedless}")

    print("\n=== FALSE POSITIVES (shipped drops on non-NT, or page>1) ===")
    if false_pos:
        for paper, page, run, dropped in false_pos:
            print(f"  ⚠️ {paper} p{page} {run}: dropped {dropped}")
    else:
        print("  none — gate held: 0 drops on inner pages and on Sakshi/Andhra Jyothi.")

    print("\n=== UNGATED PROBE — would the bare geometric rule strip a real headline? ===")
    by_paper = Counter(p[0] for p in probe)
    print(f"page-1 runs where the ungated rule drops something, by paper: {dict(by_paper)}")
    for paper in ("sakshi", "andhra_jyothi"):
        hits = [p for p in probe if p[0] == paper]
        print(f"\n  --- {paper}: {len(hits)} page-1 run(s) would drop something ungated ---")
        for _, run, dropped, types in hits[:12]:
            print(f"    {run}: {dropped}  types={dict(types)}")
        if not hits:
            print("    none — rule is inert on this paper's page-1 even ungated (safe).")


if __name__ == "__main__":
    report(*run())
