#!/usr/bin/env python3
"""
Dashboard Components for Real-time Whale Monitoring

Provides dashboard widgets and components for real-time visualization
of whale trading data with interactive charts and metrics.

Author: Pezhman Hajipour
Version: 3.0.0
"""

import logging
import asyncio
import json
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any, Callable
from dataclasses import dataclass
import uuid

# Check for pandas (required for data processing)
PANDAS_AVAILABLE = False
try:
    import pandas as pd
    PANDAS_AVAILABLE = True
except ImportError:
    PANDAS_AVAILABLE = False

# Check for visualization libraries
MATPLOTLIB_AVAILABLE = False
PLOTLY_AVAILABLE = False

try:
    import matplotlib.pyplot as plt
    MATPLOTLIB_AVAILABLE = True
except ImportError:
    MATPLOTLIB_AVAILABLE = False

try:
    import plotly.graph_objects as go
    import plotly.express as px
    from plotly.subplots import make_subplots
    import plotly.io as pio
    PLOTLY_AVAILABLE = True
except ImportError:
    PLOTLY_AVAILABLE = False
    # Create dummy objects for type hints when plotly is not available
    class _DummyGo:
        class Figure:
            pass
    go = _DummyGo()

logger = logging.getLogger(__name__)

@dataclass
class TradingMetrics:
    """Trading metrics data structure"""
    timestamp: datetime
    total_volume_24h: float
    whale_count_24h: int
    buy_volume_24h: float
    sell_volume_24h: float
    top_whale_address: str
    top_volume_24h: float
    active_symbols: int
    market_sentiment: float
    alerts_count: int

@dataclass
class AlertData:
    """Alert data structure"""
    alert_id: str
    timestamp: datetime
    alert_type: str
    priority: str  # INFO, WARNING, CRITICAL
    message: str
    symbol: Optional[str] = None
    volume: Optional[float] = None
    address: Optional[str] = None

