"""Models for EPG (Electronic Program Guide) app.

This module defines models for:
- Electronic Program Guide entries
- TV show schedules and metadata
- Program categories and genres
- Broadcast schedules
- Content impressions and analytics
"""

import uuid
from django.db import models
from django.utils import timezone
from django.core.validators import MinValueValidator, MaxValueValidator
from django.contrib.auth import get_user_model

from apps.core.models import TimeStampedModel, UUIDModel, SoftDeleteModel
from apps.channels.models import Channel

User = get_user_model()


class ProgramGenre(TimeStampedModel, SoftDeleteModel):
    """Program genre classification."""
    
    name = models.CharField(
        max_length=100,
        unique=True,
        help_text="Genre name (e.g., Drama, Comedy, News)"
    )
    description = models.TextField(
        blank=True,
        help_text="Detailed description of the genre"
    )
    parent_genre = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='sub_genres',
        help_text="Parent genre for hierarchical classification"
    )
    color_code = models.CharField(
        max_length=7,
        blank=True,
        help_text="Hex color code for UI display"
    )
    
    class Meta:
        db_table = 'epg_program_genres'
        verbose_name = 'Program Genre'
        verbose_name_plural = 'Program Genres'
        ordering = ['name']
    
    def __str__(self):
        return self.name
    
    @property
    def full_path(self):
        """Get full hierarchical path of genre."""
        if self.parent_genre:
            return f"{self.parent_genre.full_path} > {self.name}"
        return self.name


class ProgramCategory(TimeStampedModel, SoftDeleteModel):
    """Program category classification."""
    
    name = models.CharField(
        max_length=100,
        unique=True,
        help_text="Category name (e.g., Entertainment, Sports, Education)"
    )
    description = models.TextField(
        blank=True,
        help_text="Detailed description of the category"
    )
    parent_category = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='sub_categories',
        help_text="Parent category for hierarchical classification"
    )
    
    class Meta:
        db_table = 'epg_program_categories'
        verbose_name = 'Program Category'
        verbose_name_plural = 'Program Categories'
        ordering = ['name']
    
    def __str__(self):
        return self.name


