"""
Notification Models for Stream Processor Application

This module contains Django models for managing notifications,
including Telegram messages, email alerts, and other notification
channels. These models track notification history, delivery status,
and configuration settings.
"""

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from django.core.validators import MinValueValidator, MaxValueValidator
from apps.core.models import TimestampedModel, UUIDModel, StatusModel
from apps.core.validators import (
    validate_name_format, validate_json_configuration, validate_webhook_config,
    validate_email_config, validate_telegram_config, validate_template_content
)
import json


class NotificationChannel(TimestampedModel, UUIDModel):
    """
    Model representing a notification delivery channel.
    
    Notification channels define how and where notifications should be sent,
    including configuration for different service types like Telegram,
    email, webhooks, and other messaging platforms.
    
    Attributes:
        name (CharField): Human-readable name for the channel
        channel_type (CharField): Type of notification channel
        is_active (BooleanField): Whether the channel is currently active
        configuration (JSONField): Channel-specific configuration data
        rate_limit (PositiveIntegerField): Maximum messages per minute
        retry_attempts (PositiveIntegerField): Number of retry attempts
        timeout_seconds (PositiveIntegerField): Timeout for delivery attempts
        created_by (ForeignKey): User who created this channel
    """
    
    # Channel identification
    name = models.CharField(
        max_length=100,
        unique=True,
        validators=[validate_name_format],
        help_text="Human-readable name for the notification channel"
    )
    
    # Channel type configuration
    CHANNEL_TYPES = [
        ('telegram', 'Telegram Bot'),
        ('email', 'Email'),
        ('webhook', 'Webhook'),
        ('slack', 'Slack'),
        ('discord', 'Discord'),
        ('sms', 'SMS'),
    ]
    
    channel_type = models.CharField(
        max_length=20,
        choices=CHANNEL_TYPES,
        help_text="Type of notification channel"
    )
    
    # Channel status
    is_active = models.BooleanField(
        default=True,
        db_index=True,
        help_text="Whether this notification channel is active"
    )
    
    # Channel configuration (varies by type)
    configuration = models.JSONField(
        default=dict,
        help_text="Channel-specific configuration parameters"
    )
    
    # Delivery settings
    rate_limit = models.PositiveIntegerField(
        default=30,
        validators=[MinValueValidator(1), MaxValueValidator(1000)],
        help_text="Maximum number of messages per minute"
    )
    
    retry_attempts = models.PositiveIntegerField(
        default=3,
        validators=[MinValueValidator(0), MaxValueValidator(10)],
        help_text="Number of retry attempts for failed deliveries"
    )
    
    timeout_seconds = models.PositiveIntegerField(
        default=30,
        validators=[MinValueValidator(5), MaxValueValidator(300)],
        help_text="Timeout for delivery attempts in seconds"
    )
    
    # User association
    created_by = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='notification_channels',
        help_text="User who created this notification channel"
    )
    
    class Meta:
        verbose_name = "Notification Channel"
        verbose_name_plural = "Notification Channels"
        ordering = ['name']
        indexes = [
            models.Index(fields=['channel_type']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self):
        """String representation of the notification channel."""
        return f"{self.name} ({self.channel_type})"
    
    def get_telegram_config(self):
        """Get Telegram-specific configuration."""
        if self.channel_type == 'telegram':
            return {
                'bot_token': self.configuration.get('bot_token', ''),
                'chat_id': self.configuration.get('chat_id', ''),
            }
        return {}
    
    def get_email_config(self):
        """Get email-specific configuration."""
        if self.channel_type == 'email':
            return {
                'recipients': self.configuration.get('recipients', []),
                'subject_prefix': self.configuration.get('subject_prefix', '[Stream Processor]'),
            }
        return {}
    
    def get_webhook_config(self):
        """Get webhook-specific configuration."""
        if self.channel_type == 'webhook':
            return {
                'url': self.configuration.get('url', ''),
                'headers': self.configuration.get('headers', {}),
                'method': self.configuration.get('method', 'POST'),
            }
        return {}


class NotificationTemplate(TimestampedModel, UUIDModel):
    """
    Model for notification message templates.
    
    Templates define reusable message formats for different types
    of notifications, allowing for consistent messaging and easy
    customization of notification content.
    
    Attributes:
        name (CharField): Name for the template
        template_type (CharField): Type of notification this template is for
        subject_template (CharField): Subject line template (for email)
        message_template (TextField): Main message content template
        is_active (BooleanField): Whether the template is active
        variables (JSONField): Available template variables
        channel_types (JSONField): Compatible channel types
    """
    
    # Template identification
    name = models.CharField(
        max_length=100,
        unique=True,
        help_text="Name for this notification template"
    )
    
    # Template categorization
    TEMPLATE_TYPES = [
        ('jingle_detected', 'Jingle Detected'),
        ('ad_break_started', 'Ad Break Started'),
        ('ad_break_ended', 'Ad Break Ended'),
        ('stream_started', 'Stream Started'),
        ('stream_stopped', 'Stream Stopped'),
        ('stream_error', 'Stream Error'),
        ('system_alert', 'System Alert'),
        ('health_check', 'Health Check'),
    ]
    
    template_type = models.CharField(
        max_length=30,
        choices=TEMPLATE_TYPES,
        help_text="Type of notification this template is designed for"
    )
    
    # Template content
    subject_template = models.CharField(
        max_length=200,
        blank=True,
        help_text="Subject line template (used for email notifications)"
    )
    
    message_template = models.TextField(
        help_text="Main message content template with variable placeholders"
    )
    
    # Template status
    is_active = models.BooleanField(
        default=True,
        db_index=True,
        help_text="Whether this template is active for use"
    )
    
    # Template metadata
    variables = models.JSONField(
        default=list,
        help_text="List of available variables for this template"
    )
    
    channel_types = models.JSONField(
        default=list,
        help_text="List of compatible notification channel types"
    )
    
    class Meta:
        verbose_name = "Notification Template"
        verbose_name_plural = "Notification Templates"
        ordering = ['template_type', 'name']
        indexes = [
            models.Index(fields=['template_type']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self):
        """String representation of the notification template."""
        return f"{self.name} ({self.template_type})"
    
    def render_message(self, context):
        """
        Render the message template with provided context variables.
        
        Args:
            context (dict): Variables to substitute in the template
            
        Returns:
            str: Rendered message content
        """
        try:
            return self.message_template.format(**context)
        except KeyError as e:
            return f"Template error: Missing variable {e}"
    
    def render_subject(self, context):
        """
        Render the subject template with provided context variables.
        
        Args:
            context (dict): Variables to substitute in the template
            
        Returns:
            str: Rendered subject line
        """
        if not self.subject_template:
            return f"[{self.template_type}] Notification"
        try:
            return self.subject_template.format(**context)
        except KeyError as e:
            return f"Subject error: Missing variable {e}"


class Notification(TimestampedModel, UUIDModel, StatusModel):
    """
    Model representing an individual notification instance.
    
    This model tracks individual notification messages that have been
    sent or are pending delivery, including their content, delivery
    status, and any error information.
    
    Attributes:
        channel (ForeignKey): Notification channel to use for delivery
        template (ForeignKey): Template used for this notification
        recipient (CharField): Target recipient identifier
        subject (CharField): Notification subject line
        message (TextField): Notification message content
        context_data (JSONField): Variables used for template rendering
        scheduled_at (DateTimeField): When the notification should be sent
        sent_at (DateTimeField): When the notification was actually sent
        delivery_attempts (PositiveIntegerField): Number of delivery attempts
        error_message (TextField): Last error message if delivery failed
        external_id (CharField): External service message ID
    """
    
    # Notification configuration
    channel = models.ForeignKey(
        NotificationChannel,
        on_delete=models.CASCADE,
        related_name='notifications',
        help_text="Notification channel to use for delivery"
    )
    
    template = models.ForeignKey(
        NotificationTemplate,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='notifications',
        help_text="Template used to generate this notification"
    )
    
    # Delivery information
    recipient = models.CharField(
        max_length=200,
        help_text="Target recipient identifier (email, chat ID, etc.)"
    )
    
    subject = models.CharField(
        max_length=200,
        blank=True,
        help_text="Notification subject line"
    )
    
    message = models.TextField(
        help_text="Notification message content"
    )
    
    # Template context
    context_data = models.JSONField(
        default=dict,
        blank=True,
        help_text="Variables used for template rendering"
    )
    
    # Scheduling and delivery tracking
    scheduled_at = models.DateTimeField(
        default=timezone.now,
        help_text="When this notification should be sent"
    )
    
    sent_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When this notification was actually sent"
    )
    
    delivery_attempts = models.PositiveIntegerField(
        default=0,
        help_text="Number of delivery attempts made"
    )
    
    error_message = models.TextField(
        blank=True,
        help_text="Last error message if delivery failed"
    )
    
    external_id = models.CharField(
        max_length=100,
        blank=True,
        help_text="External service message ID for tracking"
    )
    
    class Meta:
        verbose_name = "Notification"
        verbose_name_plural = "Notifications"
        ordering = ['-scheduled_at']
        indexes = [
            models.Index(fields=['channel', 'status']),
            models.Index(fields=['scheduled_at']),
            models.Index(fields=['sent_at']),
            models.Index(fields=['status']),
        ]
    
    def __str__(self):
        """String representation of the notification."""
        return f"{self.channel.name} - {self.subject or self.message[:50]}"
    
    def is_pending(self):
        """Check if the notification is pending delivery."""
        return self.status == 'pending' and not self.sent_at
    
    def is_sent(self):
        """Check if the notification has been successfully sent."""
        return self.status == 'completed' and self.sent_at is not None
    
    def is_failed(self):
        """Check if the notification delivery has failed."""
        return self.status == 'failed'
    
    def mark_sent(self, external_id=None):
        """Mark the notification as successfully sent."""
        self.status = 'completed'
        self.sent_at = timezone.now()
        if external_id:
            self.external_id = external_id
        self.save(update_fields=['status', 'sent_at', 'external_id'])
    
    def mark_failed(self, error_message):
        """Mark the notification as failed with error message."""
        self.status = 'failed'
        self.error_message = error_message
        self.delivery_attempts += 1
        self.save(update_fields=['status', 'error_message', 'delivery_attempts'])
    
    def can_retry(self):
        """Check if the notification can be retried."""
        return (
            self.status == 'failed' and
            self.delivery_attempts < self.channel.retry_attempts
        )


class NotificationRule(TimestampedModel, UUIDModel):
    """
    Model for automatic notification rules.
    
    Rules define when and how notifications should be automatically
    triggered based on system events, allowing for automated alerting
    and monitoring without manual intervention.
    
    Attributes:
        name (CharField): Name for the notification rule
        event_type (CharField): Type of event that triggers this rule
        channel (ForeignKey): Default channel for notifications
        template (ForeignKey): Default template for notifications
        is_active (BooleanField): Whether the rule is currently active
        conditions (JSONField): Conditions that must be met for triggering
        throttle_minutes (PositiveIntegerField): Minimum time between notifications
        last_triggered (DateTimeField): When this rule was last triggered
        priority (IntegerField): Rule priority for ordering
    """
    
    # Rule identification
    name = models.CharField(
        max_length=100,
        unique=True,
        help_text="Name for this notification rule"
    )
    
    # Event configuration
    EVENT_TYPES = [
        ('jingle_detected', 'Jingle Detected'),
        ('ad_break_started', 'Ad Break Started'),
        ('ad_break_ended', 'Ad Break Ended'),
        ('stream_started', 'Stream Started'),
        ('stream_stopped', 'Stream Stopped'),
        ('stream_error', 'Stream Error'),
        ('high_error_rate', 'High Error Rate'),
        ('system_startup', 'System Startup'),
        ('system_shutdown', 'System Shutdown'),
    ]
    
    event_type = models.CharField(
        max_length=30,
        choices=EVENT_TYPES,
        help_text="Type of event that triggers this notification rule"
    )
    
    # Default notification configuration
    channel = models.ForeignKey(
        NotificationChannel,
        on_delete=models.CASCADE,
        related_name='rules',
        help_text="Default notification channel for this rule"
    )
    
    template = models.ForeignKey(
        NotificationTemplate,
        on_delete=models.CASCADE,
        related_name='rules',
        help_text="Default notification template for this rule"
    )
    
    # Rule status and behavior
    is_active = models.BooleanField(
        default=True,
        db_index=True,
        help_text="Whether this notification rule is active"
    )
    
    conditions = models.JSONField(
        default=dict,
        help_text="Additional conditions that must be met for triggering"
    )
    
    # Throttling and control
    throttle_minutes = models.PositiveIntegerField(
        default=5,
        validators=[MinValueValidator(0), MaxValueValidator(1440)],
        help_text="Minimum minutes between notifications from this rule"
    )
    
    last_triggered = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When this rule was last triggered"
    )
    
    priority = models.IntegerField(
        default=0,
        help_text="Rule priority for ordering (higher numbers = higher priority)"
    )
    
    class Meta:
        verbose_name = "Notification Rule"
        verbose_name_plural = "Notification Rules"
        ordering = ['-priority', 'name']
        indexes = [
            models.Index(fields=['event_type']),
            models.Index(fields=['is_active']),
            models.Index(fields=['priority']),
        ]
    
    def __str__(self):
        """String representation of the notification rule."""
        return f"{self.name} ({self.event_type})"
    
    def can_trigger(self):
        """Check if the rule can be triggered based on throttling."""
        if not self.is_active:
            return False
        
        if not self.last_triggered:
            return True
        
        time_since_last = timezone.now() - self.last_triggered
        return time_since_last.total_seconds() >= (self.throttle_minutes * 60)
    
    def mark_triggered(self):
        """Mark the rule as triggered and update timestamp."""
        self.last_triggered = timezone.now()
        self.save(update_fields=['last_triggered'])
    
    def check_conditions(self, event_data):
        """
        Check if event data meets the rule conditions.
        
        Args:
            event_data (dict): Event data to check against conditions
            
        Returns:
            bool: True if conditions are met, False otherwise
        """
        if not self.conditions:
            return True
        
        # Implement condition checking logic based on your requirements
        # This is a simplified example
        for key, expected_value in self.conditions.items():
            if key not in event_data:
                return False
            if event_data[key] != expected_value:
                return False
        
        return True