class TradingMetricsWidget:
    """Real-time trading metrics dashboard widget"""
    
    def __init__(self, update_interval: int = 5000):
        self.update_interval = update_interval
        self.metrics_history = []
        self.callbacks = []
        
    def add_metrics(self, metrics: TradingMetrics):
        """Add new trading metrics"""
        self.metrics_history.append(metrics)
        
        # Keep only last 100 metrics for performance
        if len(self.metrics_history) > 100:
            self.metrics_history = self.metrics_history[-100:]
            
        # Notify callbacks
        for callback in self.callbacks:
            try:
                callback(metrics)
            except Exception as e:
                logger.error(f"Error in metrics callback: {e}")
                
    def add_callback(self, callback: Callable[[TradingMetrics], None]):
        """Add callback for metrics updates"""
        self.callbacks.append(callback)
        
    def get_latest_metrics(self) -> Optional[TradingMetrics]:
        """Get the latest trading metrics"""
        return self.metrics_history[-1] if self.metrics_history else None
        
    def get_metrics_summary(self) -> Dict[str, Any]:
        """Get metrics summary for dashboard"""
        if not self.metrics_history:
            return {}
            
        latest = self.get_latest_metrics()
        history_1h = [m for m in self.metrics_history 
                     if m.timestamp > datetime.now() - timedelta(hours=1)]
        
        return {
            'latest': {
                'timestamp': latest.timestamp.isoformat(),
                'total_volume_24h': latest.total_volume_24h,
                'whale_count_24h': latest.whale_count_24h,
                'buy_volume_24h': latest.buy_volume_24h,
                'sell_volume_24h': latest.sell_volume_24h,
                'market_sentiment': latest.market_sentiment,
                'alerts_count': latest.alerts_count
            },
            'summary_1h': {
                'avg_volume': sum(m.total_volume_24h for m in history_1h) / len(history_1h) if history_1h else 0,
                'avg_whales': sum(m.whale_count_24h for m in history_1h) / len(history_1h) if history_1h else 0,
                'trend': 'up' if len(history_1h) > 1 and history_1h[-1].total_volume_24h > history_1h[0].total_volume_24h else 'down'
            }
        }
        
    def create_plotly_dashboard(self) -> Optional[go.Figure]:
        """Create Plotly dashboard for metrics visualization"""
        if not PLOTLY_AVAILABLE:
            raise ImportError("Plotly not available. Install with: pip install hyperliquid-monitor-plus[viz]")
            
        if not self.metrics_history:
            logger.warning("No metrics data for dashboard")
            return None
            
        # Convert to DataFrame
        df = pd.DataFrame([{
            'timestamp': m.timestamp,
            'total_volume_24h': m.total_volume_24h,
            'whale_count_24h': m.whale_count_24h,
            'buy_volume_24h': m.buy_volume_24h,
            'sell_volume_24h': m.sell_volume_24h,
            'market_sentiment': m.market_sentiment,
            'alerts_count': m.alerts_count
        } for m in self.metrics_history])
        
        # Create subplots
        fig = make_subplots(
            rows=3, cols=2,
            subplot_titles=('Total Volume (24h)', 'Whale Count (24h)',
                          'BUY vs SELL Volume', 'Market Sentiment',
                          'Alerts Count', 'Volume vs Whale Count'),
            specs=[[{"secondary_y": False}, {"secondary_y": False}],
                   [{"secondary_y": False}, {"secondary_y": False}],
                   [{"secondary_y": False}, {"secondary_y": False}]]
        )
        
        # Total volume
        fig.add_trace(
            go.Scatter(
                x=df['timestamp'],
                y=df['total_volume_24h'],
                mode='lines+markers',
                name='Total Volume',
                line=dict(color='blue', width=2),
                hovertemplate='<b>Time:</b> %{x}<br><b>Volume:</b> $%{y:,.0f}<extra></extra>'
            ),
            row=1, col=1
        )
        
        # Whale count
        fig.add_trace(
            go.Scatter(
                x=df['timestamp'],
                y=df['whale_count_24h'],
                mode='lines+markers',
                name='Whale Count',
                line=dict(color='green', width=2),
                hovertemplate='<b>Time:</b> %{x}<br><b>Count:</b> %{y}<extra></extra>'
            ),
            row=1, col=2
        )
        
        # BUY vs SELL volume
        fig.add_trace(
            go.Scatter(
                x=df['timestamp'],
                y=df['buy_volume_24h'],
                mode='lines',
                name='BUY Volume',
                line=dict(color='lightgreen', width=2),
                fill='tonexty',
                hovertemplate='<b>Time:</b> %{x}<br><b>BUY Volume:</b> $%{y:,.0f}<extra></extra>'
            ),
            row=2, col=1
        )
        
        fig.add_trace(
            go.Scatter(
                x=df['timestamp'],
                y=-df['sell_volume_24h'],
                mode='lines',
                name='SELL Volume',
                line=dict(color='lightcoral', width=2),
                fill='tozeroy',
                hovertemplate='<b>Time:</b> %{x}<br><b>SELL Volume:</b> $%{y:,.0f}<extra></extra>'
            ),
            row=2, col=1
        )
        
        # Market sentiment
        colors = ['red' if s < 0 else 'green' for s in df['market_sentiment']]
        fig.add_trace(
            go.Bar(
                x=df['timestamp'],
                y=df['market_sentiment'],
                name='Sentiment',
                marker_color=colors,
                hovertemplate='<b>Time:</b> %{x}<br><b>Sentiment:</b> %{y:.2f}<extra></extra>'
            ),
            row=2, col=2
        )
        
        # Alerts count
        fig.add_trace(
            go.Scatter(
                x=df['timestamp'],
                y=df['alerts_count'],
                mode='lines+markers',
                name='Alerts',
                line=dict(color='orange', width=2),
                hovertemplate='<b>Time:</b> %{x}<br><b>Alerts:</b> %{y}<extra></extra>'
            ),
            row=3, col=1
        )
        
        # Volume vs Whale Count scatter
        fig.add_trace(
            go.Scatter(
                x=df['whale_count_24h'],
                y=df['total_volume_24h'],
                mode='markers',
                name='Volume vs Whales',
                marker=dict(
                    size=8,
                    color=df['market_sentiment'],
                    colorscale='RdYlGn',
                    showscale=True,
                    colorbar=dict(title="Sentiment", x=1.1)
                ),
                text=df['timestamp'],
                hovertemplate='<b>Whales:</b> %{x}<br><b>Volume:</b> $%{y:,.0f}<br><b>Time:</b> %{text}<extra></extra>'
            ),
            row=3, col=2
        )
        
        # Update layout
        fig.update_layout(
            title_text="Whale Trading Dashboard - Real-time Metrics",
            title_x=0.5,
            height=900,
            showlegend=True,
            hovermode='x unified'
        )
        
        # Update axes labels
        fig.update_xaxes(title_text="Time", row=1, col=1)
        fig.update_yaxes(title_text="Volume (USD)", row=1, col=1)
        
        fig.update_xaxes(title_text="Time", row=1, col=2)
        fig.update_yaxes(title_text="Whale Count", row=1, col=2)
        
        fig.update_xaxes(title_text="Time", row=2, col=1)
        fig.update_yaxes(title_text="Volume (USD)", row=2, col=1)
        
        fig.update_xaxes(title_text="Time", row=2, col=2)
        fig.update_yaxes(title_text="Sentiment Score", row=2, col=2)
        
        fig.update_xaxes(title_text="Time", row=3, col=1)
        fig.update_yaxes(title_text="Alert Count", row=3, col=1)
        
        fig.update_xaxes(title_text="Whale Count", row=3, col=2)
        fig.update_yaxes(title_text="Volume (USD)", row=3, col=2)
        
        return fig

