#!/usr/bin/env python3
"""UNREGISTERED descriptive checks on the AM-fix result. Nothing here can move the
verdict, which rule.py fixed and analyse.py computed. These exist so that a reader can
see whether the sealed number is a quoting artefact of one minute or one side of the book.

    python robustness.py [--bars ~/data/bars/XAUUSD_2025_m1.csv]
"""
import argparse, pathlib, sys, os
import numpy as np, pandas as pd
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from analyse import load, bp, M_1015, M_1030, LONDON  # noqa

def frac_neg(a, b):
    r = bp(a, b); ok = np.isfinite(r)
    return float((r[ok] < 0).mean()), int(ok.sum()), float(np.nanmean(r[ok]))

def main():
    ap = argparse.ArgumentParser(); ap.add_argument("--bars", default="~/data/bars/XAUUSD_2025_m1.csv")
    a = ap.parse_args()
    df = pd.read_csv(pathlib.Path(a.bars).expanduser(), parse_dates=["minute"])
    ts = df["minute"].dt.tz_convert(LONDON)
    df["day"] = ts.dt.date; df["m"] = ts.dt.hour * 60 + ts.dt.minute
    days = sorted(df["day"].unique()); ix = {d: i for i, d in enumerate(days)}
    def grid(col):
        g = np.full((len(days), 1440), np.nan); g[df["day"].map(ix).to_numpy(), df["m"].to_numpy()] = df[col].to_numpy(); return g
    bid, ask = grid("bid_close"), grid("ask_close"); mid = (bid + ask) / 2
    print(f"days with both fix bars: {int(np.isfinite(mid[:, M_1015] * mid[:, M_1030]).sum())} of {len(days)}")
    print("\nrecount, mid, 10:15 -> 10:30 (the sealed window, all days with both bars, no pool floor):")
    f, n, mu = frac_neg(mid[:, M_1015], mid[:, M_1030]); print(f"  F={f:.4f} n={n} mean={mu:+.3f} bp")
    print("\none side of the book only:")
    for name, g in (("bid", bid), ("ask", ask)):
        f, n, mu = frac_neg(g[:, M_1015], g[:, M_1030]); print(f"  {name}: F={f:.4f} n={n} mean={mu:+.3f} bp")
    print("\nspread (ask-bid, in price units) at the two ends:")
    sp = ask - bid
    print(f"  10:15 median {np.nanmedian(sp[:, M_1015]):.3f}   10:30 median {np.nanmedian(sp[:, M_1030]):.3f}   10:29 median {np.nanmedian(sp[:, M_1030-1]):.3f}   10:31 median {np.nanmedian(sp[:, M_1030+1]):.3f}")
    print("\nshifted windows (mid), start -> end, London clock:")
    for s, e in ((M_1015 - 5, M_1030 - 5), (M_1015 + 5, M_1030 + 5), (M_1015, M_1030 - 1), (M_1015, M_1030 - 2), (M_1015 - 15, M_1015), (M_1030, M_1030 + 15), (M_1015 - 30, M_1015 - 15)):
        f, n, mu = frac_neg(mid[:, s], mid[:, e]); print(f"  {s//60:02d}:{s%60:02d} -> {e//60:02d}:{e%60:02d}  F={f:.4f} n={n} mean={mu:+.3f} bp")
    print("\nby quarter (mid, sealed window):")
    q = pd.Series([pd.Timestamp(d).quarter for d in days])
    r = bp(mid[:, M_1015], mid[:, M_1030])
    for k in (1, 2, 3, 4):
        sel = (q == k).to_numpy() & np.isfinite(r); print(f"  Q{k}: F={(r[sel] < 0).mean():.4f} n={int(sel.sum())} mean={r[sel].mean():+.3f} bp")
    print("\nby minute inside the window: fraction of days each 1-minute step is down (mid):")
    steps = []
    for m in range(M_1015, M_1030):
        rr = bp(mid[:, m], mid[:, m + 1]); ok = np.isfinite(rr); steps.append((m, float((rr[ok] < 0).mean()), float(rr[ok].mean())))
    print("  " + "  ".join(f"{m%60:02d}:{f:.2f}" for m, f, _ in steps))
    print("  mean bp per minute: " + "  ".join(f"{mu:+.2f}" for _, _, mu in steps))

if __name__ == "__main__":
    main()
