#!/usr/bin/env python3
"""Build both workbooks against every run on disk, and fail loudly on any that break.

Why this exists
---------------
`write_vr` is one long function with branches that only run on particular
shapes of input. The pan-feed sheet has an empty-state branch that had never
executed, because every file tested until then contained pan feeds -- and it
referenced a format key, `f["sub"]`, that does not exist. The first EPG without
a pan feed hit it and the workbook would not build.

A formatting error does not lose a run: `run_pipeline` falls back to a plain
export. It loses the *presentable* artefact, silently, which is worse -- the
estimate looks finished and the download is wrong.

So: build everything, against every run already on disk, and say what broke.

    python3 worker/check_reports.py           # every run with a detailed workbook
    python3 worker/check_reports.py <RUN> …   # named runs

It also checks that every format key the report modules ask for is one
vr_style actually defines, which is the specific failure above and cannot be
caught by building a single file.
"""
from __future__ import annotations

import io
import json
import re
import sys
import traceback
from pathlib import Path

BASE = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(BASE))
_vendor = BASE / "vendor"
if _vendor.is_dir():
    sys.path.insert(0, str(_vendor))

import pandas as pd  # noqa: E402
import xlsxwriter  # noqa: E402

from engine.excel_report import write_report  # noqa: E402
from engine.reference import ReferenceData  # noqa: E402
from engine.vr_report import write_vr  # noqa: E402
from engine.vr_style import build as build_formats  # noqa: E402


def check_format_keys() -> list[str]:
    """Every f["..."] the report modules reference must exist in vr_style.

    Static, so it catches a key inside a branch no test input reaches.
    """
    from engine.excel_report import _formats as summary_formats

    # The two books have SEPARATE format tables -- vr_style.build for the VR
    # book, excel_report._formats for the summary one. Checking every key
    # against one of them reports two dozen false failures.
    missing = []
    for mod, builder in (("vr_report.py", build_formats),
                         ("excel_report.py", summary_formats)):
        have = set(builder(xlsxwriter.Workbook(io.BytesIO())))
        src = (BASE / "engine" / mod).read_text()
        used = set(re.findall(r'f\["([a-z_0-9]+)"\]', src))
        # numkey() composes names at runtime ("n" + "_b"); those bases are
        # exercised by building a real workbook below rather than statically.
        missing += [f"{mod}:{k}" for k in sorted(used - have) if k]
    return missing


def main(argv: list[str]) -> int:
    bad = check_format_keys()
    if bad:
        print(f"[FAIL] format keys used but not defined in vr_style: {bad}")
    else:
        print("[ok]   every format key referenced is defined")

    cfg = json.loads((BASE / "worker" / "config.json").read_text())
    exports = Path(cfg["exports_dir"])
    ref = ReferenceData.from_fixtures(
        cfg.get("fixtures_dir") or (BASE / "reference_cache"))

    files = ([exports / f"{r}_detailed.xlsx" for r in argv] if argv
             else sorted(exports.glob("*_detailed.xlsx")))
    fails = len(bad)
    for f in files:
        if not f.exists():
            print(f"[skip] {f.name}")
            continue
        rid = f.name[: -len("_detailed.xlsx")]
        df = pd.read_excel(f)
        if "flag" in df.columns:
            df["flag"] = df["flag"].fillna("")
        # Shapes that have broken a build before: no pan feeds, no OTT market,
        # a single broadcast day. Reported so a green run is not mistaken for
        # coverage of all three.
        shape = []
        if "channel_name" in df:
            shape.append(f"{df['country_name'].nunique()}mkt")
        shape.append(f"{len(df)}row")
        for kind, fn in (("vr", write_vr), ("client", write_report)):
            try:
                out = exports.parent / f".check_{rid}_{kind}.xlsx"
                fn(df, out, rid, f"check {rid}", ref)
                out.unlink(missing_ok=True)
                print(f"[ok]   {rid} {kind:6s} {' '.join(shape)}")
            except Exception:                       # noqa: BLE001
                fails += 1
                print(f"[FAIL] {rid} {kind:6s} {' '.join(shape)}")
                traceback.print_exc()
    print(f"\n{'FAILED' if fails else 'all good'} — {fails} problem(s)")
    return 1 if fails else 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
