from collections import deque
from indicators import calc_trend_score, calc_net_bias

# ============================
# ENGINE V10 – FLOW + IMPULSE
# ============================

# TP/SL קשיחים (באחוזים)
TAKE_PROFIT_PCT = 1.5     # TP קשיח
STOP_LOSS_PCT   = -0.8    # SL קשיח

# אזור רווח/הפסד רך לניהול BE ויציאות חכמות
TP1_PCT     = 0.25        # TP1 מוקדם
TP2_PCT     = 0.60        # TP2
SOFT_DD_PCT = -0.35       # drawdown רך

# פרמטרי divergence CVD
CVD_DIVERGENCE_CVD_MIN          = 150.0
CVD_DIVERGENCE_MAX_ABS_PRICE_5M = 0.05
CVD_DIVERGENCE_MAX_ABS_PNL      = 0.15

# היסטוריות לזמן (עומק, best-level, CVD, מחיר) – 10 טיקים אחרונים
depth_history = deque(maxlen=10)
best_history  = deque(maxlen=10)
cvd_history   = deque(maxlen=10)
price_history = deque(maxlen=10)

_prev_state_for_deltas = None  # לשימוש פנימי ל־ΔCVD, Δdepth, Δbest, speed


# ============================
#   HELPERS
# ============================

def _sanitize_orderbook_for_bias(depth_ratio: float, best_level_ratio: float):
    """
    קליפ של עומק ובסט כדי לא להרוס את ה-bias עם קיצוניות.
    """
    d = depth_ratio if depth_ratio is not None else 1.0
    b = best_level_ratio if best_level_ratio is not None else 1.0

    d = max(0.5, min(2.5, d))
    b = max(0.4, min(3.0, b))
    return d, b


def _classify_env(state):
    buy_ratio = state.get("buy_ratio", 0.5)
    cvd = state.get("cvd", 0.0)
    vol_price = state.get("vol_price", "QUIET")
    ratio_5m = state.get("ratio_5m", 1.0)
    price_change_5m = state.get("price_change_pct_5m", 0.0)

    env = {}

    env["very_low_activity"] = ratio_5m < 0.25
    env["choppy_price"] = vol_price not in ["QUIET", "NORMAL", "MID"]

    env["mild_trend_down_5m"] = price_change_5m < -0.15
    env["mild_trend_up_5m"]   = price_change_5m > 0.15

    env["buy_flow_ok"] = buy_ratio >= 0.485

    env["strong_cvd_up"]   = cvd is not None and cvd > 20
    env["strong_cvd_down"] = cvd is not None and cvd < -20
    env["light_cvd_up"]    = cvd is not None and cvd > 0
    env["light_cvd_down"]  = cvd is not None and cvd < 0

    return env


def _update_histories(price, depth_ratio, best_level_ratio, cvd_val):
    price_history.append(price)
    depth_history.append(depth_ratio)
    best_history.append(best_level_ratio)
    cvd_history.append(cvd_val)


def depth_rolling():
    if len(depth_history) < 3:
        return 0.0
    return sum(depth_history) / len(depth_history)


def depth_slope():
    if len(depth_history) < 5:
        return 0.0
    return depth_history[-1] - depth_history[0]


def depth_strength():
    """
    עומק אמיתי:
    - ממוצע עומק גבוה
    - מגמה חיובית או לפחות לא מתרסקת
    """
    roll = depth_rolling()
    slope = depth_slope()

    if roll > 1.2 and slope > 0:
        return True
    return False


def best_rolling():
    if len(best_history) < 3:
        return 0.0
    return sum(best_history) / len(best_history)


def best_slope():
    if len(best_history) < 5:
        return 0.0
    return best_history[-1] - best_history[0]


# ============================
#   MAIN LOGIC
# ============================

