"""
V1 vs V2 Backtest — Replays V1's submitted trades through V2's strategy engine
to compare performance. Uses OHLC data to determine actual outcomes.
"""
import json
import time
import sys
from pathlib import Path
from collections import defaultdict

sys.path.insert(0, str(Path(__file__).parent.parent))
from hlbot.api import HLClient
from hlbot.orderflow import compute_rsi, compute_macd, compute_atr, compute_vwap
from hlbot.models import Candle


def load_v1_trades(path: str) -> list[dict]:
    """Load V1 journal entries."""
    trades = []
    with open(path) as f:
        for line in f:
            try:
                entry = json.loads(line.strip())
                if entry.get("event") == "submit":
                    trades.append(entry)
            except:
                continue
    return trades


def fetch_candles(client: HLClient, coin: str, start_ms: int, end_ms: int) -> list[Candle]:
    """Fetch 5m candles for a time range."""
    raw = client.candles(coin, "5m", start_ms, min(end_ms, start_ms + 200 * 300_000))
    time.sleep(0.3)
    if not raw:
        return []
    return [Candle.from_hl(c) for c in raw]


def simulate_trade(candles: list[Candle], side: str, entry: float, stop: float, target: float) -> dict:
    """Simulate a trade against OHLC candles."""
    mfe = 0.0
    mae = 0.0
    exit_price = None
    exit_reason = None

    for c in candles:
        if side == "long":
            fav = (c.high - entry) / entry * 100
            adv = (c.low - entry) / entry * 100
            mfe = max(mfe, fav)
            mae = min(mae, adv)
            if exit_price is None and c.low <= stop:
                exit_price = stop
                exit_reason = "STOP"
            if exit_price is None and c.high >= target:
                exit_price = target
                exit_reason = "TP"
        else:
            fav = (entry - c.low) / entry * 100
            adv = (entry - c.high) / entry * 100
            mfe = max(mfe, fav)
            mae = min(mae, adv)
            if exit_price is None and c.high >= stop:
                exit_price = stop
                exit_reason = "STOP"
            if exit_price is None and c.low <= target:
                exit_price = target
                exit_reason = "TP"

    if exit_price is None:
        exit_reason = "OPEN"

    return {
        "exit": exit_reason,
        "mfe_pct": mfe,
        "mae_pct": mae,
        "exit_price": exit_price,
    }


def would_v2_filter(trade: dict, candles_before: list[Candle]) -> str | None:
    """
    Check if V2's strategy engine would have rejected this trade.
    Returns reject reason or None (would have traded).
    """
    if not candles_before or len(candles_before) < 15:
        return None

    closes = [c.close for c in candles_before]
    rsi = compute_rsi(closes)
    macd = compute_macd(closes)
    atr = compute_atr(candles_before)
    vwap = compute_vwap(candles_before)
    mid = closes[-1]

    side = trade.get("side", "long")
    entry = trade.get("entry", 0)
    stop = trade.get("stop", 0)
    risk = abs(entry - stop)

    # V2 Risk Layer 4: noise floor
    if atr > 0 and risk < atr * 0.8:
        return f"V2:stop_in_noise(atr={atr:.4f},risk={risk:.4f})"

    # V2 Signal alignment check
    price_vs_vwap = (mid - vwap) / vwap if vwap else 0

    if side == "long":
        # V2 requires: RSI 35-65, MACD >= 0, near/above VWAP
        if rsi < 30 or rsi > 70:
            return f"V2:rsi_exhausted({rsi:.0f})"
        if price_vs_vwap < -0.01 and macd < 0:
            return f"V2:long_against_trend(vwap={price_vs_vwap:+.3f},macd={macd:.4f})"
    else:
        if rsi < 30 or rsi > 70:
            return f"V2:rsi_exhausted({rsi:.0f})"
        if price_vs_vwap > 0.01 and macd > 0:
            return f"V2:short_against_trend(vwap={price_vs_vwap:+.3f},macd={macd:.4f})"

    return None


