"""
Telegram notification service implementation.

This module provides integration with Telegram Bot API 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 (
    TelegramConfig,
    NotificationMessage,
    NotificationResult,
    NotificationStats,
    NotificationPriority,
    NotificationType,
    MessageFormat,
)


class TelegramNotifier:
    """
    Telegram notification service.
    
    Sends notifications via Telegram Bot API with support for:
    - Text messages with Markdown/HTML formatting
    - Images
    - Files
    - Retry mechanism
    - Rate limiting
    
    Thread-safe implementation.
    """
    
    def __init__(self, config: TelegramConfig):
        """
        Initialize Telegram notifier.
        
        Args:
            config: Telegram configuration
        """
        self.config = config
        self._lock = RLock()
        self._stats = NotificationStats()
        self._rate_limit_tokens: List[datetime] = []
        self._max_tokens = 30  # 30 messages per minute
        self._token_window = 60  # seconds
        
        # API endpoints
        self.base_url = f"https://api.telegram.org/bot{config.bot_token}"
        self.send_message_url = f"{self.base_url}/sendMessage"
        self.send_photo_url = f"{self.base_url}/sendPhoto"
        self.send_document_url = f"{self.base_url}/sendDocument"
    
    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.image_url:
                        result = self._send_photo(message)
                    elif message.file_path:
                        result = self._send_file(message)
                    else:
                        result = self._send_text(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_text(self, message: NotificationMessage) -> NotificationResult:
        """Send a text message."""
        # Format message
        text = message.format_message()
        
        # Prepare request payload
        payload = {
            "chat_id": self.config.chat_id,
            "text": text,
            "disable_notification": self.config.disable_notification,
            "disable_web_page_preview": self.config.disable_web_page_preview,
        }
        
        # Add parse mode if not plain text
        if message.format != MessageFormat.PLAIN:
            payload["parse_mode"] = self.config.parse_mode
        
        # Send request
        response = requests.post(self.send_message_url, json=payload, timeout=10)
        response.raise_for_status()
        
        data = response.json()
        
        if not data.get("ok"):
            raise Exception(f"Telegram API error: {data.get('description')}")
        
        message_id = str(data["result"]["message_id"])
        
        return NotificationResult(
            success=True,
            message_id=message_id,
            metadata={"response": data},
        )
    
    def _send_photo(self, message: NotificationMessage) -> NotificationResult:
        """Send a photo message."""
        # Format caption
        caption = message.format_message()
        
        # Prepare request payload
        payload = {
            "chat_id": self.config.chat_id,
            "photo": message.image_url,
            "caption": caption,
            "disable_notification": self.config.disable_notification,
        }
        
        # Add parse mode if not plain text
        if message.format != MessageFormat.PLAIN:
            payload["parse_mode"] = self.config.parse_mode
        
        # Send request
        response = requests.post(self.send_photo_url, json=payload, timeout=30)
        response.raise_for_status()
        
        data = response.json()
        
        if not data.get("ok"):
            raise Exception(f"Telegram API error: {data.get('description')}")
        
        message_id = str(data["result"]["message_id"])
        
        return NotificationResult(
            success=True,
            message_id=message_id,
            metadata={"response": data, "type": "photo"},
        )
    
    def _send_file(self, message: NotificationMessage) -> NotificationResult:
        """Send a file message."""
        if not message.file_path or not message.file_path.exists():
            raise FileNotFoundError(f"File not found: {message.file_path}")
        
        # Format caption
        caption = message.format_message()
        
        # Prepare request payload
        data = {
            "chat_id": self.config.chat_id,
            "caption": caption,
            "disable_notification": self.config.disable_notification,
        }
        
        # Add parse mode if not plain text
        if message.format != MessageFormat.PLAIN:
            data["parse_mode"] = self.config.parse_mode
        
        # Send request with file
        with open(message.file_path, "rb") as f:
            files = {"document": f}
            response = requests.post(
                self.send_document_url,
                data=data,
                files=files,
                timeout=60,
            )
        
        response.raise_for_status()
        
        response_data = response.json()
        
        if not response_data.get("ok"):
            raise Exception(f"Telegram API error: {response_data.get('description')}")
        
        message_id = str(response_data["result"]["message_id"])
        
        return NotificationResult(
            success=True,
            message_id=message_id,
            metadata={"response": response_data, "type": "document"},
        )
    
    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 Telegram bot connection.
        
        Returns:
            True if connection is successful, False otherwise
        """
        try:
            url = f"{self.base_url}/getMe"
            response = requests.get(url, timeout=5)
            response.raise_for_status()
            data = response.json()
            return data.get("ok", False)
        except Exception:
            return False