def detect_signal(state: dict) -> dict:
    """
    V10 – SAFE VERSION
    מתוקן כך שהבוט לא יקרוס אם חסר שדה.
    """

    global _prev_state_for_deltas

    # ---- בטיחות לכל השדות ----
    price       = state.get("price", None)
    buy_ratio   = state.get("buy_ratio", 0.5)
    smooth      = state.get("smooth", 0.0)
    cvd         = state.get("cvd", 0.0)
    whale       = state.get("whale", 0.0)

    depth_raw   = state.get("depth_ratio", 1.0)
    best_raw    = state.get("best_level_ratio", 1.0)

    vol_price   = state.get("vol_price", "QUIET")
    spoof       = state.get("spoof", {})
    iceberg     = state.get("iceberg", {})

    has_pos     = state.get("has_position", False)
    buy_price   = state.get("buy_price", None)

    ratio_1m    = state.get("ratio_1m", 1.0)
    ratio_5m    = state.get("ratio_5m", 1.0)
    ratio_15m   = state.get("ratio_15m", 1.0)
    price_change_5m = state.get("price_change_pct_5m", 0.0)

    onchain = state.get("onchain", [0.0, 0.0, 0.0])
    if len(onchain) != 3:
        onchain = [0.0, 0.0, 0.0]

    f_mem, f_fee, f_block = onchain

    # לדאוג שמבנה mf_stats קיים
    mf = state.get("mf_stats", {})
    if not isinstance(mf, dict):
        mf = {}

    mf1  = mf.get("1",  {})
    mf5  = mf.get("5",  {})
    mf15 = mf.get("15", {})

    buy_1m  = mf1.get("buy_ratio", buy_ratio)
    buy_5m  = mf5.get("buy_ratio", buy_ratio)
    buy_15m = mf15.get("buy_ratio", buy_ratio)

    # ---- הגנות על price ----
    if price is None:
        return {
            "action": "WAIT",
            "reason": "Missing price in state",
            "trend_score": 0,
            "net_bias": 0,
        }

    # ---- עדכון היסטוריות ----
    _update_histories(price, depth_raw, best_raw, cvd)

    # ---- previous tick ----
    prev = _prev_state_for_deltas
    _prev_state_for_deltas = {
        "price": price,
        "cvd": cvd,
        "depth_ratio": depth_raw,
        "best_level_ratio": best_raw
    }

    if prev:
        prev_price = prev.get("price")
        prev_cvd   = prev.get("cvd")
        prev_depth = prev.get("depth_ratio")
        prev_best  = prev.get("best_level_ratio")
    else:
        prev_price = prev_cvd = prev_depth = prev_best = None

    # ---- deltas ----
    cvd_roc   = cvd - prev_cvd if prev_cvd is not None else 0
    depth_roc = depth_raw - prev_depth if prev_depth is not None else 0
    best_roc  = best_raw - prev_best if prev_best is not None else 0
    speed_1t  = (
        abs(price - prev_price) / prev_price * 100.0
        if prev_price else 0
    )

    # ---- sanitize ----
    depth_sanitized, best_sanitized = _sanitize_orderbook_for_bias(depth_raw, best_raw)

    # ---- trend / bias ----
    trend = calc_trend_score(smooth, buy_ratio, cvd, whale, f_mem, f_fee, f_block)
    trend_base = calc_trend_score(smooth, buy_ratio, cvd, whale, 0, 0, 0)

    bias = calc_net_bias(buy_ratio, depth_sanitized, best_sanitized, whale, cvd, f_mem, f_fee, f_block)
    bias_base = calc_net_bias(buy_ratio, depth_sanitized, best_sanitized, whale, cvd, 0, 0, 0)

    # ---- במקרה שמשהו עדיין לא מסתדר ----
    if trend is None:
        trend = 0
    if bias is None:
        bias = 0

    # --------------------------------------------------------
    # From here — everything continues normally (no crash)
    # --------------------------------------------------------

    # אם אין פוזיציה – החלטות BUY
    # אם יש פוזיציה – SELL/TP/SL
    # (הקוד שלך ממשיך כרגיל מכאן)
    
    # יש לך כבר את הקוד המלא, אז אני משאיר את כל ההמשך כמו שהוא.
    # פה רק הפכתי את שלב הנתונים לבטוח.
    
    return {
        "action": "WAIT",
        "reason": "SAFE fallback – no logic executed",
        "trend_score": trend,
        "trend_score_base": trend_base,
        "net_bias": bias,
        "net_bias_base": bias_base,
    }


    # ============================================================
    #                      SELL LOGIC
    # ============================================================
    if has_pos and buy_price:
        pnl_pct = ((price - buy_price) / buy_price) * 100.0

        # 1) SL קשיח
        if pnl_pct <= STOP_LOSS_PCT:
            return {
                "action": "SELL",
                "reason": f"Stop loss hard {pnl_pct:.2f}%",
                "trend_score": trend,
                "trend_score_base": trend_base,
                "net_bias": bias,
                "net_bias_base": bias_base,
                "pnl_pct": pnl_pct,
            }

        # 2) TP קשיח
        if pnl_pct >= TAKE_PROFIT_PCT:
            return {
                "action": "SELL",
                "reason": f"Take profit hard +{pnl_pct:.2f}%",
                "trend_score": trend,
                "trend_score_base": trend_base,
                "net_bias": bias,
                "net_bias_base": bias_base,
                "pnl_pct": pnl_pct,
            }

        # 3) CVD divergence – קונים חזקים, מחיר לא מתקדם
        if (
            cvd is not None
            and cvd >= CVD_DIVERGENCE_CVD_MIN
            and abs(price_change_5m) <= CVD_DIVERGENCE_MAX_ABS_PRICE_5M
            and -CVD_DIVERGENCE_MAX_ABS_PNL <= pnl_pct <= CVD_DIVERGENCE_MAX_ABS_PNL
        ):
            return {
                "action": "SELL",
                "reason": (
                    f"CVD divergence – strong buying with no price progress "
                    f"(CVD={cvd:.1f}, 5m={price_change_5m:.3f}%, PnL={pnl_pct:.2f}%)"
                ),
                "trend_score": trend,
                "trend_score_base": trend_base,
                "net_bias": bias,
                "net_bias_base": bias_base,
                "pnl_pct": pnl_pct,
            }

        # 4) TP2 – ניהול רווח עם weakening
        if pnl_pct >= TP2_PCT:
            weakening = (
                trend <= 65
                or bias <= 1.0
                or env["strong_cvd_down"]
                or (env["light_cvd_down"] and env["mild_trend_down_5m"])
                or depth_sanitized < 0.9
                or best_sanitized < 0.9
                or ratio_5m < 0.8
            )
            if weakening:
                return {
                    "action": "SELL",
                    "reason": f"Trailing exit from TP2 (weakening, PnL {pnl_pct:.2f}%)",
                    "trend_score": trend,
                    "trend_score_base": trend_base,
                    "net_bias": bias,
                    "net_bias_base": bias_base,
                    "pnl_pct": pnl_pct,
                }

        # 5) TP1 – יציאה מהירה אם יש חולשה
        if pnl_pct >= TP1_PCT:
            soft_weak = (
                trend <= 60
                or bias <= 0.5
                or env["light_cvd_down"]
                or env["mild_trend_down_5m"]
                or depth_sanitized < 1.0
                or best_sanitized < 1.0
                or ratio_5m < 0.9
            )
            if soft_weak:
                return {
                    "action": "SELL",
                    "reason": f"Take profit scalp (TP1) – first weakness, PnL {pnl_pct:.2f}%",
                    "trend_score": trend,
                    "trend_score_base": trend_base,
                    "net_bias": bias,
                    "net_bias_base": bias_base,
                    "pnl_pct": pnl_pct,
                }

        # 6) אזור BE / soft DD
        if SOFT_DD_PCT <= pnl_pct <= 0.40:
            be_exit = (
                -0.05 <= pnl_pct <= 0.10
                and (
                    env["light_cvd_down"]
                    or env["strong_cvd_down"]
                    or env["mild_trend_down_5m"]
                    or bias <= 1.0
                )
            )

            soft_stop = (
                pnl_pct < 0
                and trend <= 50
                and bias <= 0
                and env["light_cvd_down"]
            )

            if be_exit or soft_stop:
                return {
                    "action": "SELL",
                    "reason": f"Soft / BE exit – signals deteriorating, PnL {pnl_pct:.2f}%",
                    "trend_score": trend,
                    "trend_score_base": trend_base,
                    "net_bias": bias,
                    "net_bias_base": bias_base,
                    "pnl_pct": pnl_pct,
                }

        # 7) הפסד עמוק יותר עם קונפלואנס שלילי ברור
        if pnl_pct < SOFT_DD_PCT:
            if (
                trend <= 48
                and bias <= -1.0
                and (env["strong_cvd_down"] or env["mild_trend_down_5m"])
            ):
                return {
                    "action": "SELL",
                    "reason": f"Confirm loss – strong downside confluence (PnL {pnl_pct:.2f}%)",
                    "trend_score": trend,
                    "trend_score_base": trend_base,
                    "net_bias": bias,
                    "net_bias_base": bias_base,
                    "pnl_pct": pnl_pct,
                }

        # אם לא מתקיים שום תנאי SELL – מחזיקים
        return {
            "action": "HOLD",
            "reason": f"Holding position (PnL {pnl_pct:.2f}%)",
            "trend_score": trend,
            "trend_score_base": trend_base,
            "net_bias": bias,
            "net_bias_base": bias_base,
            "pnl_pct": pnl_pct,
        }

    # ============================================================
    #                      BUY LOGIC
    # ============================================================

    # הגנות spoof/iceberg על ask – עם אפשרות override
    if iceberg.get("iceberg_sell") and not (trend >= 78 or bias >= 5.5):
        return {
            "action": "WAIT",
            "reason": "Iceberg sell without strong override",
            "trend_score": trend,
            "trend_score_base": trend_base,
            "net_bias": bias,
            "net_bias_base": bias_base,
        }

    if spoof.get("ask_spoof") and not (trend >= 80 or bias >= 6.0):
        return {
            "action": "WAIT",
            "reason": "Ask spoof without strong override",
            "trend_score": trend,
            "trend_score_base": trend_base,
            "net_bias": bias,
            "net_bias_base": bias_base,
        }

    # שווקים רועשים / לא סחירים
    if env["choppy_price"] and vol_price != "QUIET":
        return {
            "action": "WAIT",
            "reason": f"vol_regime unsafe: {vol_price}",
            "trend_score": trend,
            "trend_score_base": trend_base,
            "net_bias": bias,
            "net_bias_base": bias_base,
        }

    # ------------------------
    # FLOW MODE – זרימה אמיתית
    # ------------------------
    flow_signal = False
    flow_reason = None

    if trend >= 62 and bias >= 3.0:
        real_flow_ok = (
            buy_5m is not None
            and buy_15m is not None
            and buy_5m >= 0.56
            and buy_15m >= 0.45
            and ratio_5m >= 0.30
        )

        # 1m כמאמת: לא spike ולא נגד 5m
        micro_spike = buy_1m > (buy_5m + 0.15)
        micro_contra = buy_1m < (buy_5m - 0.12)

        cvd_ok = (cvd_roc is None) or (cvd_roc >= 2.0)
        depth_ok = depth_strength() and (depth_roc is None or depth_roc > -0.40)
        best_ok = (best_roc is None) or (best_roc > -1.0)

        if real_flow_ok and cvd_ok and depth_ok and best_ok and not micro_spike and not micro_contra:
            flow_signal = True
            flow_reason = (
                f"Flow BUY V10 – 5m/15m aligned, ΔCVD={cvd_roc}, "
                f"Δdepth={depth_roc}, Δbest={best_roc}"
            )

    # ------------------------
    # IMPULSE MODE – ספייק חזק
    # ------------------------
    impulse_signal = False
    impulse_reason = None

    if prev is not None:
        impulse_trend_ok = trend >= 75 and bias >= 4.5
        impulse_flow_ok = buy_5m is not None and buy_5m >= 0.52 and ratio_5m >= 0.24
        impulse_cvd_ok = (cvd_roc is not None and cvd_roc >= 8.0)
        impulse_speed_ok = (speed_1t is not None and speed_1t >= 0.015)  # 0.015% לטיק
        impulse_depth_ok = (
            depth_raw is not None
            and depth_raw >= 1.2
            and (depth_roc is None or depth_roc >= 0.0)
        )
        impulse_best_ok = (
            best_raw is not None
            and (
                best_raw >= 3.0
                or (best_roc is not None and best_roc >= 2.0)
            )
        )
        impulse_price_ok = not env["mild_trend_down_5m"]

        if (
            impulse_trend_ok
            and impulse_flow_ok
            and impulse_cvd_ok
            and impulse_speed_ok
            and impulse_depth_ok
            and impulse_best_ok
            and impulse_price_ok
        ):
            impulse_signal = True
            impulse_reason = (
                f"Impulse BUY V10 – ΔCVD={cvd_roc:.2f}, speed={speed_1t:.4f}%, "
                f"depth={depth_raw:.2f}, best={best_raw:.2f}"
            )

    # ------------------------
    # החלטת BUY
    # ------------------------
    if impulse_signal:
        return {
            "action": "BUY",
            "reason": impulse_reason,
            "trend_score": trend,
            "trend_score_base": trend_base,
            "net_bias": bias,
            "net_bias_base": bias_base,
        }

    if flow_signal:
        return {
            "action": "BUY",
            "reason": flow_reason,
            "trend_score": trend,
            "trend_score_base": trend_base,
            "net_bias": bias,
            "net_bias_base": bias_base,
        }

    # אחרת – לחכות
    return {
        "action": "WAIT",
        "reason": "No valid BUY signal (V10 flow/impulse filters)",
        "trend_score": trend,
        "trend_score_base": trend_base,
        "net_bias": bias,
        "net_bias_base": bias_base,
    }