class Program(UUIDModel, TimeStampedModel, SoftDeleteModel):
    """TV Program/Show definition."""
    
    CONTENT_RATING_CHOICES = [
        ('G', 'General Audiences'),
        ('PG', 'Parental Guidance'),
        ('PG13', 'Parents Strongly Cautioned'),
        ('R', 'Restricted'),
        ('NC17', 'Adults Only'),
        ('TV-Y', 'All Children'),
        ('TV-Y7', 'Children 7+'),
        ('TV-G', 'General Audience'),
        ('TV-PG', 'Parental Guidance'),
        ('TV-14', 'Parents Strongly Cautioned'),
        ('TV-MA', 'Mature Audiences'),
    ]
    
    name = models.CharField(
        max_length=255,
        help_text="Program/show name"
    )
    original_title = models.CharField(
        max_length=255,
        blank=True,
        help_text="Original title if different from name"
    )
    description = models.TextField(
        blank=True,
        help_text="Program description/synopsis"
    )
    short_description = models.CharField(
        max_length=500,
        blank=True,
        help_text="Short description for listings"
    )
    genre = models.ForeignKey(
        ProgramGenre,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='programs'
    )
    category = models.ForeignKey(
        ProgramCategory,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='programs'
    )
    content_rating = models.CharField(
        max_length=10,
        choices=CONTENT_RATING_CHOICES,
        blank=True,
        help_text="Content rating/age classification"
    )
    duration_minutes = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Typical duration in minutes"
    )
    production_year = models.PositiveIntegerField(
        null=True,
        blank=True,
        validators=[MinValueValidator(1900), MaxValueValidator(2100)],
        help_text="Year of production"
    )
    country_of_origin = models.CharField(
        max_length=100,
        blank=True,
        help_text="Country where the program was produced"
    )
    language = models.CharField(
        max_length=50,
        blank=True,
        help_text="Primary language of the program"
    )
    director = models.CharField(
        max_length=255,
        blank=True,
        help_text="Director(s) of the program"
    )
    cast = models.TextField(
        blank=True,
        help_text="Main cast members"
    )
    producer = models.CharField(
        max_length=255,
        blank=True,
        help_text="Producer(s) of the program"
    )
    series_id = models.CharField(
        max_length=100,
        blank=True,
        help_text="Series identifier for episodic content"
    )
    season_number = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Season number for series"
    )
    episode_number = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Episode number within season"
    )
    is_series = models.BooleanField(
        default=False,
        help_text="Whether this is part of a series"
    )
    is_live = models.BooleanField(
        default=False,
        help_text="Whether this is live content"
    )
    is_repeat = models.BooleanField(
        default=False,
        help_text="Whether this is a repeat broadcast"
    )
    poster_image = models.ImageField(
        upload_to='epg/posters/',
        null=True,
        blank=True,
        help_text="Program poster/thumbnail image"
    )
    banner_image = models.ImageField(
        upload_to='epg/banners/',
        null=True,
        blank=True,
        help_text="Program banner image"
    )
    external_id = models.CharField(
        max_length=100,
        blank=True,
        help_text="External system identifier"
    )
    metadata = models.JSONField(
        default=dict,
        blank=True,
        help_text="Additional program metadata"
    )
    
    class Meta:
        db_table = 'epg_programs'
        verbose_name = 'Program'
        verbose_name_plural = 'Programs'
        ordering = ['name']
        indexes = [
            models.Index(fields=['name']),
            models.Index(fields=['genre']),
            models.Index(fields=['category']),
            models.Index(fields=['series_id']),
            models.Index(fields=['external_id']),
        ]
    
    def __str__(self):
        if self.is_series and self.season_number and self.episode_number:
            return f"{self.name} S{self.season_number:02d}E{self.episode_number:02d}"
        return self.name
    
    @property
    def full_title(self):
        """Get full title including series/episode info."""
        title = self.name
        if self.is_series:
            if self.season_number and self.episode_number:
                title += f" - Season {self.season_number}, Episode {self.episode_number}"
            elif self.episode_number:
                title += f" - Episode {self.episode_number}"
        return title


