"""
Discord notification service implementation.

This module provides integration with Discord Webhooks for sending notifications.
"""

import time
import requests
from datetime import datetime, timedelta
from threading import RLock
from typing import Optional, Dict, Any, List
from pathlib import Path

from .types import (
    DiscordConfig,
    NotificationMessage,
    NotificationResult,
    NotificationStats,
    NotificationPriority,
    NotificationType,
    MessageFormat,
)


class DiscordNotifier:
    """
    Discord notification service.
    
    Sends notifications via Discord Webhooks with support for:
    - Rich embeds
    - Images
    - Files
    - Custom username and avatar
    - Retry mechanism
    - Rate limiting
    
    Thread-safe implementation.
    """
    
    def __init__(self, config: DiscordConfig):
        """
        Initialize Discord notifier.
        
        Args:
            config: Discord configuration
        """
        self.config = config
        self._lock = RLock()
        self._stats = NotificationStats()
        self._rate_limit_tokens: List[datetime] = []
        self._max_tokens = 30  # 30 messages per minute (Discord limit: 30/min per webhook)
        self._token_window = 60  # seconds
    
    def send(
        self,
        message: NotificationMessage,
        max_retries: int = 3,
        retry_delay: float = 1.0,
    ) -> NotificationResult:
        """
        Send a notification message.
        
        Args:
            message: Message to send
            max_retries: Maximum number of retry attempts
            retry_delay: Delay between retries in seconds
            
        Returns:
            NotificationResult with success/failure information
        """
        with self._lock:
            start_time = time.time()
            attempts = 0
            last_error = None
            
            # Check rate limit
            if not self._check_rate_limit():
                self._stats.rate_limited += 1
                return NotificationResult(
                    success=False,
                    error="Rate limit exceeded",
                    attempts=0,
                )
            
            while attempts < max_retries:
                attempts += 1
                
                try:
                    # Send based on message content
                    if message.file_path:
                        result = self._send_file(message)
                    else:
                        result = self._send_embed(message)
                    
                    # Update statistics
                    duration = time.time() - start_time
                    self._update_stats(message, True, duration)
                    
                    # Call success callback
                    if message.on_success:
                        try:
                            message.on_success(result)
                        except Exception:
                            pass  # Ignore callback errors
                    
                    return result
                    
                except Exception as e:
                    last_error = str(e)
                    
                    if attempts < max_retries:
                        time.sleep(retry_delay)
                        self._stats.retried += 1
                    else:
                        # Final failure
                        duration = time.time() - start_time
                        self._update_stats(message, False, duration)
                        
                        result = NotificationResult(
                            success=False,
                            error=last_error,
                            attempts=attempts,
                        )
                        
                        # Call failure callback
                        if message.on_failure:
                            try:
                                message.on_failure(result)
                            except Exception:
                                pass  # Ignore callback errors
                        
                        return result
            
            # Should not reach here
            return NotificationResult(
                success=False,
                error="Unknown error",
                attempts=attempts,
            )
    
    def _send_embed(self, message: NotificationMessage) -> NotificationResult:
        """Send a rich embed message."""
        # Build embed
        embed = {
            "title": message.title,
            "description": message.body,
            "timestamp": message.timestamp.isoformat(),
            "color": self._get_color_for_priority(message.priority),
        }
        
        # Add image if provided
        if message.image_url:
            embed["image"] = {"url": message.image_url}
        
        # Add footer with notification type
        embed["footer"] = {
            "text": f"{message.notification_type.value.replace('_', ' ').title()}"
        }
        
        # Prepare request payload
        payload = {
            "embeds": [embed],
        }
        
        # Add custom username and avatar if provided
        if self.config.username:
            payload["username"] = self.config.username
        if self.config.avatar_url:
            payload["avatar_url"] = self.config.avatar_url
        
        # Send request
        response = requests.post(self.config.webhook_url, json=payload, timeout=10)
        
        # Discord returns 204 No Content on success
        if response.status_code not in [200, 204]:
            response.raise_for_status()
        
        return NotificationResult(
            success=True,
            message_id=None,  # Discord webhooks don't return message ID
            metadata={"status_code": response.status_code},
        )
    
    def _send_file(self, message: NotificationMessage) -> NotificationResult:
        """Send a file message with embed."""
        if not message.file_path or not message.file_path.exists():
            raise FileNotFoundError(f"File not found: {message.file_path}")
        
        # Build embed for caption
        embed = {
            "title": message.title,
            "description": message.body,
            "timestamp": message.timestamp.isoformat(),
            "color": self._get_color_for_priority(message.priority),
            "footer": {
                "text": f"{message.notification_type.value.replace('_', ' ').title()}"
            },
        }
        
        # Prepare multipart form data
        payload = {
            "payload_json": {
                "embeds": [embed],
            }
        }
        
        # Add custom username and avatar if provided
        if self.config.username:
            payload["payload_json"]["username"] = self.config.username
        if self.config.avatar_url:
            payload["payload_json"]["avatar_url"] = self.config.avatar_url
        
        # Convert payload to JSON string
        import json
        files = {
            "payload_json": (None, json.dumps(payload["payload_json"]), "application/json"),
            "file": (message.file_path.name, open(message.file_path, "rb")),
        }
        
        try:
            # Send request
            response = requests.post(
                self.config.webhook_url,
                files=files,
                timeout=60,
            )
            
            # Discord returns 204 No Content on success
            if response.status_code not in [200, 204]:
                response.raise_for_status()
            
            return NotificationResult(
                success=True,
                message_id=None,  # Discord webhooks don't return message ID
                metadata={"status_code": response.status_code, "type": "file"},
            )
        finally:
            # Close file handle
            files["file"][1].close()
    
    def _get_color_for_priority(self, priority: NotificationPriority) -> int:
        """
        Get Discord embed color for priority level.
        
        Returns:
            Integer color value (Discord uses decimal RGB)
        """
        colors = {
            NotificationPriority.LOW: 0x95A5A6,      # Gray
            NotificationPriority.NORMAL: 0x3498DB,   # Blue
            NotificationPriority.HIGH: 0xF39C12,     # Orange
            NotificationPriority.CRITICAL: 0xE74C3C,  # Red
        }
        return colors.get(priority, 0x3498DB)  # Default to blue
    
    def _check_rate_limit(self) -> bool:
        """
        Check if rate limit allows sending.
        
        Returns:
            True if sending is allowed, False otherwise
        """
        now = datetime.now()
        cutoff = now - timedelta(seconds=self._token_window)
        
        # Remove expired tokens
        self._rate_limit_tokens = [
            t for t in self._rate_limit_tokens if t > cutoff
        ]
        
        # Check if we have capacity
        if len(self._rate_limit_tokens) >= self._max_tokens:
            return False
        
        # Add new token
        self._rate_limit_tokens.append(now)
        return True
    
    def _update_stats(
        self,
        message: NotificationMessage,
        success: bool,
        duration: float,
    ) -> None:
        """Update notification statistics."""
        self._stats.total_sent += 1
        
        if success:
            self._stats.successful += 1
        else:
            self._stats.failed += 1
        
        self._stats.total_duration += duration
        self._stats.last_sent = datetime.now()
        
        # Update by-type statistics
        msg_type = message.notification_type
        self._stats.by_type[msg_type] = self._stats.by_type.get(msg_type, 0) + 1
        
        # Update by-priority statistics
        priority = message.priority
        self._stats.by_priority[priority] = self._stats.by_priority.get(priority, 0) + 1
    
    def get_stats(self) -> NotificationStats:
        """
        Get notification statistics.
        
        Returns:
            NotificationStats object
        """
        with self._lock:
            return self._stats
    
    def reset_stats(self) -> None:
        """Reset notification statistics."""
        with self._lock:
            self._stats = NotificationStats()
    
    def test_connection(self) -> bool:
        """
        Test Discord webhook connection.
        
        Returns:
            True if connection is successful, False otherwise
        """
        try:
            # Send a minimal test message
            payload = {
                "content": "Test connection"
            }
            response = requests.post(self.config.webhook_url, json=payload, timeout=5)
            
            # Discord returns 204 No Content on success
            return response.status_code in [200, 204]
        except Exception:
            return False
