"""
Strategy Analyzer Types Module
Defines data structures for trading strategy analysis
"""

from dataclasses import dataclass, field
from datetime import datetime, time
from typing import List, Optional, Dict, Literal
from enum import Enum


class PatternType(str, Enum):
    """Types of trading patterns that can be identified"""
    TREND_FOLLOWING = "trend_following"
    MEAN_REVERSION = "mean_reversion"
    BREAKOUT = "breakout"
    SCALPING = "scalping"
    SWING = "swing"
    POSITION = "position"
    MOMENTUM = "momentum"
    CONTRARIAN = "contrarian"
    UNKNOWN = "unknown"


class RecommendationType(str, Enum):
    """Types of strategy recommendations"""
    RISK_MANAGEMENT = "risk_management"
    POSITION_SIZING = "position_sizing"
    TIMING = "timing"
    DIVERSIFICATION = "diversification"
    EXIT_STRATEGY = "exit_strategy"
    ENTRY_STRATEGY = "entry_strategy"
    LEVERAGE = "leverage"
    COIN_SELECTION = "coin_selection"


@dataclass
class TimeAnalysis:
    """
    Analysis of trading performance by time periods
    
    Attributes:
        hour_of_day: Performance by hour (0-23)
        day_of_week: Performance by day (0=Mon, 6=Sun)
        best_hour: Most profitable hour
        worst_hour: Least profitable hour
        best_day: Most profitable day of week
        worst_day: Least profitable day of week
        avg_trade_duration_minutes: Average time between entry and exit
        total_active_hours: Total hours with trading activity
    """
    hour_of_day: Dict[int, float] = field(default_factory=dict)
    day_of_week: Dict[int, float] = field(default_factory=dict)
    best_hour: Optional[int] = None
    worst_hour: Optional[int] = None
    best_day: Optional[int] = None
    worst_day: Optional[int] = None
    avg_trade_duration_minutes: float = 0.0
    total_active_hours: int = 0
    
    @property
    def best_day_name(self) -> Optional[str]:
        """Get the name of the best performing day"""
        if self.best_day is None:
            return None
        days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
        return days[self.best_day]
    
    @property
    def worst_day_name(self) -> Optional[str]:
        """Get the name of the worst performing day"""
        if self.worst_day is None:
            return None
        days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
        return days[self.worst_day]


@dataclass
class TradePattern:
    """
    Represents an identified trading pattern
    
    Attributes:
        pattern_type: Type of pattern identified
        confidence: Confidence level (0-100)
        description: Human-readable description
        supporting_metrics: Metrics that support this pattern identification
        sample_trades_count: Number of trades analyzed for this pattern
    """
    pattern_type: PatternType
    confidence: float
    description: str
    supporting_metrics: Dict[str, float] = field(default_factory=dict)
    sample_trades_count: int = 0
    
    def __post_init__(self):
        """Validate pattern data"""
        if not 0 <= self.confidence <= 100:
            raise ValueError(f"Confidence must be between 0 and 100, got {self.confidence}")


