"""
Channel Schedule Management Models

This module contains models for managing channel broadcasting schedules:
- ChannelSchedule: Scheduled programming and events for channels

These models handle the scheduling of content, maintenance windows,
special events, and test broadcasts for television channels.
"""

from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError

from apps.common.models import BaseModel
from apps.channels.models import Channel


class ChannelSchedule(BaseModel):
    """
    Channel broadcasting schedule management.
    
    Manages scheduled programming, maintenance windows, special events,
    and test broadcasts for television channels. Supports different
    types of scheduled content and ad insertion settings.
    
    Attributes:
        channel: Foreign key to the channel this schedule belongs to
        title: Name/title of the scheduled content or event
        schedule_type: Type of scheduled content (regular, special, maintenance, test)
        start_time: When the scheduled content begins
        end_time: When the scheduled content ends
        description: Detailed description of the scheduled content
        content_url: Primary URL for the scheduled content
        backup_content_url: Fallback URL if primary content fails
        allow_ads: Whether ads are allowed during this scheduled time
        ad_break_duration: Duration of ad breaks in seconds
        is_active: Whether this schedule entry is currently active
        
    Related Models:
        - Channel: Many-to-one relationship (channel can have multiple schedules)
    """
    
    SCHEDULE_TYPES = [
        ("regular", _("Regular Programming")),
        ("special", _("Special Event")),
        ("maintenance", _("Maintenance Window")),
        ("test", _("Test Broadcast")),
        ("emergency", _("Emergency Broadcast")),
        ("replay", _("Replay/Rerun")),
    ]
    
    # Relationships
    channel = models.ForeignKey(
        Channel,
        on_delete=models.CASCADE,
        related_name="schedules",
        verbose_name=_("Channel"),
        help_text=_("Channel this schedule entry belongs to")
    )
    
    # Basic Information
    title = models.CharField(
        max_length=255,
        verbose_name=_("Schedule Title"),
        help_text=_("Name or title of the scheduled content or event")
    )
    schedule_type = models.CharField(
        max_length=20,
        choices=SCHEDULE_TYPES,
        default="regular",
        verbose_name=_("Schedule Type"),
        help_text=_("Type of scheduled content or event")
    )
    
    # Timing
    start_time = models.DateTimeField(
        verbose_name=_("Start Time"),
        help_text=_("When the scheduled content begins")
    )
    end_time = models.DateTimeField(
        verbose_name=_("End Time"),
        help_text=_("When the scheduled content ends")
    )
    
    # Content Details
    description = models.TextField(
        blank=True,
        verbose_name=_("Description"),
        help_text=_("Detailed description of the scheduled content")
    )
    content_url = models.URLField(
        blank=True,
        verbose_name=_("Content URL"),
        help_text=_("Primary URL for the scheduled content stream")
    )
    backup_content_url = models.URLField(
        blank=True,
        verbose_name=_("Backup Content URL"),
        help_text=_("Fallback URL if primary content stream fails")
    )
    
    # Content Metadata
    content_rating = models.CharField(
        max_length=10,
        blank=True,
        verbose_name=_("Content Rating"),
        help_text=_("Content rating (G, PG, PG-13, R, etc.)")
    )
    language = models.CharField(
        max_length=50,
        blank=True,
        verbose_name=_("Language"),
        help_text=_("Primary language of the scheduled content")
    )
    
    # Ad Settings
    allow_ads = models.BooleanField(
        default=True,
        verbose_name=_("Allow Advertisements"),
        help_text=_("Whether ads are allowed during this scheduled time")
    )
    ad_break_duration = models.PositiveIntegerField(
        default=120,
        verbose_name=_("Ad Break Duration"),
        help_text=_("Duration of ad breaks in seconds")
    )
    max_ad_breaks_per_hour = models.PositiveIntegerField(
        default=4,
        verbose_name=_("Max Ad Breaks Per Hour"),
        help_text=_("Maximum number of ad breaks allowed per hour")
    )
    
    # Priority and Overrides
    priority = models.PositiveIntegerField(
        default=1,
        verbose_name=_("Priority"),
        help_text=_("Priority level for schedule conflicts (higher = more important)")
    )
    can_be_preempted = models.BooleanField(
        default=True,
        verbose_name=_("Can Be Preempted"),
        help_text=_("Whether this schedule can be overridden by higher priority content")
    )
    
    # Status and Control
    is_active = models.BooleanField(
        default=True,
        verbose_name=_("Is Active"),
        help_text=_("Whether this schedule entry is currently active")
    )
    auto_start = models.BooleanField(
        default=True,
        verbose_name=_("Auto Start"),
        help_text=_("Whether content should start automatically at the scheduled time")
    )
    auto_end = models.BooleanField(
        default=True,
        verbose_name=_("Auto End"),
        help_text=_("Whether content should end automatically at the scheduled time")
    )
    
    # Notifications and Alerts
    send_start_notification = models.BooleanField(
        default=False,
        verbose_name=_("Send Start Notification"),
        help_text=_("Send notification when content starts")
    )
    send_end_notification = models.BooleanField(
        default=False,
        verbose_name=_("Send End Notification"),
        help_text=_("Send notification when content ends")
    )
    notification_recipients = models.JSONField(
        default=list,
        blank=True,
        verbose_name=_("Notification Recipients"),
        help_text=_("List of email addresses or user IDs to notify")
    )
    
    # Tracking and Metadata
    actual_start_time = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_("Actual Start Time"),
        help_text=_("When the content actually started (for tracking)")
    )
    actual_end_time = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_("Actual End Time"),
        help_text=_("When the content actually ended (for tracking)")
    )
    
    # Additional metadata
    metadata = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_("Metadata"),
        help_text=_("Additional metadata for the scheduled content")
    )
    
    class Meta:
        db_table = "channel_schedules"
        ordering = ["channel", "start_time"]
        verbose_name = _("Channel Schedule")
        verbose_name_plural = _("Channel Schedules")
        indexes = [
            models.Index(fields=["channel", "start_time"]),
            models.Index(fields=["start_time", "end_time"]),
            models.Index(fields=["schedule_type", "is_active"]),
            models.Index(fields=["priority", "start_time"]),
        ]
    
    def __str__(self):
        return f"{self.title} on {self.channel.name} ({self.start_time})"
    
    def clean(self):
        """
        Validate the schedule entry.
        
        Raises:
            ValidationError: If validation fails
        """
        super().clean()
        
        # Validate time range
        if self.start_time and self.end_time:
            if self.start_time >= self.end_time:
                raise ValidationError({
                    'end_time': _('End time must be after start time.')
                })
        
        # Validate ad break settings
        if self.allow_ads and self.ad_break_duration <= 0:
            raise ValidationError({
                'ad_break_duration': _('Ad break duration must be positive if ads are allowed.')
            })
        
        # Validate priority
        if self.priority <= 0:
            raise ValidationError({
                'priority': _('Priority must be a positive integer.')
            })
    
    @property
    def duration(self):
        """
        Get the scheduled duration in seconds.
        
        Returns:
            float: Duration in seconds, or 0 if times not set
        """
        if self.start_time and self.end_time:
            return (self.end_time - self.start_time).total_seconds()
        return 0
    
    @property
    def duration_minutes(self):
        """Get the scheduled duration in minutes."""
        return self.duration / 60
    
    @property
    def duration_hours(self):
        """Get the scheduled duration in hours."""
        return self.duration / 3600
    
    @property
    def is_currently_active(self):
        """
        Check if the schedule is currently active.
        
        Returns:
            bool: True if schedule is active and within time range
        """
        if not self.is_active:
            return False
            
        now = timezone.now()
        return self.start_time <= now <= self.end_time
    
    @property
    def is_upcoming(self):
        """Check if the schedule is upcoming (future)."""
        now = timezone.now()
        return self.is_active and self.start_time > now
    
    @property
    def is_past(self):
        """Check if the schedule is in the past."""
        now = timezone.now()
        return self.end_time < now
    
    @property
    def actual_duration(self):
        """
        Get the actual duration based on actual start/end times.
        
        Returns:
            float: Actual duration in seconds, or None if not available
        """
        if self.actual_start_time and self.actual_end_time:
            return (self.actual_end_time - self.actual_start_time).total_seconds()
        return None
    
    def mark_started(self, start_time=None):
        """
        Mark the schedule as started.
        
        Args:
            start_time: Optional custom start time, defaults to now
        """
        self.actual_start_time = start_time or timezone.now()
        self.save(update_fields=['actual_start_time'])
    
    def mark_ended(self, end_time=None):
        """
        Mark the schedule as ended.
        
        Args:
            end_time: Optional custom end time, defaults to now
        """
        self.actual_end_time = end_time or timezone.now()
        self.save(update_fields=['actual_end_time'])
    
    def get_overlapping_schedules(self):
        """
        Get other schedules that overlap with this one.
        
        Returns:
            QuerySet: Overlapping schedule entries for the same channel
        """
        return ChannelSchedule.objects.filter(
            channel=self.channel,
            is_active=True
        ).exclude(
            id=self.id
        ).filter(
            models.Q(
                start_time__lt=self.end_time,
                end_time__gt=self.start_time
            )
        )
    
    def has_conflicts(self):
        """
        Check if this schedule has conflicts with other schedules.
        
        Returns:
            bool: True if there are conflicting schedules
        """
        return self.get_overlapping_schedules().exists()
    
    def get_status(self):
        """
        Get the current status of this schedule.
        
        Returns:
            str: Current status (upcoming, active, ended, inactive)
        """
        if not self.is_active:
            return 'inactive'
        
        now = timezone.now()
        
        if self.end_time < now:
            return 'ended'
        elif self.start_time <= now <= self.end_time:
            return 'active'
        else:
            return 'upcoming'
    
    def calculate_ad_opportunities(self):
        """
        Calculate ad insertion opportunities for this schedule.
        
        Returns:
            list: List of timestamps where ads can be inserted
        """
        if not self.allow_ads or self.duration <= 0:
            return []
        
        opportunities = []
        duration_seconds = int(self.duration)
        max_breaks = int(self.duration_hours * self.max_ad_breaks_per_hour)
        
        if max_breaks <= 0:
            return opportunities
        
        # Calculate intervals between ad breaks
        interval = duration_seconds / (max_breaks + 1)
        
        for i in range(1, max_breaks + 1):
            opportunity_time = self.start_time + timezone.timedelta(seconds=int(i * interval))
            opportunities.append(opportunity_time)
        
        return opportunities
    
    def get_schedule_summary(self):
        """
        Get a summary of this schedule.
        
        Returns:
            dict: Summary information about the schedule
        """
        return {
            'title': self.title,
            'channel': self.channel.name,
            'type': self.get_schedule_type_display(),
            'start_time': self.start_time,
            'end_time': self.end_time,
            'duration_minutes': self.duration_minutes,
            'status': self.get_status(),
            'allow_ads': self.allow_ads,
            'priority': self.priority,
            'has_conflicts': self.has_conflicts(),
        }