#!/usr/bin/env python3
"""
TIER 1 OPTIMIZATION SWEEP — LOT_SIZE 0.02 baseline
=====================================================
Sweeps three variables independently around the winning 0.02 config:
  1. THREAD_PROFIT_TARGET: $8, $10, $12, $15, $18, $20
  2. RECOVERY_PROFIT_TARGET: $15, $20, $25, $30, $40, $50
  3. MAX_INITIAL_ORDERS: 6, 8, 10, 12, 15

Then runs top combos from each sweep together.

Usage:
    python3 run_tier1_sweep.py --max-rows=2000000   # fast sample (~3 min per variant)
    python3 run_tier1_sweep.py                       # full dataset
"""

import sys, os, json, time, importlib
from datetime import datetime

os.chdir(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ".")

# Lower CPU priority
try:
    os.nice(10)
except OSError:
    pass

import settings
from run_btc_backtest import load_ticks_to_candles
from backtest import BacktestEngine
from utils import setup_logging

CSV_PATH = "BTCUSD_PAST6MONTHS.csv"
RESULTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "results")
PROGRESS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "backtest_progress.json")


def write_progress(data):
    tmp = PROGRESS_FILE + ".tmp"
    try:
        with open(tmp, "w") as f:
            json.dump(data, f, indent=2, default=str)
        os.replace(tmp, PROGRESS_FILE)
    except Exception:
        pass


def save_snapshot():
    keys = [
        "LOT_SIZE", "MAX_INITIAL_ORDERS", "ENTRY_TP_PIPS", "ENTRY_SL_PIPS",
        "RECOVERY_GRID_STEPS", "RECOVERY_TPS", "THREAD_PROFIT_TARGET",
        "RECOVERY_PROFIT_TARGET",
        "_REF_ENTRY_TP_PIPS", "_REF_RECOVERY_TPS", "_REF_THREAD_PROFIT_TARGET",
    ]
    return {k: getattr(settings, k) for k in keys}


def restore(snap):
    for k, v in snap.items():
        setattr(settings, k, v)
    import hedge_recovery_engine
    importlib.reload(hedge_recovery_engine)


def apply_overrides(ov):
    for k, v in ov.items():
        setattr(settings, k, v)
    if any(k.startswith("_REF_") for k in ov):
        norm = settings._normalize_for_pair(settings.SYMBOL)
        settings.ENTRY_TP_PIPS = norm["entry_tp_pips"]
        settings.RECOVERY_GRID_STEPS = norm["grid_steps"]
        settings.RECOVERY_TPS = norm["recovery_tps"]
    if "_REF_THREAD_PROFIT_TARGET" in ov:
        settings.THREAD_PROFIT_TARGET = ov["_REF_THREAD_PROFIT_TARGET"]
    import hedge_recovery_engine
    importlib.reload(hedge_recovery_engine)


def run_one(name, ov, data):
    snap = save_snapshot()
    try:
        apply_overrides(ov)
        engine = BacktestEngine(
            symbol=settings.SYMBOL,
            initial_balance=settings.INITIAL_BALANCE,
            commission_percent=settings.COMMISSION_PERCENT,
            slippage_pips=settings.SLIPPAGE_PIPS,
        )
        r = engine.run(data, progress_callback=None)
        return {
            "name": name,
            "net": r.total_return,
            "pct": r.total_return_percent,
            "trades": r.total_trades,
            "wr": r.win_rate,
            "gp": r.gross_profit,
            "gl": r.gross_loss,
            "comm": r.total_commission,
            "pf": r.gross_profit / r.gross_loss if r.gross_loss > 0 else 999,
            "dd": r.max_drawdown_percent,
            "min_eq": r.min_equity,
            "max_rec": r.max_recovery_level,
            "lot": settings.LOT_SIZE,
            "mo": settings.MAX_INITIAL_ORDERS,
            "ttp": settings.THREAD_PROFIT_TARGET,
            "rpt": settings.RECOVERY_PROFIT_TARGET,
            "rar": r.total_return_percent / r.max_drawdown_percent if r.max_drawdown_percent > 0 else 0,
        }
    finally:
        restore(snap)