class AlertDistributionWidget:
    """Alert distribution and management dashboard widget"""
    
    def __init__(self):
        self.alerts = []
        self.alert_callbacks = []
        
    def add_alert(self, alert: AlertData):
        """Add new alert"""
        self.alerts.append(alert)
        
        # Keep only last 500 alerts for performance
        if len(self.alerts) > 500:
            self.alerts = self.alerts[-500:]
            
        # Notify callbacks
        for callback in self.alert_callbacks:
            try:
                callback(alert)
            except Exception as e:
                logger.error(f"Error in alert callback: {e}")
                
    def add_callback(self, callback: Callable[[AlertData], None]):
        """Add callback for new alerts"""
        self.alert_callbacks.append(callback)
        
    def get_recent_alerts(self, hours: int = 24) -> List[AlertData]:
        """Get alerts from the last N hours"""
        cutoff = datetime.now() - timedelta(hours=hours)
        return [alert for alert in self.alerts if alert.timestamp > cutoff]
        
    def get_alert_statistics(self) -> Dict[str, Any]:
        """Get alert statistics for dashboard"""
        recent_alerts = self.get_recent_alerts(24)
        
        if not recent_alerts:
            return {}
            
        priority_counts = {}
        type_counts = {}
        
        for alert in recent_alerts:
            priority_counts[alert.priority] = priority_counts.get(alert.priority, 0) + 1
            type_counts[alert.alert_type] = type_counts.get(alert.alert_type, 0) + 1
            
        return {
            'total_alerts_24h': len(recent_alerts),
            'priority_distribution': priority_counts,
            'type_distribution': type_counts,
            'latest_alert': {
                'timestamp': recent_alerts[-1].timestamp.isoformat(),
                'type': recent_alerts[-1].alert_type,
                'priority': recent_alerts[-1].priority,
                'message': recent_alerts[-1].message
            }
        }
        
    def create_alert_dashboard(self) -> Optional[go.Figure]:
        """Create alert distribution dashboard"""
        if not PLOTLY_AVAILABLE:
            raise ImportError("Plotly not available. Install with: pip install hyperliquid-monitor-plus[viz]")
            
        recent_alerts = self.get_recent_alerts(24)
        
        if not recent_alerts:
            logger.warning("No recent alerts for dashboard")
            return None
            
        # Prepare data
        df = pd.DataFrame([{
            'timestamp': alert.timestamp,
            'priority': alert.priority,
            'alert_type': alert.alert_type,
            'hour': alert.timestamp.hour
        } for alert in recent_alerts])
        
        # Create subplots
        fig = make_subplots(
            rows=2, cols=2,
            subplot_titles=('Alerts by Priority', 'Alerts by Type',
                          'Alerts Over Time', 'Hourly Distribution'),
            specs=[[{"type": "pie"}, {"type": "bar"}],
                   [{"type": "scatter"}, {"type": "bar"}]]
        )
        
        # Priority distribution pie chart
        priority_counts = df['priority'].value_counts()
        fig.add_trace(
            go.Pie(
                labels=priority_counts.index,
                values=priority_counts.values,
                name="Priority",
                hovertemplate='<b>%{label}</b><br>Count: %{value}<br>Percentage: %{percent}<extra></extra>'
            ),
            row=1, col=1
        )
        
        # Alert type distribution
        type_counts = df['alert_type'].value_counts().head(10)
        fig.add_trace(
            go.Bar(
                x=type_counts.values,
                y=type_counts.index,
                orientation='h',
                name="Alert Types",
                marker_color='lightblue',
                hovertemplate='<b>%{y}</b><br>Count: %{x}<extra></extra>'
            ),
            row=1, col=2
        )
        
        # Alerts over time
        alert_timeline = df.groupby('timestamp').size()
        fig.add_trace(
            go.Scatter(
                x=alert_timeline.index,
                y=alert_timeline.values,
                mode='lines+markers',
                name='Alert Timeline',
                line=dict(color='purple', width=2),
                hovertemplate='<b>Time:</b> %{x}<br><b>Alerts:</b> %{y}<extra></extra>'
            ),
            row=2, col=1
        )
        
        # Hourly distribution
        hourly_counts = df['hour'].value_counts().sort_index()
        fig.add_trace(
            go.Bar(
                x=hourly_counts.index,
                y=hourly_counts.values,
                name='Hourly',
                marker_color='coral',
                hovertemplate='<b>Hour:</b> %{x}:00<br><b>Alerts:</b> %{y}<extra></extra>'
            ),
            row=2, col=2
        )
        
        # Update layout
        fig.update_layout(
            title_text="Alert Distribution Dashboard - Last 24 Hours",
            title_x=0.5,
            height=700,
            showlegend=False
        )
        
        return fig

