#!/usr/bin/env python3
"""
Validate optimizer winners — 6-month month-by-month with margin analysis.
Usage: nohup python3 -u validate_winners.py > results/validation_log.txt 2>&1 &
"""
import os, sys, json, csv, subprocess, time, re, glob, signal
from datetime import datetime

os.chdir(os.path.dirname(os.path.abspath(__file__)))

SETTINGS_FILE = "settings.py"
RESULTS_DIR = "results"
MONTHS = ["202508", "202509", "202510", "202511", "202512", "202601"]
START_BALANCE = 20000.0
PROGRESS_FILE = os.path.join(RESULTS_DIR, "validation_progress.json")

# Winners to validate
WINNERS = [
    {"label": "W1", "LOT_SIZE": 0.015, "THREAD_PROFIT_TARGET": 12, "MAX_INITIAL_ORDERS": 10,
     "MIN_ENTRY_SPACING": 0, "PROGRESSIVE_RLM_ENABLED": False, "desc": "0.015/TTP12/MO10"},
    {"label": "W2", "LOT_SIZE": 0.015, "THREAD_PROFIT_TARGET": 15, "MAX_INITIAL_ORDERS": 10,
     "MIN_ENTRY_SPACING": 0, "PROGRESSIVE_RLM_ENABLED": False, "desc": "0.015/TTP15/MO10 (B5)"},
    {"label": "W3", "LOT_SIZE": 0.010, "THREAD_PROFIT_TARGET": 12, "MAX_INITIAL_ORDERS": 10,
     "MIN_ENTRY_SPACING": 0, "PROGRESSIVE_RLM_ENABLED": False, "desc": "0.010/TTP12/MO10 (tiny)"},
    {"label": "W4", "LOT_SIZE": 0.020, "THREAD_PROFIT_TARGET": 12, "MAX_INITIAL_ORDERS": 10,
     "MIN_ENTRY_SPACING": 50, "PROGRESSIVE_RLM_ENABLED": False, "desc": "0.020/TTP12/MO10/SP50"},
]

FIXED = {
    "RECOVERY_PROFIT_TARGET": 30.0, "RECOVERY_LOT_MULTIPLIER": 1.0,
    "ADAPTIVE_GRID": True, "REFERENCE_ATR": 132.1, "COMMISSION_PERCENT": 0.025,
    "PROGRESSIVE_RLM_START": 25, "PROGRESSIVE_RLM_RAMP": True, "PROGRESSIVE_RLM_MAX": 1.3,
    "MAX_LEVERAGE": 10.0, "MARGIN_CAP_ENABLED": False, "MARGIN_RESERVE_PCT": 0.20,
    "MARGIN_RELIEF_ENABLED": False, "MARGIN_RELIEF_LEVEL": 4, "MARGIN_RELIEF_CLOSE_SUBS": True,
}

SETTINGS_BACKUP = None

def read_settings():
    with open(SETTINGS_FILE) as f: return f.read()

def write_setting(text, key, value):
    if isinstance(value, bool): val_str = "True" if value else "False"
    else: val_str = str(value)
    new_text = re.sub(rf'^{key}\s*=\s*.*$', f'{key} = {val_str}', text, flags=re.MULTILINE)
    return new_text

def apply_settings(params, balance):
    text = read_settings()
    for k, v in FIXED.items(): text = write_setting(text, k, v)
    for k, v in params.items():
        if k not in ('label', 'desc'): text = write_setting(text, k, v)
    text = write_setting(text, "INITIAL_BALANCE", balance)
    with open(SETTINGS_FILE, 'w') as f: f.write(text)

def run_backtest(month, balance):
    apply_settings({}, balance)  # just update balance
    cmd = [sys.executable, "bot_runner.py", f"--csv={month}", "--max-rows=0",
           "--tf=5min", "--throttle=0", "--timeout=300"]
    try:
        subprocess.run(cmd, capture_output=True, text=True, timeout=330)
    except subprocess.TimeoutExpired:
        return None
    
    result_files = sorted(glob.glob(os.path.join(RESULTS_DIR, "result_*.json")), reverse=True)
    if not result_files: return None
    with open(result_files[0]) as f: data = json.load(f)
    if data.get("status", "").lower() != "complete": return None
    return data

def get_peak_notional(trades_file):
    """Calculate peak notional from trade CSV."""
    if not os.path.exists(trades_file): return 0, 0
    events = []
    with open(trades_file) as f:
        for row in csv.DictReader(f):
            lots = float(row['lots'])
            price = float(row['entry_price'])
            n = lots * price
            events.append(('o', row['entry_time'], n, lots))
            events.append(('c', row['exit_time'], -n, -lots))
    events.sort(key=lambda x: x[1])
    mx_n = mx_l = rn = rl = 0
    for _, _, n, l in events:
        rn += n; rl += l
        if rn > mx_n: mx_n = rn; mx_l = rl
    return mx_n, mx_l

