"""SKH2_CHANDE_WIN — DD-reduction wave: re-tune indicator WINDOWS (Chande/ATR) for DD.

Family task: smoother indicators -> more stable regime -> potentially lower standalone maxDD.
We hold the VERIFIED V2 winner's pattern/exits/bands FIXED and sweep ONLY the windows:
    n_vola, n_volume in {7,13,21,34}
    atr_win           in {10,14,21}
    ltf_atr_win       in {10,14,21}

Everything is expressible via SkyhookParams -> the SHARED honest harness sk.study() applies
the exact leak-free FULL+HOLDOUT+fee-sweep+per-year machinery, and sk.causality / sk.marginal
give the same comparable numbers as every other agent.

WINNER (baseline to beat):
    SkyhookParams(ptn_n=45, sl_atr=2.5, tp_atr=7.0, uscitalong=24, uscitashort=16,
                  vola_lo=35.0, vola_hi=95.0, vol_lo=0.0)
    minFull +0.83 minHold +0.81 ; standalone DD BTC 34% / ETH 31% (>30% = the problem).

GOAL: max_dd < 0.30 while keeping minHold >= ~0.70 and earns_slot True, blend w25 uplift_hold >= 0.55.
"""
from __future__ import annotations

import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/skyhook")

import itertools

import skyhooklib as sk
from src.strategies.skyhook import SkyhookParams

# ---- fixed winner spine (pattern / exits / bands) --------------------------
FIXED = dict(ptn_n=45, sl_atr=2.5, tp_atr=7.0, uscitalong=24, uscitashort=16,
             vola_lo=35.0, vola_hi=95.0, vol_lo=0.0)


def mk(n_vola, n_volume, atr_win, ltf_atr_win):
    return SkyhookParams(n_vola=n_vola, n_volume=n_volume, atr_win=atr_win,
                         ltf_atr_win=ltf_atr_win, **FIXED)


def cheap_eval(p):
    """Fast standalone screen: FULL+HOLD on BTC&ETH only (no fee-sweep/marginal)."""
    rb = sk.run_asset("BTC", p)
    re = sk.run_asset("ETH", p)
    min_full = min(rb["full"]["sharpe"], re["full"]["sharpe"])
    min_hold = min(rb["holdout"]["sharpe"], re["holdout"]["sharpe"])
    max_dd = max(rb["full"]["maxdd"], re["full"]["maxdd"])
    min_tr = min(rb["full"]["n_trades"], re["full"]["n_trades"])
    return dict(min_full=min_full, min_hold=min_hold, max_dd=max_dd, min_tr=min_tr,
                btc_dd=rb["full"]["maxdd"], eth_dd=re["full"]["maxdd"],
                btc_hold=rb["holdout"]["sharpe"], eth_hold=re["holdout"]["sharpe"])


def earns_slot(rep, marg):
    return (rep["verdict"]["grade"] != "FAIL"
            and marg.get("marginal_verdict") == "ADDS"
            and bool(marg.get("robust_oos"))
            and not bool(marg.get("is_hedge")))


def beats_winner(rep, marg, ev):
    es = earns_slot(rep, marg)
    w25 = marg.get("blends", {}).get("w25", {}).get("uplift_hold")
    mh = rep["verdict"]["min_asset_holdout_sharpe"]
    return bool(es and ev["max_dd"] < 0.30 and (w25 is not None and w25 >= 0.55) and mh >= 0.65)


# ---- WINNER reference (so DD comparison is apples-to-apples in THIS harness) ----
def winner_params():
    return SkyhookParams(**FIXED)