def main():
    v1_path = "/root/hyperliquid-bb-bot/data/signal-deck-live.jsonl"
    if not Path(v1_path).exists():
        print(f"V1 journal not found: {v1_path}")
        return

    print("Loading V1 trades...")
    v1_trades = load_v1_trades(v1_path)
    print(f"Loaded {len(v1_trades)} V1 submitted trades\n")

    client = HLClient()
    fee_bps = 4.5  # taker fee

    v1_results = []
    v2_results = []  # trades V2 would have taken (passed filter)
    v2_filtered = []  # trades V2 would have rejected

    for i, trade in enumerate(v1_trades):
        coin = trade.get("coin", "?")
        side = trade.get("side", "?")
        entry = trade.get("entry", 0)
        stop = trade.get("stop", 0)
        target = trade.get("target", 0)
        ts_ms = trade.get("timestamp_ms", 0)

        if not all([coin, entry, stop, target, ts_ms]):
            continue

        # Fetch candles from trade time
        now_ms = int(time.time() * 1000)
        candles_after = fetch_candles(client, coin, ts_ms, min(ts_ms + 200 * 300_000, now_ms))

        # Fetch candles BEFORE trade for indicator check
        candles_before = fetch_candles(client, coin, ts_ms - 3600_000, ts_ms)

        # Simulate V1 trade
        v1_sim = simulate_trade(candles_after, side, entry, stop, target)

        # Check if V2 would filter
        v2_filter = would_v2_filter(trade, candles_before)

        # Calculate PnL
        margin = trade.get("margin_usd", 30)
        leverage = trade.get("leverage", 20)
        notional = margin * leverage

        if v1_sim["exit"] == "TP":
            pnl_pct = abs(target - entry) / entry
            v1_pnl = notional * pnl_pct - notional * fee_bps / 10000 * 2  # entry + exit
        elif v1_sim["exit"] == "STOP":
            pnl_pct = abs(entry - stop) / entry
            v1_pnl = -notional * pnl_pct - notional * fee_bps / 10000 * 2
        else:
            v1_pnl = 0  # open trade

        result = {
            "coin": coin,
            "side": side,
            "entry": entry,
            "stop": stop,
            "target": target,
            "ts": trade.get("timestamp", "?")[:16],
            "v1_exit": v1_sim["exit"],
            "v1_pnl": v1_pnl,
            "v1_mfe": v1_sim["mfe_pct"],
            "v1_mae": v1_sim["mae_pct"],
            "v2_filter": v2_filter,
        }

        v1_results.append(result)

        if v2_filter:
            v2_filtered.append(result)
        else:
            v2_results.append(result)

        if (i + 1) % 10 == 0:
            print(f"  Processed {i + 1}/{len(v1_trades)} trades...")

    # === RESULTS ===
    print("\n" + "=" * 100)
    print("V1 vs V2 BACKTEST RESULTS")
    print("=" * 100)

    # V1 Summary
    v1_closed = [r for r in v1_results if r["v1_exit"] in ("TP", "STOP")]
    v1_tp = [r for r in v1_results if r["v1_exit"] == "TP"]
    v1_sl = [r for r in v1_results if r["v1_exit"] == "STOP"]
    v1_total_pnl = sum(r["v1_pnl"] for r in v1_results)
    v1_fees = sum(abs(r["v1_pnl"]) * 0 if r["v1_exit"] == "OPEN" else (30 * 20 * 4.5 / 10000 * 2) for r in v1_closed)

    print(f"\nV1 (Original Bot):")
    print(f"  Trades:     {len(v1_results)} ({len(v1_closed)} closed)")
    print(f"  TP hits:    {len(v1_tp)}")
    print(f"  SL hits:    {len(v1_sl)}")
    print(f"  Win Rate:   {len(v1_tp)/len(v1_closed)*100:.1f}%" if v1_closed else "  Win Rate: N/A")
    print(f"  Gross PnL:  ${v1_total_pnl:+.2f}")
    print(f"  Fees:       ${v1_fees:.2f}")
    print(f"  Net PnL:    ${v1_total_pnl - v1_fees:+.2f}")

    # V2 Summary (filtered trades only)
    v2_closed = [r for r in v2_results if r["v1_exit"] in ("TP", "STOP")]
    v2_tp = [r for r in v2_results if r["v1_exit"] == "TP"]
    v2_sl = [r for r in v2_results if r["v1_exit"] == "STOP"]
    v2_total_pnl = sum(r["v1_pnl"] for r in v2_results)
    v2_fees = sum((30 * 20 * 4.5 / 10000 * 2) for r in v2_closed)

    print(f"\nV2 (After Strategy Filter):")
    print(f"  Trades:     {len(v2_results)} of {len(v1_results)} ({len(v2_filtered)} filtered out)")
    print(f"  TP hits:    {len(v2_tp)}")
    print(f"  SL hits:    {len(v2_sl)}")
    print(f"  Win Rate:   {len(v2_tp)/len(v2_closed)*100:.1f}%" if v2_closed else "  Win Rate: N/A")
    print(f"  Gross PnL:  ${v2_total_pnl:+.2f}")
    print(f"  Fees:       ${v2_fees:.2f}")
    print(f"  Net PnL:    ${v2_total_pnl - v2_fees:+.2f}")

    # Savings from filtering
    filtered_pnl = sum(r["v1_pnl"] for r in v2_filtered)
    saved_fees = sum((30 * 20 * 4.5 / 10000 * 2) for r in v2_filtered if r["v1_exit"] in ("TP", "STOP"))

    print(f"\nV2 Filter Impact:")
    print(f"  Trades avoided:  {len(v2_filtered)}")
    print(f"  Losses avoided:  ${-filtered_pnl:.2f}" if filtered_pnl < 0 else f"  Profit lost: ${filtered_pnl:.2f}")
    print(f"  Fees saved:      ${saved_fees:.2f}")

    # Per-coin breakdown
    print(f"\nPer-Coin Breakdown:")
    print(f"  {'COIN':6} | {'V1 Trades':>10} | {'V1 PnL':>10} | {'V2 Trades':>10} | {'V2 PnL':>10} | {'Filtered':>8}")
    coins = set(r["coin"] for r in v1_results)
    for coin in sorted(coins):
        v1c = [r for r in v1_results if r["coin"] == coin]
        v2c = [r for r in v2_results if r["coin"] == coin]
        filt = [r for r in v2_filtered if r["coin"] == coin]
        v1p = sum(r["v1_pnl"] for r in v1c)
        v2p = sum(r["v1_pnl"] for r in v2c)
        print(f"  {coin:6} | {len(v1c):>10} | ${v1p:>+8.2f} | {len(v2c):>10} | ${v2p:>+8.2f} | {len(filt):>8}")

    # Filter reasons breakdown
    if v2_filtered:
        print(f"\nV2 Filter Reasons:")
        reasons = defaultdict(int)
        for r in v2_filtered:
            if r["v2_filter"]:
                key = r["v2_filter"].split("(")[0]
                reasons[key] += 1
        for reason, count in sorted(reasons.items(), key=lambda x: -x[1]):
            print(f"  {count:3}x {reason}")

    # Individual trade log
    print(f"\n{'='*100}")
    print("DETAILED TRADE LOG")
    print(f"{'='*100}")
    print(f"  {'TIME':16} | {'COIN':5} | {'SIDE':5} | {'EXIT':5} | {'PnL':>8} | {'MFE':>7} | {'MAE':>7} | V2")
    for r in v1_results:
        v2_tag = "PASS" if not r["v2_filter"] else f"SKIP({r['v2_filter'][:30]})"
        print(
            f"  {r['ts']} | {r['coin']:5} | {r['side']:5} | {r['v1_exit']:5} | "
            f"${r['v1_pnl']:>+6.2f} | {r['v1_mfe']:>+5.2f}% | {r['v1_mae']:>+5.2f}% | {v2_tag}"
        )


if __name__ == "__main__":
    main()