@dataclass
class StrategyMetrics:
    """
    Comprehensive metrics for evaluating a trading strategy
    
    Attributes:
        strategy_name: Name/identifier of the strategy
        total_trades: Total number of trades
        winning_trades: Number of profitable trades
        losing_trades: Number of losing trades
        break_even_trades: Number of break-even trades
        
        gross_profit: Total profit from winning trades
        gross_loss: Total loss from losing trades (absolute value)
        net_profit: Net profit after all trades
        total_fees: Total fees paid
        
        win_rate: Percentage of winning trades
        profit_factor: Ratio of gross profit to gross loss
        risk_reward_ratio: Average win / Average loss
        expectancy: Expected profit per trade
        
        max_drawdown: Maximum peak-to-trough decline
        max_drawdown_percentage: Maximum drawdown as percentage
        recovery_factor: Net profit / Max drawdown
        
        sharpe_ratio: Risk-adjusted return metric
        sortino_ratio: Downside risk-adjusted return
        calmar_ratio: Return / Max drawdown
        
        avg_win: Average profit on winning trades
        avg_loss: Average loss on losing trades
        largest_win: Largest single winning trade
        largest_loss: Largest single losing trade
        
        avg_trade_size: Average position size
        max_consecutive_wins: Maximum consecutive winning trades
        max_consecutive_losses: Maximum consecutive losing trades
        
        coins_traded: List of coins traded
        most_traded_coin: Most frequently traded coin
        most_profitable_coin: Most profitable coin
        least_profitable_coin: Least profitable coin
        
        time_analysis: Time-based performance analysis
        identified_patterns: Detected trading patterns
        
        start_date: First trade date
        end_date: Last trade date
        analysis_timestamp: When this analysis was performed
    """
    strategy_name: str = "Default Strategy"
    
    # Trade counts
    total_trades: int = 0
    winning_trades: int = 0
    losing_trades: int = 0
    break_even_trades: int = 0
    
    # Profit/Loss
    gross_profit: float = 0.0
    gross_loss: float = 0.0
    net_profit: float = 0.0
    total_fees: float = 0.0
    
    # Performance ratios
    win_rate: float = 0.0
    profit_factor: float = 0.0
    risk_reward_ratio: float = 0.0
    expectancy: float = 0.0
    
    # Risk metrics
    max_drawdown: float = 0.0
    max_drawdown_percentage: float = 0.0
    recovery_factor: float = 0.0
    
    # Advanced ratios
    sharpe_ratio: float = 0.0
    sortino_ratio: float = 0.0
    calmar_ratio: float = 0.0
    
    # Trade statistics
    avg_win: float = 0.0
    avg_loss: float = 0.0
    largest_win: float = 0.0
    largest_loss: float = 0.0
    
    # Position sizing
    avg_trade_size: float = 0.0
    avg_trade_value: float = 0.0
    total_volume: float = 0.0
    
    # Streaks
    max_consecutive_wins: int = 0
    max_consecutive_losses: int = 0
    current_streak: int = 0
    current_streak_type: Optional[Literal["WIN", "LOSS"]] = None
    
    # Coin analysis
    coins_traded: List[str] = field(default_factory=list)
    most_traded_coin: Optional[str] = None
    most_profitable_coin: Optional[str] = None
    least_profitable_coin: Optional[str] = None
    coin_performance: Dict[str, float] = field(default_factory=dict)
    
    # Time analysis
    time_analysis: Optional[TimeAnalysis] = None
    
    # Pattern recognition
    identified_patterns: List[TradePattern] = field(default_factory=list)
    primary_pattern: Optional[PatternType] = None
    
    # Date range
    start_date: Optional[datetime] = None
    end_date: Optional[datetime] = None
    analysis_timestamp: Optional[datetime] = None
    
    # PnL history for equity curve
    equity_curve: List[float] = field(default_factory=list)
    
    @property
    def net_profit_after_fees(self) -> float:
        """Net profit after subtracting all fees"""
        return self.net_profit - self.total_fees
    
    @property
    def avg_trade_pnl(self) -> float:
        """Average PnL per trade"""
        if self.total_trades == 0:
            return 0.0
        return self.net_profit / self.total_trades
    
    @property
    def trade_duration_days(self) -> int:
        """Number of days between first and last trade"""
        if not self.start_date or not self.end_date:
            return 0
        return (self.end_date - self.start_date).days
    
    @property
    def trades_per_day(self) -> float:
        """Average trades per day"""
        days = self.trade_duration_days
        if days == 0:
            return float(self.total_trades)
        return self.total_trades / days
    
    def is_profitable(self) -> bool:
        """Check if strategy is overall profitable"""
        return self.net_profit > 0
    
    def get_grade(self) -> str:
        """
        Get an overall grade for the strategy
        Based on multiple metrics
        """
        score = 0
        
        # Win rate contribution (max 25 points)
        if self.win_rate >= 60:
            score += 25
        elif self.win_rate >= 50:
            score += 20
        elif self.win_rate >= 40:
            score += 15
        else:
            score += 5
        
        # Profit factor contribution (max 25 points)
        if self.profit_factor >= 2.0:
            score += 25
        elif self.profit_factor >= 1.5:
            score += 20
        elif self.profit_factor >= 1.2:
            score += 15
        elif self.profit_factor >= 1.0:
            score += 10
        else:
            score += 0
        
        # Risk/Reward contribution (max 25 points)
        if self.risk_reward_ratio >= 2.0:
            score += 25
        elif self.risk_reward_ratio >= 1.5:
            score += 20
        elif self.risk_reward_ratio >= 1.0:
            score += 15
        else:
            score += 5
        
        # Recovery factor contribution (max 25 points)
        if self.recovery_factor >= 3.0:
            score += 25
        elif self.recovery_factor >= 2.0:
            score += 20
        elif self.recovery_factor >= 1.0:
            score += 15
        elif self.recovery_factor > 0:
            score += 10
        else:
            score += 0
        
        # Convert score to grade
        if score >= 90:
            return "A+"
        elif score >= 80:
            return "A"
        elif score >= 70:
            return "B+"
        elif score >= 60:
            return "B"
        elif score >= 50:
            return "C+"
        elif score >= 40:
            return "C"
        elif score >= 30:
            return "D"
        else:
            return "F"


@dataclass
class StrategyRecommendation:
    """
    A recommendation for improving strategy performance
    
    Attributes:
        recommendation_type: Category of recommendation
        priority: Priority level (1=highest, 5=lowest)
        title: Short title
        description: Detailed description
        expected_impact: Expected improvement description
        metrics_affected: List of metrics this would improve
    """
    recommendation_type: RecommendationType
    priority: int
    title: str
    description: str
    expected_impact: str = ""
    metrics_affected: List[str] = field(default_factory=list)
    
    def __post_init__(self):
        """Validate recommendation data"""
        if not 1 <= self.priority <= 5:
            raise ValueError(f"Priority must be between 1 and 5, got {self.priority}")


@dataclass
class StrategyComparison:
    """
    Comparison between multiple strategies
    
    Attributes:
        strategies: List of strategy metrics being compared
        best_overall: Name of best performing strategy
        best_by_metric: Best strategy for each key metric
        summary: Text summary of comparison
        comparison_timestamp: When this comparison was performed
    """
    strategies: List[StrategyMetrics] = field(default_factory=list)
    best_overall: Optional[str] = None
    best_by_metric: Dict[str, str] = field(default_factory=dict)
    summary: str = ""
    comparison_timestamp: Optional[datetime] = None
    
    def get_strategy(self, name: str) -> Optional[StrategyMetrics]:
        """Get a strategy by name"""
        for s in self.strategies:
            if s.strategy_name == name:
                return s
        return None
    
    def get_ranking(self) -> List[str]:
        """Get strategies ranked by overall performance (net profit)"""
        sorted_strategies = sorted(
            self.strategies,
            key=lambda s: s.net_profit,
            reverse=True
        )
        return [s.strategy_name for s in sorted_strategies]