class EpgEntry(UUIDModel, TimeStampedModel, SoftDeleteModel):
    """Electronic Program Guide entry - scheduled broadcast."""
    
    STATUS_CHOICES = [
        ('scheduled', 'Scheduled'),
        ('live', 'Live'),
        ('completed', 'Completed'),
        ('cancelled', 'Cancelled'),
        ('delayed', 'Delayed'),
    ]
    
    channel = models.ForeignKey(
        Channel,
        on_delete=models.CASCADE,
        related_name='epg_entries',
        help_text="Channel broadcasting this program"
    )
    program = models.ForeignKey(
        Program,
        on_delete=models.CASCADE,
        related_name='epg_entries',
        help_text="Program being broadcast"
    )
    start_time = models.DateTimeField(
        help_text="Scheduled start time"
    )
    end_time = models.DateTimeField(
        help_text="Scheduled end time"
    )
    actual_start_time = models.DateTimeField(
        null=True,
        blank=True,
        help_text="Actual start time (if different from scheduled)"
    )
    actual_end_time = models.DateTimeField(
        null=True,
        blank=True,
        help_text="Actual end time (if different from scheduled)"
    )
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='scheduled'
    )
    title_override = models.CharField(
        max_length=255,
        blank=True,
        help_text="Override title for this specific broadcast"
    )
    description_override = models.TextField(
        blank=True,
        help_text="Override description for this specific broadcast"
    )
    is_premiere = models.BooleanField(
        default=False,
        help_text="Whether this is a premiere broadcast"
    )
    is_finale = models.BooleanField(
        default=False,
        help_text="Whether this is a finale broadcast"
    )
    is_special = models.BooleanField(
        default=False,
        help_text="Whether this is a special broadcast"
    )
    audio_language = models.CharField(
        max_length=50,
        blank=True,
        help_text="Audio language for this broadcast"
    )
    subtitle_language = models.CharField(
        max_length=50,
        blank=True,
        help_text="Subtitle language available"
    )
    has_closed_captions = models.BooleanField(
        default=False,
        help_text="Whether closed captions are available"
    )
    has_audio_description = models.BooleanField(
        default=False,
        help_text="Whether audio description is available"
    )
    external_id = models.CharField(
        max_length=100,
        blank=True,
        help_text="External system identifier"
    )
    notes = models.TextField(
        blank=True,
        help_text="Internal notes about this broadcast"
    )
    metadata = models.JSONField(
        default=dict,
        blank=True,
        help_text="Additional broadcast metadata"
    )
    
    class Meta:
        db_table = 'epg_entries'
        verbose_name = 'EPG Entry'
        verbose_name_plural = 'EPG Entries'
        ordering = ['start_time']
        indexes = [
            models.Index(fields=['channel', 'start_time']),
            models.Index(fields=['start_time', 'end_time']),
            models.Index(fields=['status']),
            models.Index(fields=['external_id']),
        ]
        constraints = [
            models.CheckConstraint(
                check=models.Q(end_time__gt=models.F('start_time')),
                name='epg_entry_end_after_start'
            ),
        ]
    
    def __str__(self):
        return f"{self.channel.name} - {self.display_title} ({self.start_time})"
    
    @property
    def display_title(self):
        """Get display title (override or program title)."""
        return self.title_override or self.program.name
    
    @property
    def display_description(self):
        """Get display description (override or program description)."""
        return self.description_override or self.program.description
    
    @property
    def duration_minutes(self):
        """Get scheduled duration in minutes."""
        if self.start_time and self.end_time:
            delta = self.end_time - self.start_time
            return int(delta.total_seconds() / 60)
        return None
    
    @property
    def actual_duration_minutes(self):
        """Get actual duration in minutes."""
        if self.actual_start_time and self.actual_end_time:
            delta = self.actual_end_time - self.actual_start_time
            return int(delta.total_seconds() / 60)
        return None
    
    @property
    def is_currently_airing(self):
        """Check if program is currently airing."""
        now = timezone.now()
        start = self.actual_start_time or self.start_time
        end = self.actual_end_time or self.end_time
        return start <= now <= end
    
    def clean(self):
        """Validate EPG entry data."""
        from django.core.exceptions import ValidationError
        
        if self.start_time and self.end_time:
            if self.end_time <= self.start_time:
                raise ValidationError("End time must be after start time")
        
        if self.actual_start_time and self.actual_end_time:
            if self.actual_end_time <= self.actual_start_time:
                raise ValidationError("Actual end time must be after actual start time")


class EpgImpressions(TimeStampedModel):
    """Track impressions/viewership for EPG entries."""
    
    epg_entry = models.ForeignKey(
        EpgEntry,
        on_delete=models.CASCADE,
        related_name='impressions',
        help_text="EPG entry being tracked"
    )
    timestamp = models.DateTimeField(
        help_text="When the impression was recorded"
    )
    viewer_count = models.PositiveIntegerField(
        default=0,
        help_text="Number of viewers at this timestamp"
    )
    rating = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        null=True,
        blank=True,
        help_text="Rating percentage"
    )
    share = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        null=True,
        blank=True,
        help_text="Market share percentage"
    )
    demographic_data = models.JSONField(
        default=dict,
        blank=True,
        help_text="Demographic breakdown of viewers"
    )
    source = models.CharField(
        max_length=100,
        blank=True,
        help_text="Source of the impression data"
    )
    
    class Meta:
        db_table = 'epg_impressions'
        verbose_name = 'EPG Impression'
        verbose_name_plural = 'EPG Impressions'
        ordering = ['-timestamp']
        indexes = [
            models.Index(fields=['epg_entry', 'timestamp']),
            models.Index(fields=['timestamp']),
        ]
    
    def __str__(self):
        return f"{self.epg_entry} - {self.viewer_count} viewers at {self.timestamp}"


