"""
Notification system type definitions.

This module defines the core types for the notification system, including
notification services, configurations, messages, and priorities.
"""

from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Dict, Any, Optional, List, Callable
from pathlib import Path


class NotificationService(str, Enum):
    """Supported notification services."""
    
    TELEGRAM = "telegram"
    DISCORD = "discord"
    EMAIL = "email"  # Future expansion
    SLACK = "slack"  # Future expansion
    WEBHOOK = "webhook"  # Future expansion


class NotificationPriority(str, Enum):
    """Notification priority levels."""
    
    LOW = "low"
    NORMAL = "normal"
    HIGH = "high"
    CRITICAL = "critical"


class MessageFormat(str, Enum):
    """Message formatting options."""
    
    PLAIN = "plain"  # Plain text
    MARKDOWN = "markdown"  # Markdown formatting
    HTML = "html"  # HTML formatting


class NotificationType(str, Enum):
    """Types of notifications."""
    
    ALERT = "alert"
    PNL_UPDATE = "pnl_update"
    POSITION_CHANGE = "position_change"
    STRATEGY_RECOMMENDATION = "strategy_recommendation"
    REPORT_READY = "report_ready"
    SYSTEM_STATUS = "system_status"
    ERROR = "error"
    CUSTOM = "custom"


@dataclass
class TelegramConfig:
    """Telegram bot configuration."""
    
    bot_token: str
    chat_id: str
    parse_mode: str = "Markdown"  # "Markdown", "HTML", or "None"
    disable_notification: bool = False
    disable_web_page_preview: bool = True
    
    def __post_init__(self):
        """Validate configuration."""
        if not self.bot_token:
            raise ValueError("Telegram bot token is required")
        if not self.chat_id:
            raise ValueError("Telegram chat ID is required")
        if self.parse_mode not in ["Markdown", "HTML", "None"]:
            raise ValueError(f"Invalid parse_mode: {self.parse_mode}")


@dataclass
class DiscordConfig:
    """Discord webhook configuration."""
    
    webhook_url: str
    username: Optional[str] = None
    avatar_url: Optional[str] = None
    
    def __post_init__(self):
        """Validate configuration."""
        if not self.webhook_url:
            raise ValueError("Discord webhook URL is required")
        if not self.webhook_url.startswith("https://discord.com/api/webhooks/"):
            raise ValueError("Invalid Discord webhook URL")


@dataclass
class NotificationConfig:
    """General notification configuration."""
    
    service: NotificationService
    telegram_config: Optional[TelegramConfig] = None
    discord_config: Optional[DiscordConfig] = None
    
    # General settings
    enabled: bool = True
    max_retries: int = 3
    retry_delay: float = 1.0  # seconds
    rate_limit: int = 30  # messages per minute
    
    # Filtering
    min_priority: NotificationPriority = NotificationPriority.NORMAL
    enabled_types: Optional[List[NotificationType]] = None
    
    def __post_init__(self):
        """Validate configuration."""
        if self.service == NotificationService.TELEGRAM and not self.telegram_config:
            raise ValueError("Telegram config is required for Telegram service")
        if self.service == NotificationService.DISCORD and not self.discord_config:
            raise ValueError("Discord config is required for Discord service")
        
        if self.max_retries < 0:
            raise ValueError("max_retries must be non-negative")
        if self.retry_delay < 0:
            raise ValueError("retry_delay must be non-negative")
        if self.rate_limit <= 0:
            raise ValueError("rate_limit must be positive")
        
        # Default: enable all notification types
        if self.enabled_types is None:
            self.enabled_types = list(NotificationType)


@dataclass
class NotificationMessage:
    """A notification message to be sent."""
    
    title: str
    body: str
    notification_type: NotificationType = NotificationType.CUSTOM
    priority: NotificationPriority = NotificationPriority.NORMAL
    format: MessageFormat = MessageFormat.MARKDOWN
    
    # Optional metadata
    metadata: Dict[str, Any] = field(default_factory=dict)
    timestamp: datetime = field(default_factory=datetime.now)
    
    # Optional attachments
    image_url: Optional[str] = None
    file_path: Optional[Path] = None
    
    # Optional callback
    on_success: Optional[Callable] = None
    on_failure: Optional[Callable] = None
    
    def to_plain_text(self) -> str:
        """Convert message to plain text."""
        return f"{self.title}\n\n{self.body}"
    
    def to_markdown(self) -> str:
        """Convert message to Markdown format."""
        return f"**{self.title}**\n\n{self.body}"
    
    def to_html(self) -> str:
        """Convert message to HTML format."""
        return f"<b>{self.title}</b>\n\n{self.body}"
    
    def format_message(self, target_format: Optional[MessageFormat] = None) -> str:
        """
        Format message according to specified format.
        
        Args:
            target_format: Target format (uses self.format if None)
            
        Returns:
            Formatted message string
        """
        fmt = target_format or self.format
        
        if fmt == MessageFormat.PLAIN:
            return self.to_plain_text()
        elif fmt == MessageFormat.MARKDOWN:
            return self.to_markdown()
        elif fmt == MessageFormat.HTML:
            return self.to_html()
        else:
            return self.to_plain_text()
    
    @property
    def is_urgent(self) -> bool:
        """Check if message is urgent (HIGH or CRITICAL priority)."""
        return self.priority in [NotificationPriority.HIGH, NotificationPriority.CRITICAL]


@dataclass
class NotificationResult:
    """Result of a notification attempt."""
    
    success: bool
    message_id: Optional[str] = None  # Service-specific message ID
    error: Optional[str] = None
    timestamp: datetime = field(default_factory=datetime.now)
    attempts: int = 1
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    @property
    def failed(self) -> bool:
        """Check if notification failed."""
        return not self.success


@dataclass
class NotificationStats:
    """Statistics for notification sending."""
    
    total_sent: int = 0
    successful: int = 0
    failed: int = 0
    retried: int = 0
    rate_limited: int = 0
    
    # Per-type statistics
    by_type: Dict[NotificationType, int] = field(default_factory=dict)
    by_priority: Dict[NotificationPriority, int] = field(default_factory=dict)
    
    # Timing statistics
    total_duration: float = 0.0  # seconds
    last_sent: Optional[datetime] = None
    
    @property
    def success_rate(self) -> float:
        """Calculate success rate percentage."""
        if self.total_sent == 0:
            return 0.0
        return (self.successful / self.total_sent) * 100
    
    @property
    def failure_rate(self) -> float:
        """Calculate failure rate percentage."""
        if self.total_sent == 0:
            return 0.0
        return (self.failed / self.total_sent) * 100
    
    @property
    def average_duration(self) -> float:
        """Calculate average send duration."""
        if self.successful == 0:
            return 0.0
        return self.total_duration / self.successful
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert statistics to dictionary."""
        return {
            "total_sent": self.total_sent,
            "successful": self.successful,
            "failed": self.failed,
            "retried": self.retried,
            "rate_limited": self.rate_limited,
            "success_rate": round(self.success_rate, 2),
            "failure_rate": round(self.failure_rate, 2),
            "average_duration": round(self.average_duration, 4),
            "last_sent": self.last_sent.isoformat() if self.last_sent else None,
            "by_type": {k.value: v for k, v in self.by_type.items()},
            "by_priority": {k.value: v for k, v in self.by_priority.items()},
        }


# Type aliases for callbacks
NotificationCallback = Callable[[NotificationResult], None]
ErrorCallback = Callable[[Exception, NotificationMessage], None]
