"""
Deep Limit Order Book Strategy.

Analyzes the full orderbook beyond top-of-book to detect:
1. **Wall detection** — large resting orders that indicate support/resistance
2. **Depth imbalance** — ratio of cumulative depth on bid vs ask side
3. **Orderbook skew** — asymmetry in volume distribution across price levels
4. **Thin-side prediction** — when one side of the book is thin, price likely moves that way

When a large wall sits at a certain price level, the market is unlikely to
break through it quickly. When the ask side is thin relative to the bid side,
buying pressure is likely to push price up.

Usage:
    from strategies.deep_lob import DeepLOB
    model = DeepLOB()
    signal = model.analyze(orderbook_bids, orderbook_asks, mark_price)
"""
import math
from collections import deque

class DeepLOB:
    """Analyze full orderbook depth for trade signals."""

    def __init__(self, depth_levels: int = 10):
        self.depth_levels = depth_levels
        self.signal_history: deque = deque(maxlen=50)

    def analyze(self, bids: list, asks: list, mark_price: float) -> dict:
        """Analyze orderbook and return a signal.
        
        Args:
            bids: list of [price, size] pairs sorted best→worst (descending)
            asks: list of [price, size] pairs sorted best→worst (ascending)
            mark_price: current mark price

        Returns:
            dict with signal, strength, and metrics
        """
        if not bids or not asks or mark_price <= 0:
            return {"signal": None, "strength": 0.0}

        # 1. Cumulative depth on each side
        bid_depth = sum(sz for _, sz in bids[:self.depth_levels])
        ask_depth = sum(sz for _, sz in asks[:self.depth_levels])

        # 2. Wall detection — find largest single order on each side
        bid_walls = sorted([(px, sz) for px, sz in bids[:self.depth_levels]], key=lambda x: x[1], reverse=True)
        ask_walls = sorted([(px, sz) for px, sz in asks[:self.depth_levels]], key=lambda x: x[1], reverse=True)

        largest_bid_wall = bid_walls[0] if bid_walls else (0, 0)
        largest_ask_wall = ask_walls[0] if ask_walls else (0, 0)

        # 3. Wall-to-depth ratio — how concentrated is the orderbook?
        bid_concentration = largest_bid_wall[1] / bid_depth if bid_depth > 0 else 0
        ask_concentration = largest_ask_wall[1] / ask_depth if ask_depth > 0 else 0

        # 4. Depth-weighted mid price (more accurate than top-of-book mid)
        # Weight prices by their size to get "fair value"
        bid_weighted = sum(px * sz for px, sz in bids[:self.depth_levels]) / bid_depth if bid_depth > 0 else 0
        ask_weighted = sum(px * sz for px, sz in asks[:self.depth_levels]) / ask_depth if ask_depth > 0 else 0
        fair_price = (bid_weighted + ask_weighted) / 2 if bid_weighted > 0 and ask_weighted > 0 else mark_price

        # 5. Depth imbalance ratio
        total_depth = bid_depth + ask_depth
        depth_imbalance = (bid_depth - ask_depth) / total_depth if total_depth > 0 else 0

        # 6. Thin-side detection
        # If one side is much thinner, price likely moves that way
        depth_ratio = bid_depth / ask_depth if ask_depth > 0 else 999
        thin_side = None
        if depth_ratio > 3.0:
            thin_side = "ask"  # Ask side thin → buyers will push through → bullish
        elif depth_ratio < 0.33:
            thin_side = "bid"  # Bid side thin → sellers will push through → bearish

        # 7. Mark price vs fair value
        fv_deviation = (mark_price - fair_price) / fair_price if fair_price > 0 else 0

        # 8. Volume-weighted average spread
        vwap_spread = 0
        for i in range(min(len(bids), len(asks), 5)):
            spread_i = asks[i][0] - bids[i][0]
            vwap_spread += spread_i
        avg_spread = vwap_spread / min(len(bids), len(asks), 5) if bids and asks else 0

        # ═══════════════ Signal Generation ═══════════════

        signal = None
        strength = 0.0
        reasons = []

        # Wall imbalance signal
        if largest_bid_wall[1] > 2 * largest_ask_wall[1]:
            strength += 0.3
            reasons.append("large_bid_wall")
        elif largest_ask_wall[1] > 2 * largest_bid_wall[1]:
            strength -= 0.3
            reasons.append("large_ask_wall")

        # Depth imbalance signal
        if depth_imbalance > 0.4:
            strength += 0.25
            reasons.append("bid_depth_dominant")
        elif depth_imbalance < -0.4:
            strength -= 0.25
            reasons.append("ask_depth_dominant")

        # Thin-side prediction
        if thin_side == "ask":
            strength += 0.35
            reasons.append("thin_ask_pressure")
        elif thin_side == "bid":
            strength -= 0.35
            reasons.append("thin_bid_pressure")

        # Fair value deviation
        if fv_deviation > 0.002:
            strength -= 0.15  # Overpriced relative to weighted depth
            reasons.append("overvalued")
        elif fv_deviation < -0.002:
            strength += 0.15  # Underpriced
            reasons.append("undervalued")

        # Determine final signal
        threshold = 0.3
        if strength > threshold:
            signal = "BUY"
        elif strength < -threshold:
            signal = "SELL"

        self.signal_history.append({
            "signal": signal,
            "strength": strength,
            "depth_imbalance": round(depth_imbalance, 4),
            "thin_side": thin_side,
        })

        return {
            "signal": signal,
            "strength": round(abs(strength) if signal else strength, 4),
            "fair_price": round(fair_price, 1),
            "depth_imbalance": round(depth_imbalance, 4),
            "depth_ratio": round(depth_ratio, 2),
            "thin_side": thin_side,
            "bid_concentration": round(bid_concentration, 3),
            "ask_concentration": round(ask_concentration, 3),
            "avg_spread": round(avg_spread, 1),
            "largest_bid_wall": round(largest_bid_wall[1], 2),
            "largest_ask_wall": round(largest_ask_wall[1], 2),
            "reasons": reasons,
        }