class PerformanceDashboard:
    """Comprehensive performance dashboard"""
    
    def __init__(self):
        self.trading_metrics_widget = TradingMetricsWidget()
        self.alert_widget = AlertDistributionWidget()
        self.custom_data_sources = {}
        
    def add_data_source(self, name: str, data_source: Callable[[], Dict[str, Any]]):
        """Add custom data source for dashboard"""
        self.custom_data_sources[name] = data_source
        
    def get_dashboard_data(self) -> Dict[str, Any]:
        """Get all dashboard data"""
        dashboard_data = {
            'timestamp': datetime.now().isoformat(),
            'metrics': self.trading_metrics_widget.get_metrics_summary(),
            'alerts': self.alert_widget.get_alert_statistics(),
            'custom': {}
        }
        
        # Add custom data sources
        for name, source in self.custom_data_sources.items():
            try:
                dashboard_data['custom'][name] = source()
            except Exception as e:
                logger.error(f"Error getting data from {name}: {e}")
                dashboard_data['custom'][name] = {'error': str(e)}
                
        return dashboard_data
        
    def create_combined_dashboard(self) -> Optional[go.Figure]:
        """Create combined performance dashboard"""
        if not PLOTLY_AVAILABLE:
            raise ImportError("Plotly not available. Install with: pip install hyperliquid-monitor-plus[viz]")
            
        # Get data
        dashboard_data = self.get_dashboard_data()
        
        if not dashboard_data['metrics'] and not dashboard_data['alerts']:
            logger.warning("No data for combined dashboard")
            return None
            
        # Create figure
        fig = make_subplots(
            rows=4, cols=2,
            subplot_titles=('Trading Volume (24h)', 'Alert Statistics',
                          'Whale Count (24h)', 'Alert Priority Distribution',
                          'Market Sentiment', 'Alerts Over Time',
                          'Custom Metrics', 'Summary Statistics'),
            specs=[[{"secondary_y": False}, {"type": "pie"}],
                   [{"secondary_y": False}, {"type": "pie"}],
                   [{"secondary_y": False}, {"secondary_y": False}],
                   [{"colspan": 2}, None]],
            vertical_spacing=0.08,
            horizontal_spacing=0.1
        )
        
        # Add metrics data if available
        if dashboard_data['metrics']:
            latest = dashboard_data['metrics']['latest']
            summary = dashboard_data['metrics']['summary_1h']
            
            # Trading volume
            fig.add_trace(
                go.Indicator(
                    mode="gauge+number",
                    value=latest['total_volume_24h'],
                    domain={'x': [0, 1], 'y': [0, 1]},
                    title={'text': "Volume (USD)"},
                    gauge={
                        'axis': {'range': [None, latest['total_volume_24h'] * 1.2]},
                        'bar': {'color': "blue"},
                        'steps': [
                            {'range': [0, latest['total_volume_24h'] * 0.5], 'color': "lightgray"},
                            {'range': [latest['total_volume_24h'] * 0.5, latest['total_volume_24h'] * 0.8], 'color': "yellow"}
                        ],
                        'threshold': {
                            'line': {'color': "red", 'width': 4},
                            'thickness': 0.75,
                            'value': latest['total_volume_24h'] * 0.9
                        }
                    }
                ),
                row=1, col=1
            )
            
            # Whale count
            fig.add_trace(
                go.Indicator(
                    mode="number",
                    value=latest['whale_count_24h'],
                    title={'text': "Whales (24h)"},
                    domain={'x': [0, 1], 'y': [0, 1]}
                ),
                row=2, col=1
            )
            
            # Market sentiment
            sentiment_color = "red" if latest['market_sentiment'] < 0 else "green"
            fig.add_trace(
                go.Indicator(
                    mode="gauge+number",
                    value=latest['market_sentiment'],
                    domain={'x': [0, 1], 'y': [0, 1]},
                    title={'text': "Sentiment"},
                    gauge={
                        'axis': {'range': [-1, 1]},
                        'bar': {'color': sentiment_color},
                        'steps': [
                            {'range': [-1, -0.5], 'color': "red"},
                            {'range': [-0.5, 0.5], 'color': "yellow"},
                            {'range': [0.5, 1], 'color': "green"}
                        ]
                    }
                ),
                row=3, col=1
            )
        
        # Add alert data if available
        if dashboard_data['alerts']:
            alert_stats = dashboard_data['alerts']
            
            # Alert statistics summary
            fig.add_trace(
                go.Indicator(
                    mode="number",
                    value=alert_stats['total_alerts_24h'],
                    title={'text': "Alerts (24h)"},
                    domain={'x': [0, 1], 'y': [0, 1]}
                ),
                row=4, col=1
            )
        
        # Update layout
        fig.update_layout(
            title_text="Whale Monitor Plus - Performance Dashboard",
            title_x=0.5,
            height=1000,
            showlegend=False
        )
        
        return fig

