#!/usr/bin/env python3
"""Quét mức thiên lệch dự báo để tìm điểm vận hành đạt được chỉ tiêu.

VÌ SAO PHẢI QUÉT.  Ở FBI = 1 thì a + b = a + c, tức b = c, nên
    FAR = b/(a+b) = 1 - a/(a+b) = 1 - POD.
Chỉ tiêu {POD > 0.6, FAR < 0.2, FBI ~ 1} vì thế TƯƠNG ĐƯƠNG {POD > 0.8, FBI ~ 1}:
nó ngầm đòi POD trên 0.8 chứ không phải 0.6.  Muốn FAR < 0.2 ở mức POD 0.6-0.7 thì
BẮT BUỘC phải dự báo THIẾU, tức FBI < 1 -- đổi độ bắt lấy độ chính xác.

Script quét tỷ lệ phát báo r (tính theo bội của base rate, chính là FBI xấp xỉ),
chốt ngưỡng cho từng ô lưới trên trường NGOÀI MẪU rồi áp sang tập kiểm định.
"""

from __future__ import annotations

import json
import os
import sys

import numpy as np

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import ehd_common as C
import ehd_metrics as M

RES = os.path.join(C.OUT, "AI_Results")
NPZ = os.path.join(RES, "exp3_window2_scores.npz")
OUT = os.path.join(RES, "exp3_window2_sweep.json")
RGRID = np.round(np.arange(0.30, 1.35, 0.05), 3)
GOAL = dict(POD=0.60, POFD=0.20, FAR=0.20, CSI=0.40)


def meets(g):
    return (g["POD"] > GOAL["POD"] and g["POFD"] < GOAL["POFD"]
            and g["FAR"] < GOAL["FAR"] and g["CSI"] > GOAL["CSI"])


def main():
    z = np.load(NPZ, allow_pickle=True)
    keys = sorted({k.rsplit("|", 1)[0] for k in z.files if k.endswith("|s_val")})
    groups = {}
    for k in keys:
        ip, w, t = k.split("|")
        groups.setdefault((w, t), []).append(k)

    res = {"rgrid": RGRID.tolist(), "by_window": {}, "feasible": {}}
    for (w, t), ks in sorted(groups.items()):
        rows = []
        for r in RGRID:
            pv, ov = [], []
            for k in ks:
                so, oo = z[k + "|s_oof"], z[k + "|obs_oof"]
                sv, ov_ = z[k + "|s_val"], z[k + "|obs_val"]
                rate = float(np.clip(r * oo.mean(), 1e-4, 0.999))
                th = float(np.quantile(so, 1.0 - rate))
                pv.append(sv > th); ov.append(ov_)
            g = M.scores(*M.contingency(np.concatenate(pv), np.concatenate(ov)))
            g["r"] = float(r)
            rows.append(g)
        res["by_window"]["%s|%s" % (w, t)] = rows
        ok = [g for g in rows if meets(g)]
        if ok:
            b = max(ok, key=lambda g: g["CSI"])
            res["feasible"]["%s|%s" % (w, t)] = b
            print("[%-6s %-6s] ĐẠT ở r=%.2f: POD %.3f POFD %.3f FAR %.3f FBI %.3f "
                  "CSI %.3f SEDI %.3f" % (w, t, b["r"], b["POD"], b["POFD"],
                                          b["FAR"], b["FBI"], b["CSI"], b["SEDI"]),
                  flush=True)
        else:
            # điểm gần nhất: FAR nhỏ nhất trong các cấu hình còn POD > 0.6
            cand = [g for g in rows if g["POD"] > GOAL["POD"]]
            b = min(cand, key=lambda g: g["FAR"]) if cand else \
                max(rows, key=lambda g: g["CSI"])
            print("[%-6s %-6s] chưa đạt; gần nhất r=%.2f: POD %.3f FAR %.3f "
                  "FBI %.3f CSI %.3f" % (w, t, b["r"], b["POD"], b["FAR"],
                                         b["FBI"], b["CSI"]), flush=True)

    with open(OUT, "w") as fh:
        json.dump(res, fh, indent=1, default=float)
    print("\n[done] -> %s" % OUT, flush=True)


if __name__ == "__main__":
    main()
