"""
Whale Intelligence - Advanced pattern recognition and market analysis

This module provides sophisticated intelligence capabilities including:
- Pattern recognition in whale behavior
- Market analysis and correlation detection
- Predictive modeling for whale movements
- Adaptive learning from historical data
"""

import logging
import numpy as np
from typing import Dict, List, Optional, Tuple, Any
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from collections import defaultdict, deque
import statistics

from ..core_types import (
    EnhancedTrade, WhaleBotConfig, WhalePattern, MarketImpactAnalysis as MarketImpact,
    PatternRecognitionCallback
)
from ..analysis import SizeClassifier
from ..analysis import MarketImpactAnalyzer

logger = logging.getLogger(__name__)

@dataclass
class PatternMetrics:
    """Metrics for a detected whale pattern"""
    pattern_id: str
    pattern_type: str
    confidence: float
    occurrence_count: int
    first_seen: datetime
    last_seen: datetime
    avg_size_usd: float
    success_rate: float = 0.0  # How often following this pattern was profitable
    total_profit_usd: float = 0.0
    avg_duration_minutes: float = 0.0

@dataclass
class MarketCorrelation:
    """Correlation analysis between different market elements"""
    element_a: str
    element_b: str
    correlation_type: str  # "price", "volume", "timing"
    correlation_strength: float  # 0.0 to 1.0
    sample_count: int
    confidence: float
    last_updated: datetime = field(default_factory=datetime.now)

