"""
PolyEdge — Signal Generation

Combines fair value model with filters to produce actionable signals.
"""

import math
from typing import List, Optional, Dict, Any
from fair_value import (
    realized_vol, momentum_drift, calc_fair_value,
    calc_edge, mean_reversion_adjustment,
)
from config import (
    VOL_LOOKBACK_BARS, VOL_EWMA_SPAN, MOMENTUM_LOOKBACK, MOMENTUM_WEIGHT,
    MIN_EDGE, STRONG_EDGE, MAX_MARKET_PRICE, MIN_MARKET_PRICE,
)


class SignalEngine:
    """
    Generates trading signals based on fair value mispricing.
    
    Flow:
    1. Calculate realized vol from recent returns
    2. Estimate momentum drift
    3. Price the binary option (fair value)
    4. Compare to simulated market price
    5. Apply filters (min edge, price bounds, regime)
    6. Output signal with edge magnitude
    """
    
    def __init__(self):
        self.prices: List[float] = []
        self.returns: List[float] = []
        self.signals_generated = 0
        self.vol_history: List[float] = []
    
    def update(self, close_price: float):
        """Feed a new M5 candle close price."""
        if len(self.prices) > 0:
            log_ret = math.log(close_price / self.prices[-1])
            self.returns.append(log_ret)
        self.prices.append(close_price)
        
        # Keep bounded history
        max_history = max(VOL_LOOKBACK_BARS, MOMENTUM_LOOKBACK) + 50
        if len(self.prices) > max_history:
            self.prices = self.prices[-max_history:]
        if len(self.returns) > max_history:
            self.returns = self.returns[-max_history:]
    
    def get_vol(self) -> float:
        """Get current realized vol estimate."""
        if len(self.returns) < VOL_LOOKBACK_BARS:
            return 0.50  # Default
        recent_returns = self.returns[-VOL_LOOKBACK_BARS:]
        vol = realized_vol(recent_returns, VOL_EWMA_SPAN)
        self.vol_history.append(vol)
        return vol
    
    def get_drift(self) -> float:
        """Get current momentum drift estimate."""
        return momentum_drift(self.prices, MOMENTUM_LOOKBACK)
    
    def generate_signal(
        self,
        current_price: float,
        strike: float,
        market_yes_price: float,
        time_remaining_min: float = 5.0,
    ) -> Optional[Dict[str, Any]]:
        """
        Generate a trading signal if mispricing is detected.
        
        Args:
            current_price: Current BTC spot price
            strike: Binary option strike price
            market_yes_price: Current YES price on Polymarket (0-1)
            time_remaining_min: Minutes until market resolves
        
        Returns:
            Signal dict or None if no trade
        """
        # Need enough data
        if len(self.returns) < VOL_LOOKBACK_BARS:
            return None
        
        # Get model inputs
        vol = self.get_vol()
        drift = self.get_drift()
        
        # Price the binary
        fv = calc_fair_value(
            current_price=current_price,
            strike=strike,
            vol=vol,
            time_to_expiry_min=time_remaining_min,
            drift=drift,
            momentum_weight=MOMENTUM_WEIGHT,
        )
        
        # Calculate edge
        direction, edge = calc_edge(fv, market_yes_price)
        
        # Mean reversion bonus
        mr_bonus = mean_reversion_adjustment(market_yes_price)
        if direction == "buy_no" and market_yes_price > 0.85:
            edge += mr_bonus
        elif direction == "buy_yes" and market_yes_price < 0.15:
            edge += mr_bonus
        
        # Apply filters
        if edge < MIN_EDGE:
            return None
        
        if direction == "buy_yes" and market_yes_price > MAX_MARKET_PRICE:
            return None
        
        if direction == "buy_no" and market_yes_price < MIN_MARKET_PRICE:
            return None
        
        # Determine signal strength
        strength = "strong" if edge >= STRONG_EDGE else "normal"
        
        self.signals_generated += 1
        
        return {
            "direction": direction,
            "edge": edge,
            "strength": strength,
            "fair_value": fv,
            "market_price": market_yes_price,
            "vol": vol,
            "drift": drift,
            "current_btc": current_price,
            "strike": strike,
            "time_remaining": time_remaining_min,
        }
    
    def get_state(self) -> Dict[str, Any]:
        """Get current engine state for debugging."""
        return {
            "bars_loaded": len(self.prices),
            "current_price": self.prices[-1] if self.prices else 0,
            "vol": self.vol_history[-1] if self.vol_history else 0,
            "signals_generated": self.signals_generated,
        }