class EmissionSchedule(TimeStampedModel, SoftDeleteModel):
    """Emission/broadcast schedule template."""
    
    FREQUENCY_CHOICES = [
        ('daily', 'Daily'),
        ('weekly', 'Weekly'),
        ('monthly', 'Monthly'),
        ('custom', 'Custom'),
    ]
    
    DAY_CHOICES = [
        (0, 'Monday'),
        (1, 'Tuesday'),
        (2, 'Wednesday'),
        (3, 'Thursday'),
        (4, 'Friday'),
        (5, 'Saturday'),
        (6, 'Sunday'),
    ]
    
    name = models.CharField(
        max_length=255,
        help_text="Schedule template name"
    )
    channel = models.ForeignKey(
        Channel,
        on_delete=models.CASCADE,
        related_name='emission_schedules',
        help_text="Channel this schedule applies to"
    )
    program = models.ForeignKey(
        Program,
        on_delete=models.CASCADE,
        related_name='emission_schedules',
        help_text="Program to be scheduled"
    )
    frequency = models.CharField(
        max_length=20,
        choices=FREQUENCY_CHOICES,
        default='weekly'
    )
    start_date = models.DateField(
        help_text="When this schedule starts"
    )
    end_date = models.DateField(
        null=True,
        blank=True,
        help_text="When this schedule ends (optional)"
    )
    time_slot = models.TimeField(
        help_text="Time of day for broadcast"
    )
    duration_minutes = models.PositiveIntegerField(
        help_text="Duration in minutes"
    )
    days_of_week = models.JSONField(
        default=list,
        help_text="Days of week for broadcast (0=Monday, 6=Sunday)"
    )
    is_active = models.BooleanField(
        default=True,
        help_text="Whether this schedule is active"
    )
    priority = models.PositiveIntegerField(
        default=1,
        help_text="Schedule priority (higher number = higher priority)"
    )
    notes = models.TextField(
        blank=True,
        help_text="Notes about this schedule"
    )
    
    class Meta:
        db_table = 'epg_emission_schedules'
        verbose_name = 'Emission Schedule'
        verbose_name_plural = 'Emission Schedules'
        ordering = ['-priority', 'start_date', 'time_slot']
        indexes = [
            models.Index(fields=['channel', 'start_date']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self):
        return f"{self.name} - {self.channel.name}"
    
    def generate_epg_entries(self, start_date=None, end_date=None):
        """Generate EPG entries based on this schedule."""
        from datetime import datetime, timedelta
        
        if not start_date:
            start_date = self.start_date
        if not end_date:
            end_date = self.end_date or (start_date + timedelta(days=30))
        
        entries = []
        current_date = start_date
        
        while current_date <= end_date:
            # Check if this day should have a broadcast
            if current_date.weekday() in self.days_of_week:
                start_datetime = datetime.combine(current_date, self.time_slot)
                end_datetime = start_datetime + timedelta(minutes=self.duration_minutes)
                
                # Check if entry already exists
                existing = EpgEntry.objects.filter(
                    channel=self.channel,
                    program=self.program,
                    start_time=start_datetime
                ).first()
                
                if not existing:
                    entry = EpgEntry(
                        channel=self.channel,
                        program=self.program,
                        start_time=start_datetime,
                        end_time=end_datetime,
                        status='scheduled'
                    )
                    entries.append(entry)
            
            current_date += timedelta(days=1)
        
        # Bulk create entries
        if entries:
            EpgEntry.objects.bulk_create(entries)
        
        return len(entries)