if __name__ == "__main__":
    print("########## STAGE 1: cheap window screen (FULL+HOLD+DD, BTC&ETH) ##########")

    # winner reference in this harness
    wev = cheap_eval(winner_params())
    print(f"[WINNER ref] minFull={wev['min_full']:+.2f} minHold={wev['min_hold']:+.2f} "
          f"maxDD={wev['max_dd']*100:.0f}% (BTC {wev['btc_dd']*100:.0f}% / ETH {wev['eth_dd']*100:.0f}%) "
          f"minTr={wev['min_tr']}")

    n_vola_grid = [7, 13, 21, 34]
    n_volume_grid = [7, 13, 21, 34]
    atr_grid = [10, 14, 21]
    ltf_grid = [10, 14, 21]

    rows = []
    for nva, nvo, aw, law in itertools.product(n_vola_grid, n_volume_grid, atr_grid, ltf_grid):
        p = mk(nva, nvo, aw, law)
        ev = cheap_eval(p)
        rows.append((nva, nvo, aw, law, ev))

    # Sort by lowest DD among those that keep some hold-out edge & enough trades
    def keyf(r):
        ev = r[4]
        return ev["max_dd"]

    viable = [r for r in rows if r[4]["min_tr"] >= 20]
    viable.sort(key=keyf)

    print(f"\n--- {len(rows)} configs screened. Top 15 by LOWEST standalone maxDD "
          f"(min_tr>=20) ---")
    print(f"{'nva':>4}{'nvo':>4}{'aw':>4}{'law':>5}  {'maxDD':>7} {'btcDD':>6} {'ethDD':>6} "
          f"{'minFull':>8} {'minHold':>8} {'minTr':>6}")
    for nva, nvo, aw, law, ev in viable[:15]:
        print(f"{nva:>4}{nvo:>4}{aw:>4}{law:>5}  {ev['max_dd']*100:>6.1f}% "
              f"{ev['btc_dd']*100:>5.1f}% {ev['eth_dd']*100:>5.1f}% "
              f"{ev['min_full']:>+8.2f} {ev['min_hold']:>+8.2f} {ev['min_tr']:>6}")

    # STAGE-1 LEARNING (from broad probes): no window combo gets BOTH BTC&ETH sub-30%
    # (BTC & ETH DD move in OPPOSITE directions vs n_vola/atr_win). The best DD-CUT that
    # also keeps hold-out is the SLOWER-INDICATOR corner. Study the lowest-DD configs that
    # still keep minHold>=0.50 (the real DD/hold tradeoff frontier), plus a couple extras
    # found by the broad probe (n_vola=13, slower atr_win/ltf_atr_win).
    extra = [(13, 13, 18, 18), (13, 13, 14, 18), (13, 13, 21, 18)]  # (nva,nvo,aw,law)
    # candidates that cut DD while keeping hold-out
    cands = [r for r in viable
             if r[4]["min_hold"] >= 0.50 and r[4]["min_full"] >= 0.50]
    cands.sort(key=lambda r: r[4]["max_dd"])  # lowest DD first
    study_keys = set()
    study_list = []
    for r in cands[:5]:
        k = (r[0], r[1], r[2], r[3])
        if k not in study_keys:
            study_keys.add(k); study_list.append(r)
    # ensure the broad-probe extras are studied (they may not be on the coarse grid)
    for nva, nvo, aw, law in extra:
        k = (nva, nvo, aw, law)
        if k not in study_keys:
            ev = cheap_eval(mk(nva, nvo, aw, law))
            study_keys.add(k); study_list.append((nva, nvo, aw, law, ev))
    if not study_list:
        study_list = viable[:4]

    print(f"\n########## STAGE 2: FULL study + causality + marginal on "
          f"{len(study_list)} candidate(s) ##########")

    results = []
    for nva, nvo, aw, law, ev in study_list:
        p = mk(nva, nvo, aw, law)
        name = f"CW_nva{nva}_nvo{nvo}_aw{aw}_law{law}"
        rep = sk.study(name, p)
        caus_b = sk.causality(p, "BTC")
        caus_e = sk.causality(p, "ETH")
        marg = sk.marginal(p)
        caus_ok = bool(caus_b["ok"] and caus_e["ok"])
        es = earns_slot(rep, marg)
        bw = beats_winner(rep, marg, ev) and caus_ok
        w25 = marg.get("blends", {}).get("w25", {}).get("uplift_hold")
        w50 = marg.get("blends", {}).get("w50", {})
        results.append(dict(name=name, p=p, rep=rep, marg=marg, ev=ev,
                            caus_ok=caus_ok, es=es, bw=bw, w25=w25, w50=w50,
                            cfg=dict(n_vola=nva, n_volume=nvo, atr_win=aw, ltf_atr_win=law)))
        print("\n" + sk.fmt(rep))
        print(f"  causality BTC={caus_b} ETH={caus_e}")
        print(f"  marginal: verdict={marg.get('marginal_verdict')} corr_full={marg.get('corr_full')} "
              f"has_insample_edge={marg.get('has_insample_edge')} is_hedge={marg.get('is_hedge')} "
              f"robust_oos={marg.get('robust_oos')} multicut_persistent={marg.get('multicut_persistent')}")
        print(f"            clean_year_uplift={marg.get('clean_year_uplift')} "
              f"blend_w25_uplift_hold={w25} w50={w50}")
        print(f"  >> earns_slot={es}  beats_winner={bw}  standaloneDD={ev['max_dd']*100:.1f}%")

    # ---- pick best: prefer beats_winner; else lowest DD among earns_slot; else lowest DD ----
    def rank(r):
        # higher is better. Priority: (1) beats_winner, (2) earns_slot, then PORTFOLIO VALUE
        # (blend w25 uplift_hold + min-asset hold-out) which the wave objectives (2)&(3) reward,
        # with DD as a final tiebreak. NOTE: no window combo reaches max_dd<0.30 (DD wall is
        # structural: BTC & ETH DD move in OPPOSITE directions vs the vola window) so we report
        # the strongest earns_slot config rather than chasing an unreachable DD gate.
        w25 = r["w25"] if r["w25"] is not None else -9
        return (1 if r["bw"] else 0,
                1 if r["es"] else 0,
                round(w25, 3),
                r["rep"]["verdict"]["min_asset_holdout_sharpe"],
                -r["ev"]["max_dd"])

    if results:
        best = sorted(results, key=rank, reverse=True)[0]
        b = best
        rep = b["rep"]; marg = b["marg"]; ev = b["ev"]
        v = rep["verdict"]
        print("\n" + "=" * 78)
        print("FINAL BEST CONFIG (CHANDE_WIN family)")
        print("=" * 78)
        print(f"  config            = {b['cfg']}  (+ fixed winner spine {FIXED})")
        print(f"  name              = {b['name']}")
        print(f"  minFull           = {v['min_asset_full_sharpe']:+.3f}")
        print(f"  minHold           = {v['min_asset_holdout_sharpe']:+.3f}  "
              f"(BTC {rep['per_asset']['BTC']['holdout']['sharpe']:+.2f} / "
              f"ETH {rep['per_asset']['ETH']['holdout']['sharpe']:+.2f})")
        print(f"  standalone max_dd = {ev['max_dd']:.4f}  "
              f"(BTC {ev['btc_dd']:.4f} / ETH {ev['eth_dd']:.4f})")
        print(f"  n_trades_min      = {v['min_trades']}")
        print(f"  fee_survives 0.30%= {v['fee_survives']}")
        print(f"  causality_ok      = {b['caus_ok']}")
        print(f"  grade             = {v['grade']}")
        print(f"  --- marginal vs TP01 ---")
        print(f"  corr_full         = {marg.get('corr_full')}")
        print(f"  marginal_verdict  = {marg.get('marginal_verdict')}")
        print(f"  has_insample_edge = {marg.get('has_insample_edge')}")
        print(f"  is_hedge          = {marg.get('is_hedge')}")
        print(f"  robust_oos        = {marg.get('robust_oos')}")
        print(f"  multicut_persist  = {marg.get('multicut_persistent')}")
        print(f"  clean_year_uplift = {marg.get('clean_year_uplift')}")
        print(f"  blend w25 uplift_hold = {b['w25']}")
        print(f"  blend w50         = {b['w50']}")
        print(f"  earns_slot        = {b['es']}")
        print(f"  BEATS_WINNER      = {b['bw']}")
        print("=" * 78)

        # machine-readable line for the harness operator
        import json
        out = dict(
            family="CHANDE_WIN", best_config=b["cfg"], fixed=FIXED, name=b["name"],
            grade=v["grade"], min_full=v["min_asset_full_sharpe"],
            min_hold=v["min_asset_holdout_sharpe"], max_dd=ev["max_dd"],
            btc_dd=ev["btc_dd"], eth_dd=ev["eth_dd"], n_trades_min=v["min_trades"],
            fee_survives=v["fee_survives"], causality_ok=b["caus_ok"],
            corr_full=marg.get("corr_full"), marginal_verdict=marg.get("marginal_verdict"),
            has_insample_edge=marg.get("has_insample_edge"), is_hedge=marg.get("is_hedge"),
            robust_oos=marg.get("robust_oos"),
            multicut_persistent=marg.get("multicut_persistent"),
            clean_year_uplift=marg.get("clean_year_uplift"),
            blend_w25_uplift_hold=b["w25"], earns_slot=b["es"], beats_winner=b["bw"],
        )
        print("RESULT_JSON " + json.dumps(out, default=str))
