#!/usr/bin/env python3
"""Recompute the dashboard chart payload for runs already on disk.

The payload is written once, at the end of a run. When a new figure is added to
it -- broadcast hours, total AMA -- every run estimated before that change is
missing the key, and the tile that reads it silently does not appear. Rather
than telling people to re-run an estimate they already paid for, rebuild the
payload from the detailed workbook, which holds the same frame the pipeline
finished with.

    python3 worker/refresh_chartdata.py            # every run with a workbook
    python3 worker/refresh_chartdata.py 5496E20EBF1A ...   # named runs only

Safe to re-run: it recomputes from the workbook every time and never touches
the estimates themselves.
"""
from __future__ import annotations

import json
import sys
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

from engine.pipeline import _build_chart_payload, _json_safe  # noqa: E402
from engine.reference import ReferenceData  # noqa: E402


def refresh(detailed: Path, exports: Path, ref=None) -> tuple[str, str]:
    run_id = detailed.name[: -len("_detailed.xlsx")]
    df = pd.read_excel(detailed)
    if "flag" in df.columns:
        df["flag"] = df["flag"].fillna("")
    # ref supplies the OTT/OOH multipliers and the pan mapping. Without it
    # those figures come back zero, which is the same thing the workbook's
    # platform sheets would show -- wrong to ship silently, so main() loads it.
    payload = _json_safe(_build_chart_payload(df, ref))
    # Write beside the target and swap, so a reader mid-request never sees a
    # half-written file.
    out = exports / f"{run_id}_chartdata.json"
    tmp = out.with_suffix(".json.tmp")
    tmp.write_text(json.dumps(payload, allow_nan=False))
    tmp.replace(out)
    k = payload["kpi"]
    return run_id, (f"{k['rows_estimated']:,}/{k['rows_total']:,} rows · "
                    f"{k['total_ama_000']/1000:,.1f}M TV · "
                    f"{k['ott_000']/1000:,.1f}M OTT · "
                    f"{k['ooh_000']/1000:,.1f}M OOH · "
                    f"{k['broadcast_hours']:,} hrs")


def main(argv: list[str]) -> int:
    cfg = json.loads((BASE / "worker" / "config.json").read_text())
    exports = Path(cfg["exports_dir"])
    # Loaded once for all runs -- it is the expensive part.
    try:
        ref = ReferenceData.from_fixtures(cfg.get("fixtures_dir") or (BASE / "reference_cache"))
    except Exception as e:                          # noqa: BLE001
        print(f"[warn] no reference data ({type(e).__name__}: {e}) — "
              "OTT and OOH will be written as zero")
        ref = None
    if argv:
        files = [exports / f"{r}_detailed.xlsx" for r in argv]
    else:
        files = sorted(exports.glob("*_detailed.xlsx"))
    ok = bad = 0
    for f in files:
        if not f.exists():
            print(f"[skip] {f.name} — no detailed workbook")
            bad += 1
            continue
        try:
            rid, note = refresh(f, exports, ref)
            print(f"[ok]   {rid}  {note}")
            ok += 1
        except Exception as e:                      # noqa: BLE001
            # One unreadable workbook must not stop the rest.
            print(f"[fail] {f.name} — {type(e).__name__}: {e}")
            bad += 1
    print(f"refreshed {ok}, skipped {bad}")
    return 0 if ok or not files else 1


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