class WhaleIntelligence:
    """
    Advanced intelligence system for whale behavior analysis.
    
    Provides:
    - Real-time pattern recognition
    - Behavioral clustering
    - Predictive modeling
    - Market correlation analysis
    - Performance tracking
    """
    
    def __init__(self, config: WhaleBotConfig):
        """
        Initialize the whale intelligence system.
        
        Args:
            config: Whale bot configuration with intelligence parameters
        """
        self.config = config
        self.detected_patterns: Dict[str, PatternMetrics] = {}
        self.market_correlations: List[MarketCorrelation] = []
        self.behavioral_clusters: Dict[str, List[EnhancedTrade]] = defaultdict(list)
        
        # Historical data storage
        self.trade_history: deque = deque(maxlen=10000)  # Last 10K trades
        self.pattern_examples: Dict[str, List[EnhancedTrade]] = defaultdict(list)
        
        # Performance tracking
        self.pattern_performance: Dict[str, Dict] = defaultdict(dict)
        self.prediction_accuracy: List[float] = []
        
        # Market analysis
        self.price_history: Dict[str, deque] = defaultdict(lambda: deque(maxlen=288))  # 24h at 5min intervals
        self.volume_history: Dict[str, deque] = defaultdict(lambda: deque(maxlen=288))
        self.coin_clusters: Dict[str, List[str]] = defaultdict(list)  # Related coins
        
        self._logger = logger
        
        # Initialize pattern recognition parameters
        self._pattern_confidence_threshold = config.pattern_confidence_threshold
        self._min_pattern_examples = config.min_pattern_examples
        
    def analyze_trade(self, trade: EnhancedTrade) -> Optional[WhalePattern]:
        """
        Analyze a trade for pattern recognition.
        
        Args:
            trade: Enhanced trade to analyze
            
        Returns:
            Optional[WhalePattern]: Detected pattern or None
        """
        try:
            # Add trade to history
            self.trade_history.append(trade)
            
            # Update market data
            self._update_market_data(trade)
            
            # Check for new patterns
            detected_pattern = self._detect_patterns(trade)
            
            if detected_pattern:
                self._logger.info(f"Detected {detected_pattern.pattern_type} pattern "
                                f"(confidence: {detected_pattern.confidence:.3f})")
                
                # Update pattern metrics
                self._update_pattern_metrics(detected_pattern)
                
                # Store example
                self.pattern_examples[detected_pattern.pattern_type].append(trade)
                
                # Keep pattern examples manageable
                if len(self.pattern_examples[detected_pattern.pattern_type]) > 100:
                    self.pattern_examples[detected_pattern.pattern_type] = \
                        self.pattern_examples[detected_pattern.pattern_type][-50:]
            
            return detected_pattern
            
        except Exception as e:
            self._logger.error(f"Error analyzing trade for patterns: {e}")
            return None
    
    def _update_market_data(self, trade: EnhancedTrade):
        """Update market data with new trade information"""
        try:
            coin = trade.coin
            
            # Update price history
            self.price_history[coin].append({
                'timestamp': trade.timestamp,
                'price': trade.price,
                'size': trade.size,
                'size_usd': trade.size_usd
            })
            
            # Update volume history
            self.volume_history[coin].append({
                'timestamp': trade.timestamp,
                'volume_usd': trade.size_usd,
                'trade_count': 1
            })
            
            # Simple coin clustering based on trade patterns
            if len(self.trade_history) > 10:
                self._update_coin_clusters(trade)
                
        except Exception as e:
            self._logger.debug(f"Error updating market data: {e}")
    
    def _detect_patterns(self, trade: EnhancedTrade) -> Optional[WhalePattern]:
        """
        Detect patterns in whale behavior.
        
        Args:
            trade: Recent trade to analyze
            
        Returns:
            Optional[WhalePattern]: Detected pattern or None
        """
        try:
            patterns_detected = []
            
            # Check for accumulation pattern
            accumulation = self._detect_accumulation_pattern(trade)
            if accumulation:
                patterns_detected.append(accumulation)
            
            # Check for distribution pattern
            distribution = self._detect_distribution_pattern(trade)
            if distribution:
                patterns_detected.append(distribution)
            
            # Check for manipulation pattern
            manipulation = self._detect_manipulation_pattern(trade)
            if manipulation:
                patterns_detected.append(manipulation)
            
            # Check for momentum pattern
            momentum = self._detect_momentum_pattern(trade)
            if momentum:
                patterns_detected.append(momentum)
            
            # Return the highest confidence pattern
            if patterns_detected:
                return max(patterns_detected, key=lambda p: p.confidence)
            
            return None
            
        except Exception as e:
            self._logger.debug(f"Error detecting patterns: {e}")
            return None
    
    def _detect_accumulation_pattern(self, trade: EnhancedTrade) -> Optional[WhalePattern]:
        """Detect accumulation pattern (buying over time)"""
        try:
            coin = trade.coin
            
            # Get recent trades for this coin
            recent_trades = [
                t for t in self.trade_history 
                if t.coin == coin and (datetime.now() - t.timestamp).total_seconds() < 3600  # 1 hour
            ]
            
            if len(recent_trades) < 5:
                return None
            
            # Look for consistent buying with increasing size
            buy_trades = [t for t in recent_trades if t.whale_direction == "LONG"]
            if len(buy_trades) < 3:
                return None
            
            # Check for increasing size pattern
            sizes = [t.size_usd for t in buy_trades[-5:]]  # Last 5 buy trades
            if len(sizes) < 3:
                return None
            
            # Check if sizes are generally increasing
            increasing_count = sum(1 for i in range(1, len(sizes)) if sizes[i] > sizes[i-1])
            increasing_ratio = increasing_count / (len(sizes) - 1)
            
            # Calculate confidence based on pattern strength
            if increasing_ratio > 0.7:  # 70% increasing
                confidence = 0.6 + (increasing_ratio - 0.7) * 0.4  # 0.6 to 1.0
                
                # Adjust for size - larger trades add confidence
                avg_size = sum(sizes) / len(sizes)
                size_confidence_boost = min(0.2, avg_size / 10_000_000)  # Up to 0.2 boost
                confidence = min(1.0, confidence + size_confidence_boost)
                
                # Create pattern
                pattern = WhalePattern(
                    pattern_type="accumulation",
                    confidence=confidence,
                    historical_examples=buy_trades[-10:],  # Last 10 examples
                    predicted_duration=120,  # 2 hours typical
                    strength=confidence
                )
                
                return pattern
            
            return None
            
        except Exception as e:
            self._logger.debug(f"Error detecting accumulation pattern: {e}")
            return None
    
    def _detect_distribution_pattern(self, trade: EnhancedTrade) -> Optional[WhalePattern]:
        """Detect distribution pattern (selling over time)"""
        try:
            coin = trade.coin
            
            # Get recent trades for this coin
            recent_trades = [
                t for t in self.trade_history 
                if t.coin == coin and (datetime.now() - t.timestamp).total_seconds() < 3600  # 1 hour
            ]
            
            if len(recent_trades) < 5:
                return None
            
            # Look for consistent selling with increasing size
            sell_trades = [t for t in recent_trades if t.whale_direction == "SHORT"]
            if len(sell_trades) < 3:
                return None
            
            # Check for increasing size pattern
            sizes = [t.size_usd for t in sell_trades[-5:]]  # Last 5 sell trades
            if len(sizes) < 3:
                return None
            
            # Check if sizes are generally increasing
            increasing_count = sum(1 for i in range(1, len(sizes)) if sizes[i] > sizes[i-1])
            increasing_ratio = increasing_count / (len(sizes) - 1)
            
            # Similar confidence calculation as accumulation
            if increasing_ratio > 0.7:
                confidence = 0.6 + (increasing_ratio - 0.7) * 0.4
                
                # Size-based confidence boost
                avg_size = sum(sizes) / len(sizes)
                size_confidence_boost = min(0.2, avg_size / 10_000_000)
                confidence = min(1.0, confidence + size_confidence_boost)
                
                pattern = WhalePattern(
                    pattern_type="distribution",
                    confidence=confidence,
                    historical_examples=sell_trades[-10:],
                    predicted_duration=120,  # 2 hours typical
                    strength=confidence
                )
                
                return pattern
            
            return None
            
        except Exception as e:
            self._logger.debug(f"Error detecting distribution pattern: {e}")
            return None
    
    def _detect_manipulation_pattern(self, trade: EnhancedTrade) -> Optional[WhalePattern]:
        """Detect manipulation pattern (large moves followed by reversals)"""
        try:
            coin = trade.coin
            
            # Look for large moves in short time
            recent_large_trades = [
                t for t in self.trade_history 
                if t.coin == coin 
                and t.size_usd > 5_000_000  # $5M+ trades
                and (datetime.now() - t.timestamp).total_seconds() < 1800  # 30 minutes
            ]
            
            if len(recent_large_trades) < 2:
                return None
            
            # Check for reversal pattern
            if len(recent_large_trades) >= 2:
                trade1, trade2 = recent_large_trades[-2:]
                
                # Large move followed by opposite direction
                if (trade1.whale_direction != trade2.whale_direction and
                    abs(trade1.size_usd - trade2.size_usd) / max(trade1.size_usd, trade2.size_usd) < 0.3):  # Similar sizes
                    
                    confidence = 0.5  # Base confidence
                    
                    # Boost confidence if trades are very recent
                    time_diff = (trade2.timestamp - trade1.timestamp).total_seconds()
                    if time_diff < 300:  # 5 minutes
                        confidence += 0.3
                    
                    # Boost for very large trades
                    if trade1.size_usd > 10_000_000:  # $10M+
                        confidence += 0.2
                    
                    confidence = min(1.0, confidence)
                    
                    pattern = WhalePattern(
                        pattern_type="manipulation",
                        confidence=confidence,
                        historical_examples=recent_large_trades[-5:],
                        predicted_duration=600,  # 10 minutes typical
                        strength=confidence
                    )
                    
                    return pattern
            
            return None
            
        except Exception as e:
            self._logger.debug(f"Error detecting manipulation pattern: {e}")
            return None
    
    def _detect_momentum_pattern(self, trade: EnhancedTrade) -> Optional[WhalePattern]:
        """Detect momentum pattern (trending in one direction)"""
        try:
            coin = trade.coin
            
            # Get recent trades in same direction
            recent_trades = [
                t for t in self.trade_history 
                if t.coin == coin 
                and t.whale_direction == trade.whale_direction
                and (datetime.now() - t.timestamp).total_seconds() < 900  # 15 minutes
            ]
            
            if len(recent_trades) < 3:
                return None
            
            # Check for consistent direction with increasing size
            sizes = [t.size_usd for t in recent_trades[-5:]]  # Last 5 trades
            direction = trade.whale_direction
            
            # Simple momentum detection: consistent direction
            direction_consistency = sum(1 for t in recent_trades if t.whale_direction == direction) / len(recent_trades)
            
            if direction_consistency > 0.8:  # 80% same direction
                confidence = 0.4 + (direction_consistency - 0.8) * 0.5  # 0.4 to 0.5+ boost
                
                # Add size momentum boost
                if len(sizes) >= 3:
                    size_trend = self._calculate_trend(sizes)
                    if size_trend > 0.1:  # Positive trend
                        confidence += 0.2
                
                confidence = min(1.0, confidence)
                
                pattern = WhalePattern(
                    pattern_type="momentum",
                    confidence=confidence,
                    historical_examples=recent_trades[-8:],
                    predicted_duration=300,  # 5 minutes typical
                    strength=confidence
                )
                
                return pattern
            
            return None
            
        except Exception as e:
            self._logger.debug(f"Error detecting momentum pattern: {e}")
            return None
    
    def _calculate_trend(self, values: List[float]) -> float:
        """Calculate trend direction in a list of values"""
        if len(values) < 2:
            return 0.0
        
        # Simple trend calculation
        increases = sum(1 for i in range(1, len(values)) if values[i] > values[i-1])
        decreases = sum(1 for i in range(1, len(values)) if values[i] < values[i-1])
        
        if increases + decreases == 0:
            return 0.0
        
        return (increases - decreases) / (increases + decreases)  # -1 to 1
    
    def _update_coin_clusters(self, trade: EnhancedTrade):
        """Update coin clusters based on trading patterns"""
        try:
            # Simple clustering based on simultaneous activity
            recent_time = trade.timestamp - timedelta(minutes=5)
            
            related_trades = [
                t for t in self.trade_history 
                if abs((t.timestamp - trade.timestamp).total_seconds()) < 300  # 5 minutes
                and t.coin != trade.coin
            ]
            
            if related_trades:
                for related_trade in related_trades:
                    # Add bidirectional relationships
                    if trade.coin not in self.coin_clusters[related_trade.coin]:
                        self.coin_clusters[related_trade.coin].append(trade.coin)
                    if related_trade.coin not in self.coin_clusters[trade.coin]:
                        self.coin_clusters[trade.coin].append(related_trade.coin)
                        
        except Exception as e:
            self._logger.debug(f"Error updating coin clusters: {e}")
    
    def _update_pattern_metrics(self, pattern: WhalePattern):
        """Update metrics for a detected pattern"""
        try:
            pattern_id = f"{pattern.pattern_type}_{len(self.pattern_examples[pattern.pattern_type])}"
            
            if pattern.pattern_type in self.detected_patterns:
                metrics = self.detected_patterns[pattern.pattern_type]
                metrics.occurrence_count += 1
                metrics.last_seen = datetime.now()
                
                # Update average size
                current_avg = metrics.avg_size_usd
                examples = self.pattern_examples[pattern.pattern_type]
                new_avg = ((current_avg * (metrics.occurrence_count - 1)) + 
                          sum(t.size_usd for t in examples[-10:]) / min(10, len(examples))) / metrics.occurrence_count
                metrics.avg_size_usd = new_avg
                
            else:
                # Create new metrics
                examples = self.pattern_examples[pattern.pattern_type]
                avg_size = sum(t.size_usd for t in examples) / len(examples)
                
                self.detected_patterns[pattern.pattern_type] = PatternMetrics(
                    pattern_id=pattern_id,
                    pattern_type=pattern.pattern_type,
                    confidence=pattern.confidence,
                    occurrence_count=1,
                    first_seen=datetime.now(),
                    last_seen=datetime.now(),
                    avg_size_usd=avg_size
                )
                
        except Exception as e:
            self._logger.debug(f"Error updating pattern metrics: {e}")
    
    def get_top_patterns(self, limit: int = 5) -> List[PatternMetrics]:
        """Get top performing patterns"""
        try:
            patterns = list(self.detected_patterns.values())
            
            # Sort by confidence and occurrence count
            patterns.sort(key=lambda p: (p.confidence, p.occurrence_count), reverse=True)
            
            return patterns[:limit]
            
        except Exception as e:
            self._logger.error(f"Error getting top patterns: {e}")
            return []
    
    def get_coin_correlations(self, coin: str) -> List[MarketCorrelation]:
        """Get correlations for a specific coin"""
        try:
            related_coins = self.coin_clusters.get(coin, [])
            correlations = []
            
            for related_coin in related_coins:
                correlation = MarketCorrelation(
                    element_a=coin,
                    element_b=related_coin,
                    correlation_type="trading_pattern",
                    correlation_strength=0.5,  # Placeholder
                    sample_count=10,  # Placeholder
                    confidence=0.3  # Placeholder
                )
                correlations.append(correlation)
            
            return correlations
            
        except Exception as e:
            self._logger.error(f"Error getting coin correlations: {e}")
            return []
    
    def analyze_market_sentiment(self, coin: str) -> Dict[str, float]:
        """Analyze market sentiment for a coin based on whale activity"""
        try:
            recent_trades = [
                t for t in self.trade_history 
                if t.coin == coin and (datetime.now() - t.timestamp).total_seconds() < 3600  # 1 hour
            ]
            
            if not recent_trades:
                return {"sentiment_score": 0.0, "confidence": 0.0}
            
            # Calculate sentiment based on direction and size
            long_volume = sum(t.size_usd for t in recent_trades if t.whale_direction == "LONG")
            short_volume = sum(t.size_usd for t in recent_trades if t.whale_direction == "SHORT")
            
            total_volume = long_volume + short_volume
            if total_volume == 0:
                sentiment_score = 0.0
            else:
                sentiment_score = (long_volume - short_volume) / total_volume
            
            # Confidence based on volume and consistency
            avg_trade_size = total_volume / len(recent_trades)
            consistency_factor = min(1.0, avg_trade_size / 1_000_000)  # $1M base
            confidence = min(1.0, consistency_factor * len(recent_trades) / 10)  # Scale by sample size
            
            return {
                "sentiment_score": max(-1.0, min(1.0, sentiment_score)),
                "confidence": confidence,
                "long_volume": long_volume,
                "short_volume": short_volume,
                "trade_count": len(recent_trades)
            }
            
        except Exception as e:
            self._logger.error(f"Error analyzing market sentiment: {e}")
            return {"sentiment_score": 0.0, "confidence": 0.0}
    
    def get_intelligence_summary(self) -> Dict[str, Any]:
        """Get comprehensive intelligence summary"""
        try:
            return {
                "patterns_detected": len(self.detected_patterns),
                "pattern_types": list(self.detected_patterns.keys()),
                "top_patterns": [
                    {
                        "type": p.pattern_type,
                        "confidence": f"{p.confidence:.3f}",
                        "occurrences": p.occurrence_count,
                        "avg_size": f"${p.avg_size_usd:,.0f}"
                    }
                    for p in self.get_top_patterns()
                ],
                "coin_clusters": {
                    coin: len(related) for coin, related in self.coin_clusters.items()
                },
                "recent_trades": len(self.trade_history),
                "prediction_accuracy": f"{np.mean(self.prediction_accuracy) if self.prediction_accuracy else 0.0:.3f}",
                "market_analysis": {
                    "coins_tracked": len(self.price_history),
                    "avg_price_updates": np.mean([len(h) for h in self.price_history.values()]) if self.price_history else 0.0
                }
            }
            
        except Exception as e:
            self._logger.error(f"Error generating intelligence summary: {e}")
            return {"error": str(e)}
    
    def reset_intelligence(self):
        """Reset all intelligence data"""
        try:
            self.detected_patterns.clear()
            self.market_correlations.clear()
            self.behavioral_clusters.clear()
            self.trade_history.clear()
            self.pattern_examples.clear()
            self.pattern_performance.clear()
            self.prediction_accuracy.clear()
            self.price_history.clear()
            self.volume_history.clear()
            self.coin_clusters.clear()
            
            self._logger.info("Intelligence data reset")
            
        except Exception as e:
            self._logger.error(f"Error resetting intelligence: {e}")