def print_table(title, results):
    print(f"\n{'=' * 140}")
    print(f"  {title}")
    print(f"{'=' * 140}")
    print(f"{'#':>2} {'VARIANT':<45} {'Net $':>10} {'Return':>8} {'MaxDD%':>7} {'PF':>5} {'RAR':>6} "
          f"{'Trades':>7} {'Win%':>6} {'Comm$':>10} {'ThTP$':>6} {'RecTP$':>6} {'MaxOrd':>6}")
    print("-" * 140)

    for i, r in enumerate(results):
        marker = " <-- BEST" if i == 0 else ""
        print(f"{i+1:>2} {r['name']:<45} ${r['net']:>9,.0f} {r['pct']:>+7.1f}% {r['dd']:>6.1f}% "
              f"{r['pf']:>5.2f} {r['rar']:>6.1f} {r['trades']:>7,} {r['wr']:>5.1f}% "
              f"${r['comm']:>9,.0f} ${r['ttp']:>5.1f} ${r['rpt']:>5.1f} {r['mo']:>6}{marker}")


def main():
    setup_logging(console=False, file=True)

    max_rows = None
    for arg in sys.argv[1:]:
        if arg.startswith("--max-rows="):
            max_rows = int(arg.split("=")[1])

    print("=" * 60)
    print("  TIER 1 OPTIMIZATION SWEEP")
    print(f"  Baseline: 0.02 lot, $15 thread, $30 recovery, 10 orders")
    print(f"  Data: {CSV_PATH} {'(sampled ' + str(max_rows) + ' rows)' if max_rows else '(full)'}")
    print("=" * 60)

    print("\nLoading data...")
    data = load_ticks_to_candles(CSV_PATH, timeframe="5min", max_rows=max_rows)
    print(f"Loaded {len(data)} candles\n")

    all_results = []

    # =========================================================================
    # SWEEP 1: Thread Profit Target (hold lot=0.02, recovery=$30, orders=10)
    # =========================================================================
    print("=" * 60)
    print("  SWEEP 1: THREAD PROFIT TARGET")
    print("=" * 60)

    sweep1_variants = {
        "ThreadTP=$8":  {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": 8.0},
        "ThreadTP=$10": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": 10.0},
        "ThreadTP=$12": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": 12.0},
        "ThreadTP=$15 (baseline)": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": 15.0},
        "ThreadTP=$18": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": 18.0},
        "ThreadTP=$20": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": 20.0},
        "ThreadTP=$25": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": 25.0},
    }

    sweep1_results = []
    total_variants = len(sweep1_variants) + 6 + 5 + 4  # all sweeps
    done = 0

    for name, ov in sweep1_variants.items():
        done += 1
        write_progress({"status": "running", "sweep": "Tier1", "variant": name,
                        "progress": f"{done}/{total_variants}", "updated_at": datetime.now().isoformat()})
        print(f"  [{done}/{total_variants}] {name}...", end=" ", flush=True)
        t0 = time.time()
        r = run_one(name, ov, data)
        elapsed = time.time() - t0
        sweep1_results.append(r)
        print(f"${r['net']:>+10,.0f} ({r['pct']:>+.1f}%)  DD:{r['dd']:.1f}%  PF:{r['pf']:.2f}  RAR:{r['rar']:.1f}  [{elapsed:.0f}s]")
        time.sleep(0.5)  # brief CPU cooldown

    sweep1_results.sort(key=lambda x: x["rar"], reverse=True)
    print_table("SWEEP 1 RESULTS: Thread Profit Target (sorted by RAR)", sweep1_results)
    best_ttp = sweep1_results[0]["ttp"]
    print(f"\n  >>> Best Thread TP: ${best_ttp}")

    # =========================================================================
    # SWEEP 2: Recovery Profit Target (hold lot=0.02, thread=best, orders=10)
    # =========================================================================
    print(f"\n{'=' * 60}")
    print(f"  SWEEP 2: RECOVERY PROFIT TARGET (using ThreadTP=${best_ttp})")
    print(f"{'=' * 60}")

    sweep2_variants = {
        "RecTP=$15": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": 15.0},
        "RecTP=$20": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": 20.0},
        "RecTP=$25": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": 25.0},
        "RecTP=$30 (baseline)": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": 30.0},
        "RecTP=$40": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": 40.0},
        "RecTP=$50": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": 50.0},
    }

    sweep2_results = []
    for name, ov in sweep2_variants.items():
        done += 1
        write_progress({"status": "running", "sweep": "Tier1", "variant": name,
                        "progress": f"{done}/{total_variants}", "updated_at": datetime.now().isoformat()})
        print(f"  [{done}/{total_variants}] {name}...", end=" ", flush=True)
        t0 = time.time()
        r = run_one(name, ov, data)
        elapsed = time.time() - t0
        sweep2_results.append(r)
        print(f"${r['net']:>+10,.0f} ({r['pct']:>+.1f}%)  DD:{r['dd']:.1f}%  PF:{r['pf']:.2f}  RAR:{r['rar']:.1f}  [{elapsed:.0f}s]")
        time.sleep(0.5)

    sweep2_results.sort(key=lambda x: x["rar"], reverse=True)
    print_table("SWEEP 2 RESULTS: Recovery Profit Target (sorted by RAR)", sweep2_results)
    best_rpt = sweep2_results[0]["rpt"]
    print(f"\n  >>> Best Recovery TP: ${best_rpt}")

    # =========================================================================
    # SWEEP 3: Max Initial Orders (hold lot=0.02, thread=best, recovery=best)
    # =========================================================================
    print(f"\n{'=' * 60}")
    print(f"  SWEEP 3: MAX INITIAL ORDERS (using ThreadTP=${best_ttp}, RecTP=${best_rpt})")
    print(f"{'=' * 60}")

    sweep3_variants = {
        "MaxOrd=6":  {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": best_rpt, "MAX_INITIAL_ORDERS": 6},
        "MaxOrd=8":  {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": best_rpt, "MAX_INITIAL_ORDERS": 8},
        "MaxOrd=10 (baseline)": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": best_rpt, "MAX_INITIAL_ORDERS": 10},
        "MaxOrd=12": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": best_rpt, "MAX_INITIAL_ORDERS": 12},
        "MaxOrd=15": {"LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp, "RECOVERY_PROFIT_TARGET": best_rpt, "MAX_INITIAL_ORDERS": 15},
    }

    sweep3_results = []
    for name, ov in sweep3_variants.items():
        done += 1
        write_progress({"status": "running", "sweep": "Tier1", "variant": name,
                        "progress": f"{done}/{total_variants}", "updated_at": datetime.now().isoformat()})
        print(f"  [{done}/{total_variants}] {name}...", end=" ", flush=True)
        t0 = time.time()
        r = run_one(name, ov, data)
        elapsed = time.time() - t0
        sweep3_results.append(r)
        print(f"${r['net']:>+10,.0f} ({r['pct']:>+.1f}%)  DD:{r['dd']:.1f}%  PF:{r['pf']:.2f}  RAR:{r['rar']:.1f}  [{elapsed:.0f}s]")
        time.sleep(0.5)

    sweep3_results.sort(key=lambda x: x["rar"], reverse=True)
    print_table("SWEEP 3 RESULTS: Max Initial Orders (sorted by RAR)", sweep3_results)
    best_mo = sweep3_results[0]["mo"]
    print(f"\n  >>> Best Max Orders: {best_mo}")

    # =========================================================================
    # COMBO RUN: Top combos from all 3 sweeps
    # =========================================================================
    print(f"\n{'=' * 60}")
    print(f"  COMBO RUNS: Best values combined")
    print(f"  ThreadTP=${best_ttp}, RecTP=${best_rpt}, MaxOrd={best_mo}")
    print(f"{'=' * 60}")

    # Get runner-up values from each sweep for cross-validation
    s1_top2 = [r["ttp"] for r in sweep1_results[:2]]
    s2_top2 = [r["rpt"] for r in sweep2_results[:2]]
    s3_top2 = [r["mo"] for r in sweep3_results[:2]]

    combo_variants = {
        f"OPTIMAL: TTP=${best_ttp}, RTP=${best_rpt}, MO={best_mo}": {
            "LOT_SIZE": 0.02,
            "_REF_THREAD_PROFIT_TARGET": best_ttp,
            "RECOVERY_PROFIT_TARGET": best_rpt,
            "MAX_INITIAL_ORDERS": best_mo,
        },
        f"BASELINE: TTP=$15, RTP=$30, MO=10": {
            "LOT_SIZE": 0.02,
            "_REF_THREAD_PROFIT_TARGET": 15.0,
            "RECOVERY_PROFIT_TARGET": 30.0,
            "MAX_INITIAL_ORDERS": 10,
        },
    }

    # Add runner-up combos if different from optimal
    if len(s1_top2) > 1 and s1_top2[1] != best_ttp:
        combo_variants[f"ALT-TTP: TTP=${s1_top2[1]}, RTP=${best_rpt}, MO={best_mo}"] = {
            "LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": s1_top2[1],
            "RECOVERY_PROFIT_TARGET": best_rpt, "MAX_INITIAL_ORDERS": best_mo,
        }
    if len(s2_top2) > 1 and s2_top2[1] != best_rpt:
        combo_variants[f"ALT-RTP: TTP=${best_ttp}, RTP=${s2_top2[1]}, MO={best_mo}"] = {
            "LOT_SIZE": 0.02, "_REF_THREAD_PROFIT_TARGET": best_ttp,
            "RECOVERY_PROFIT_TARGET": s2_top2[1], "MAX_INITIAL_ORDERS": best_mo,
        }

    combo_results = []
    for name, ov in combo_variants.items():
        done += 1
        write_progress({"status": "running", "sweep": "Tier1-Combo", "variant": name,
                        "progress": f"{done}/{total_variants}", "updated_at": datetime.now().isoformat()})
        print(f"  [{done}/{total_variants}] {name}...", end=" ", flush=True)
        t0 = time.time()
        r = run_one(name, ov, data)
        elapsed = time.time() - t0
        combo_results.append(r)
        print(f"${r['net']:>+10,.0f} ({r['pct']:>+.1f}%)  DD:{r['dd']:.1f}%  PF:{r['pf']:.2f}  RAR:{r['rar']:.1f}  [{elapsed:.0f}s]")
        time.sleep(0.5)

    combo_results.sort(key=lambda x: x["rar"], reverse=True)
    print_table("COMBO RESULTS: Best params combined (sorted by RAR)", combo_results)

    # =========================================================================
    # FINAL SUMMARY
    # =========================================================================
    all_results = sweep1_results + sweep2_results + sweep3_results + combo_results
    all_results.sort(key=lambda x: x["rar"], reverse=True)

    print(f"\n\n{'#' * 140}")
    print(f"  TIER 1 SWEEP — FINAL LEADERBOARD (ALL {len(all_results)} VARIANTS)")
    print(f"{'#' * 140}")
    print_table("ALL RESULTS RANKED BY RAR", all_results[:15])

    winner = all_results[0]
    print(f"\n{'*' * 60}")
    print(f"  WINNER: {winner['name']}")
    print(f"  Net: ${winner['net']:,.0f} ({winner['pct']:+.1f}%)")
    print(f"  DD: {winner['dd']:.1f}% | PF: {winner['pf']:.2f} | RAR: {winner['rar']:.1f}")
    print(f"  ThreadTP: ${winner['ttp']} | RecTP: ${winner['rpt']} | MaxOrd: {winner['mo']}")
    print(f"{'*' * 60}")

    # Save results to JSON
    os.makedirs(RESULTS_DIR, exist_ok=True)
    result_path = os.path.join(RESULTS_DIR, f"tier1_sweep_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json")
    with open(result_path, "w") as f:
        json.dump({
            "sweep": "tier1",
            "timestamp": datetime.now().isoformat(),
            "data": CSV_PATH,
            "max_rows": max_rows,
            "candles": len(data),
            "sweep1_thread_tp": sweep1_results,
            "sweep2_recovery_tp": sweep2_results,
            "sweep3_max_orders": sweep3_results,
            "combos": combo_results,
            "winner": winner,
            "best_params": {
                "LOT_SIZE": 0.02,
                "THREAD_PROFIT_TARGET": winner["ttp"],
                "RECOVERY_PROFIT_TARGET": winner["rpt"],
                "MAX_INITIAL_ORDERS": winner["mo"],
            }
        }, f, indent=2)
    print(f"\nResults saved to {result_path}")

    write_progress({
        "status": "completed",
        "sweep": "tier1",
        "winner": winner["name"],
        "variants_tested": len(all_results),
        "updated_at": datetime.now().isoformat(),
    })


if __name__ == "__main__":
    main()
