"""Phase 2 sweep: parameters that actually move the result."""
import json
import subprocess
import shutil
from pathlib import Path

BASE = "configs/experiments/return_max_long_v7.356_composed_gld_compound.json"
SWEEP_DIR = Path("configs/experiments/_tune_v7356_sweep")
SWEEP_DIR.mkdir(exist_ok=True)
SNAPSHOT = "midlarge-liquid-long-v1_bucketfix_full_audit_canonical_ftb_fix_v2"


def make_config(name: str, risk_overrides: dict = None, exec_overrides: dict = None, signal_overrides: dict = None) -> str:
    with open(BASE) as f:
        cfg = json.load(f)
    cfg["experiment_name"] = name
    if risk_overrides:
        cfg["overrides"]["risk"].update(risk_overrides)
    if exec_overrides:
        cfg["overrides"]["execution"].update(exec_overrides)
    if signal_overrides:
        cfg["overrides"]["signal"].update(signal_overrides)
    path = SWEEP_DIR / f"{name}.json"
    path.write_text(json.dumps(cfg, indent=2))
    return str(path)


def run_backtest(config_path: str) -> dict:
    cmd = [
        "python3", "-m", "apps.backtester.run",
        "--manifest", config_path,
        "--start", "2022-01-01",
        "--split", "all",
        "--initial-equity", "10000",
        "--snapshot-id", SNAPSHOT,
    ]
    cache = Path(f"data/parquet/{SNAPSHOT}/.runtime_cache")
    if cache.exists():
        shutil.rmtree(cache)
    proc = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
    tail = proc.stdout.split("\n")[-10:]
    run_id = return_pct = sqs_total = trades = None
    for line in tail:
        if "Run complete:" in line:
            run_id = line.split("Run complete:")[1].strip()
        elif "Total return:" in line:
            return_pct = float(line.split(":")[1].strip().rstrip("%"))
        elif "SQS:" in line:
            sqs_total = float(line.split("SQS:")[1].strip().split(" ")[0])
        elif "Trades:" in line:
            trades = int(line.split(":")[1].strip())
    mdd = sharpe = pf = None
    if run_id:
        m_path = Path(f"runs/{run_id}/metrics/metrics_summary.json")
        if m_path.exists():
            m = json.loads(m_path.read_text())
            mdd = m["max_drawdown_pct"]
            sharpe = m["sharpe_ratio"]
            pf = m["profit_factor"]
    return {"return": return_pct, "trades": trades, "mdd": mdd, "sharpe": sharpe, "pf": pf, "sqs": sqs_total, "run_id": run_id}


# Aggressive sweep on parameters that should actually move results
SWEEPS = [
    # per_trade_risk: lower = each trade smaller = lower MDD
    ("T10_risk_05", {"per_trade_risk_pct": 0.5, "per_trade_risk_pct_a_tier": 0.55}, None, None),
    ("T11_risk_04", {"per_trade_risk_pct": 0.4, "per_trade_risk_pct_a_tier": 0.44}, None, None),
    ("T12_risk_08", {"per_trade_risk_pct": 0.8, "per_trade_risk_pct_a_tier": 0.88}, None, None),
    # max_positions: lower = less concentration
    ("T13_pos20", {"max_positions": 20}, None, None),
    ("T14_pos25", {"max_positions": 25}, None, None),
    # max_daily_new_risk
    ("T15_daily_30", {"max_daily_new_risk_pct": 30}, None, None),
    ("T16_daily_70", {"max_daily_new_risk_pct": 70}, None, None),
    # Holding period
    ("T17_mhd_8", None, {"max_holding_days": 8}, None),
    ("T18_mhd_15", None, {"max_holding_days": 15}, None),
    # Signal threshold tighter
    ("T19_score_50", None, None, {"score_threshold": 0.50}),
    ("T20_score_40", None, None, {"score_threshold": 0.40}),
    # Combined: best from Phase 1 + risk reduction
    ("T21_combo", {"macro_regime_risk_off_size_scaler": 0.5, "per_trade_risk_pct": 0.5, "per_trade_risk_pct_a_tier": 0.55}, None, None),
    ("T22_combo2", {"macro_regime_risk_off_size_scaler": 0.5, "per_trade_risk_pct": 0.55, "per_trade_risk_pct_a_tier": 0.60}, None, None),
]


def main():
    baseline = {"return": 2236.1, "mdd": 14.50, "sharpe": 2.72, "pf": 4.38, "sqs": 87.1, "trades": 293}
    print(f"BASELINE: ret={baseline['return']}% MDD={baseline['mdd']}% Sharpe={baseline['sharpe']} SQS={baseline['sqs']} trades={baseline['trades']}")
    print("=" * 110)
    results = []
    for name, risk_ov, exec_ov, sig_ov in SWEEPS:
        path = make_config(name, risk_ov, exec_ov, sig_ov)
        ov_str = f"risk={risk_ov or {}} exec={exec_ov or {}} signal={sig_ov or {}}"
        print(f"\n>>> {name}: {ov_str}")
        try:
            r = run_backtest(path)
            r["name"] = name
            d_ret = (r["return"] or 0) - baseline["return"]
            d_mdd = (r["mdd"] or 0) - baseline["mdd"]
            d_sqs = (r["sqs"] or 0) - baseline["sqs"]
            print(f"    ret={r['return']:.0f}% (Δ{d_ret:+.0f}) MDD={r['mdd']:.2f}% (Δ{d_mdd:+.2f}) Sharpe={r['sharpe']:.2f} PF={r['pf']:.2f} SQS={r['sqs']:.1f} (Δ{d_sqs:+.1f}) trades={r['trades']}")
            results.append(r)
        except Exception as e:
            print(f"    FAILED: {e}")

    print("\n" + "=" * 110)
    print("SUMMARY (sorted by SQS, then return):")
    results.sort(key=lambda x: (x.get("sqs") or 0, x.get("return") or 0), reverse=True)
    print(f"{'name':18s} {'return':>10s} {'MDD':>8s} {'Sharpe':>8s} {'PF':>6s} {'SQS':>7s} {'trades':>7s}")
    print(f"{'BASELINE':18s} {baseline['return']:>9.0f}% {baseline['mdd']:>7.2f}% {baseline['sharpe']:>8.2f} {baseline['pf']:>6.2f} {baseline['sqs']:>7.1f} {baseline['trades']:>7d}")
    for r in results:
        print(f"{r['name']:18s} {r['return']:>9.0f}% {r['mdd']:>7.2f}% {r['sharpe']:>8.2f} {r['pf']:>6.2f} {r['sqs']:>7.1f} {r['trades']:>7d}")


if __name__ == "__main__":
    main()