class MarketAnalyzer:
    """
    Advanced market analysis utilities for whale trading.
    
    Provides:
    - Real-time market state analysis
    - Volatility assessment
    - Liquidity analysis
    - Risk assessment
    """
    
    def __init__(self, config: WhaleBotConfig):
        self.config = config
        self._logger = logger
    
    def analyze_market_state(self, coin: str) -> Dict[str, Any]:
        """
        Analyze current market state for a coin.
        
        Args:
            coin: Coin to analyze
            
        Returns:
            Dict: Market state analysis
        """
        try:
            # This is a simplified implementation
            # Real implementation would use actual market data
            
            return {
                "volatility": "medium",
                "liquidity": "good", 
                "trend": "neutral",
                "risk_level": "moderate",
                "confidence": 0.6
            }
            
        except Exception as e:
            self._logger.error(f"Error analyzing market state: {e}")
            return {"error": str(e)}
    
    def assess_volatility(self, coin: str, timeframe_minutes: int = 60) -> float:
        """Assess volatility for a coin over specified timeframe"""
        try:
            # Simplified volatility calculation
            # Real implementation would use price data
            return 0.02  # 2% volatility
            
        except Exception as e:
            self._logger.error(f"Error assessing volatility: {e}")
            return 0.02  # Default 2%
    
    def analyze_liquidity(self, coin: str) -> Dict[str, float]:
        """Analyze liquidity conditions for a coin"""
        try:
            # Simplified liquidity analysis
            return {
                "bid_depth": 100_000,
                "ask_depth": 100_000,
                "spread_bps": 10,  # 10 basis points
                "liquidity_score": 0.7
            }
            
        except Exception as e:
            self._logger.error(f"Error analyzing liquidity: {e}")
            return {"liquidity_score": 0.5}