"""
Phase-1 runner: PHOENIX Sicily ground-sensor placement.

Solves the SAME instance with every backend and scores them all by the one true
objective (risk-weighted coverage). Prints an honest comparison table and writes
the structured decision record to results/.

Usage:
    python run_placement.py                # operational instance (CP-SAT ships)
    python run_placement.py --qpu          # downsized instance (adds SB; QPU-ready)
"""
from __future__ import annotations
import sys, os, json, argparse
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from geo_optimize import (benchmark, solve_cpsat, solve_greedy, solve_sa_direct,
                          solve_ocean_sa, solve_sim_bifurcation)
from phoenix_sicily import build_phoenix_sicily


def fmt_table(record: dict) -> str:
    rows = []
    hdr = f"{'backend':<26} {'score':>8} {'cover%':>7} {'gap':>7} {'ms':>9}  {'opt':>3}"
    rows.append(hdr)
    rows.append("-" * len(hdr))
    order = sorted(record["backends"].items(),
                   key=lambda kv: (-kv[1]["score"], kv[1]["latency_ms"]))
    for name, b in order:
        skipped = (not b["selected"]) and (
            b["note"].lower().startswith(("skip", "error")))
        if skipped:
            rows.append(f"{name:<26} {'--':>8} {'--':>7} {'--':>7} {'--':>9}  "
                        f"{'-':>3}   ({b['note']})")
        else:
            rows.append(f"{name:<26} {b['score']:>8.3f} {b['coverage_fraction']*100:>6.1f}% "
                        f"{b['gap_vs_reference']:>7.3f} {b['latency_ms']:>9.2f}  "
                        f"{'Y' if b['proven_optimal'] else '-':>3}")
    return "\n".join(rows)


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--qpu", action="store_true", help="downsized QPU-scale instance")
    ap.add_argument("--seed", type=int, default=7)
    args = ap.parse_args()

    inst = build_phoenix_sicily(qpu_scale=args.qpu, seed=args.seed)
    print(f"\n=== PHOENIX Sicily ground-sensor placement "
          f"({'QPU-scale' if args.qpu else 'operational'}) ===")
    print(f"candidates={inst.C}  demand_cells={inst.D}  budget_p={inst.budget_p}  "
          f"total_risk={inst.total_risk():.1f}")
    print(f"coverable risk ceiling (all sites): "
          f"{inst.weights[inst.cover.any(axis=0)].sum():.1f} / {inst.total_risk():.1f}\n")

    backends = [solve_cpsat, solve_greedy, solve_sa_direct, solve_ocean_sa,
                solve_sim_bifurcation]
    record = benchmark(inst, backends)

    print(fmt_table(record))
    print("\nVERDICT:", record["explanation"])

    # attach the deployable recommendation (CP-SAT) with lat/lon
    cp = record["backends"].get("cpsat_exact", {})
    sel = cp.get("selected", [])
    latlon = inst.meta["candidates_latlon"]
    record["recommended_action"] = {
        "place_sensors_at_site_ids": sel,
        "site_latlon": [latlon[c] for c in sel],
        "risk_coverage_score": cp.get("score"),
        "coverage_fraction": cp.get("coverage_fraction"),
        "proven_optimal": cp.get("proven_optimal"),
        "latency_ms": cp.get("latency_ms"),
        "fallback": "greedy (always available; (1-1/e) bound)",
    }
    record["data_provenance"] = inst.meta["data_provenance"]
    record["product_boundary"] = ("PHOENIX-only instance of the neutral geo-optimize "
                                  "library; shares no data plane with OLDGABE/INGV instances")

    os.makedirs(os.path.join(os.path.dirname(__file__), "results"), exist_ok=True)
    out = os.path.join(os.path.dirname(__file__), "results",
                       f"{inst.name}.json")
    with open(out, "w", encoding="utf-8") as f:
        json.dump(record, f, indent=2)
    print(f"\nstructured record -> {out}")
    print("\nrecommended sites (CP-SAT optimum):")
    for c in sel:
        ll = latlon[c]
        print(f"  site {c:>2}  lat {ll[0]:.5f}  lon {ll[1]:.5f}")


if __name__ == "__main__":
    main()