def main():
    global SETTINGS_BACKUP
    SETTINGS_BACKUP = read_settings()
    
    print("=" * 80)
    print("  6-MONTH VALIDATION — OPTIMIZER WINNERS")
    print(f"  {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print("=" * 80)
    
    all_results = {}
    
    for w in WINNERS:
        label = w['label']
        print(f"\n{'='*80}")
        print(f"  {label}: {w['desc']}")
        print(f"{'='*80}")
        
        # Apply winner settings
        text = read_settings()
        for k, v in FIXED.items(): text = write_setting(text, k, v)
        for k, v in w.items():
            if k not in ('label', 'desc'): text = write_setting(text, k, v)
        with open(SETTINGS_FILE, 'w') as f: f.write(text)
        
        equity = START_BALANCE
        monthly = []
        total_trades = 0
        worst_dd = 0
        peak_notional = 0
        peak_lots = 0
        
        for month in MONTHS:
            t0 = time.time()
            
            # Set balance and run
            text = read_settings()
            text = write_setting(text, "INITIAL_BALANCE", equity)
            with open(SETTINGS_FILE, 'w') as f: f.write(text)
            
            cmd = [sys.executable, "bot_runner.py", f"--csv={month}", "--max-rows=0",
                   "--tf=5min", "--throttle=0", "--timeout=300"]
            try:
                subprocess.run(cmd, capture_output=True, text=True, timeout=330)
            except subprocess.TimeoutExpired:
                print(f"  {month}: TIMEOUT")
                monthly.append({"month": month, "error": "timeout"})
                continue
            
            result_files = sorted(glob.glob(os.path.join(RESULTS_DIR, "result_*.json")), reverse=True)
            if not result_files:
                print(f"  {month}: NO RESULT FILE")
                continue
            
            with open(result_files[0]) as f: data = json.load(f)
            elapsed = time.time() - t0
            
            if data.get("status", "").lower() != "complete":
                print(f"  {month}: FAILED ({data.get('status')})")
                continue
            
            # Get peak notional from trades file
            trades_files = sorted(glob.glob(os.path.join(RESULTS_DIR, "trades_*.csv")), reverse=True)
            mn, ml = get_peak_notional(trades_files[0]) if trades_files else (0, 0)
            
            new_equity = equity + data["net_profit"]
            dd = data["max_dd_pct"]
            worst_dd = max(worst_dd, dd)
            total_trades += data["total_trades"]
            peak_notional = max(peak_notional, mn)
            peak_lots = max(peak_lots, ml)
            
            m = {
                "month": month, "start": equity, "end": new_equity,
                "net": data["net_profit"], "return_pct": data["return_pct"],
                "dd": dd, "pf": data["profit_factor"], "trades": data["total_trades"],
                "peak_notional": mn, "peak_lots": ml,
            }
            monthly.append(m)
            
            month_label = f"20{month[2:4]}-{month[4:6]}"
            print(f"  {month_label}: ${equity:>12,.0f} → ${new_equity:>12,.0f} ({data['return_pct']:>+7.1f}%) "
                  f"DD:{dd:.1f}% PF:{data['profit_factor']:.2f} "
                  f"Peak:${mn:>10,.0f} [{elapsed:.0f}s]")
            
            equity = new_equity
        
        # Summary
        total_return = (equity / START_BALANCE - 1) * 100
        
        # Margin analysis
        for acct in [15000, 20000, 25000, 30000, 40000, 50000]:
            margin_pct = peak_notional / (acct * 50) * 100
            if margin_pct <= 85:
                min_acct = acct
                break
        else:
            min_acct = int(peak_notional / 50 / 0.85)
        
        print(f"\n  SUMMARY {label}:")
        print(f"  ${START_BALANCE:,.0f} → ${equity:,.0f} (+{total_return:,.0f}%)")
        print(f"  Worst DD: {worst_dd:.1f}% | Trades: {total_trades:,}")
        print(f"  Peak notional: ${peak_notional:,.0f} ({peak_lots:.1f} lots)")
        print(f"  Min account @50x: ${min_acct:,} ({peak_notional/(min_acct*50)*100:.0f}%)")
        
        all_results[label] = {
            "desc": w['desc'], "final": equity, "return_pct": total_return,
            "worst_dd": worst_dd, "total_trades": total_trades,
            "peak_notional": peak_notional, "peak_lots": peak_lots,
            "min_account": min_acct, "monthly": monthly,
            "lot": w['LOT_SIZE'], "ttp": w['THREAD_PROFIT_TARGET'],
            "mo": w['MAX_INITIAL_ORDERS'], "sp": w['MIN_ENTRY_SPACING'],
            "rlm": w['PROGRESSIVE_RLM_ENABLED'],
        }
        
        with open(PROGRESS_FILE, 'w') as f:
            json.dump({"status": "RUNNING", "completed": label, "results": all_results,
                       "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S')}, f, indent=2)
    
    # Restore settings
    with open(SETTINGS_FILE, 'w') as f: f.write(SETTINGS_BACKUP)
    print("\n✅ Settings restored")
    
    # Final comparison
    print("\n" + "=" * 100)
    print("  FINAL COMPARISON — ALL WINNERS")
    print("=" * 100)
    print(f"{'Label':<5} {'Config':<25} {'Final':>12} {'Return':>9} {'DD':>6} {'Trades':>7} {'Peak$':>12} {'MinAcct':>10}")
    print("-" * 100)
    for label, r in all_results.items():
        print(f"{label:<5} {r['desc']:<25} ${r['final']:>11,.0f} {r['return_pct']:>+8,.0f}% {r['worst_dd']:>5.1f}% "
              f"{r['total_trades']:>7,} ${r['peak_notional']:>11,.0f} ${r['min_account']:>9,}")
    
    # Save final
    with open(PROGRESS_FILE, 'w') as f:
        json.dump({"status": "COMPLETE", "results": all_results,
                   "timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S')}, f, indent=2)
    
    print(f"\n✅ Done! Results in {PROGRESS_FILE}")

if __name__ == "__main__":
    def cleanup(signum, frame):
        if SETTINGS_BACKUP:
            with open(SETTINGS_FILE, 'w') as f: f.write(SETTINGS_BACKUP)
        sys.exit(1)
    signal.signal(signal.SIGTERM, cleanup)
    signal.signal(signal.SIGINT, cleanup)
    main()