class RealTimeChart:
    """Real-time updating chart widget"""
    
    def __init__(self, chart_type: str = "line", update_interval: int = 5000):
        self.chart_type = chart_type
        self.update_interval = update_interval
        self.data_buffer = []
        self.max_points = 100
        
    def add_data_point(self, x_value: Any, y_value: float, metadata: Optional[Dict] = None):
        """Add new data point"""
        data_point = {
            'x': x_value,
            'y': y_value,
            'metadata': metadata or {},
            'timestamp': datetime.now()
        }
        
        self.data_buffer.append(data_point)
        
        # Keep only last max_points
        if len(self.data_buffer) > self.max_points:
            self.data_buffer = self.data_buffer[-self.max_points:]
            
    def get_recent_data(self, count: int = 20) -> List[Dict]:
        """Get recent data points"""
        return self.data_buffer[-count:] if self.data_buffer else []
        
    def create_plotly_chart(self, title: str = "Real-time Chart") -> Optional[go.Figure]:
        """Create Plotly real-time chart"""
        if not PLOTLY_AVAILABLE:
            raise ImportError("Plotly not available. Install with: pip install hyperliquid-monitor-plus[viz]")
            
        if not self.data_buffer:
            logger.warning("No data for real-time chart")
            return None
            
        fig = go.Figure()
        
        # Add trace based on chart type
        x_values = [point['x'] for point in self.data_buffer]
        y_values = [point['y'] for point in self.data_buffer]
        
        if self.chart_type == "line":
            fig.add_trace(go.Scatter(
                x=x_values,
                y=y_values,
                mode='lines+markers',
                name=title,
                line=dict(width=2),
                marker=dict(size=6)
            ))
        elif self.chart_type == "bar":
            fig.add_trace(go.Bar(
                x=x_values,
                y=y_values,
                name=title,
                marker_color='lightblue'
            ))
        elif self.chart_type == "scatter":
            fig.add_trace(go.Scatter(
                x=x_values,
                y=y_values,
                mode='markers',
                name=title,
                marker=dict(size=8)
            ))
            
        # Update layout
        fig.update_layout(
            title=title,
            xaxis_title="Time" if isinstance(x_values[0], datetime) else "X Value",
            yaxis_title="Y Value",
            height=400,
            showlegend=True
        )
        
        return fig