# Adtlas TV Advertising Platform - Channels Models
# Comprehensive models for TV channels, networks, zones, and relationships

import uuid
from decimal import Decimal
from django.db import models
from django.urls import reverse 
from django.utils.translation import gettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator

from apps.common.models import BaseModel


class GeographicZone(BaseModel):
    """
    Geographic zones for targeted TV advertising.
    
    Represents geographic regions where TV channels broadcast and where
    advertising campaigns can be targeted. Supports hierarchical zones
    (country > state > city > DMA) for precise geographic targeting.
    """
    
    ZONE_TYPES = [
        ('country', _('Country')),
        ('state', _('State/Province')),
        ('city', _('City')),
        ('dma', _('Designated Market Area')),
        ('postal', _('Postal Code')),
        ('custom', _('Custom Zone')),
    ]
    
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        verbose_name=_('Zone ID')
    )
    name = models.CharField(
        max_length=200,
        verbose_name=_('Zone Name'),
        help_text=_('Name of the geographic zone (e.g., "New York DMA", "California")')
    )
    code = models.CharField(
        max_length=20,
        unique=True,
        verbose_name=_('Zone Code'),
        help_text=_('Unique identifier code for the zone (e.g., "NYC", "CA")')
    )
    zone_type = models.CharField(
        max_length=20,
        choices=ZONE_TYPES,
        verbose_name=_('Zone Type'),
        help_text=_('Type of geographic zone')
    )
    parent_zone = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='child_zones',
        verbose_name=_('Parent Zone'),
        help_text=_('Parent geographic zone for hierarchical organization')
    )
    population = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name=_('Population'),
        help_text=_('Total population in this geographic zone')
    )
    tv_households = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name=_('TV Households'),
        help_text=_('Number of households with television access')
    )
    latitude = models.DecimalField(
        max_digits=10,
        decimal_places=7,
        null=True,
        blank=True,
        verbose_name=_('Latitude'),
        help_text=_('Geographic latitude coordinate')
    )
    longitude = models.DecimalField(
        max_digits=10,
        decimal_places=7,
        null=True,
        blank=True,
        verbose_name=_('Longitude'),
        help_text=_('Geographic longitude coordinate')
    )
    timezone = models.CharField(
        max_length=50,
        default='UTC',
        verbose_name=_('Timezone'),
        help_text=_('Primary timezone for this zone (e.g., "America/New_York")')
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether this zone is active for advertising targeting')
    )
    
    class Meta:
        verbose_name = _('Geographic Zone')
        verbose_name_plural = _('Geographic Zones')
        ordering = ['zone_type', 'name']
        indexes = [
            models.Index(fields=['zone_type', 'is_active']),
            models.Index(fields=['parent_zone', 'zone_type']),
            models.Index(fields=['code']),
        ]
    
    def __str__(self):
        return f"{self.name} ({self.get_zone_type_display()})"
    
    def get_absolute_url(self):
        return reverse('channels:zone_detail', kwargs={'pk': self.pk})
    
    @property
    def full_hierarchy(self):
        """Get the full hierarchical path of this zone."""
        hierarchy = [self.name]
        current = self.parent_zone
        while current:
            hierarchy.insert(0, current.name)
            current = current.parent_zone
        return ' > '.join(hierarchy)
    
    def get_child_zones_count(self):
        """Get the count of direct child zones."""
        return self.child_zones.filter(is_active=True).count()


class BroadcastNetwork(BaseModel):
    """
    Television broadcasting networks and media companies.
    
    Represents major TV networks (ABC, NBC, CBS, etc.) and their hierarchical
    relationships, including parent companies, subsidiaries, and affiliates.
    """
    
    NETWORK_TYPES = [
        ('broadcast', _('Broadcast Network')),
        ('cable', _('Cable Network')),
        ('satellite', _('Satellite Network')),
        ('streaming', _('Streaming Service')),
        ('local', _('Local Station')),
        ('syndication', _('Syndication Network')),
    ]
    
    name = models.CharField(
        max_length=200,
        verbose_name=_('Network Name'),
        help_text=_('Full name of the broadcasting network')
    )
    short_name = models.CharField(
        max_length=50,
        verbose_name=_('Short Name'),
        help_text=_('Abbreviated name or call letters (e.g., "ABC", "NBC")')
    )
    network_type = models.CharField(
        max_length=20,
        choices=NETWORK_TYPES,
        verbose_name=_('Network Type'),
        help_text=_('Type of broadcasting network')
    )
    parent_network = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='subsidiary_networks',
        verbose_name=_('Parent Network'),
        help_text=_('Parent company or network group')
    )
    founded_date = models.DateField(
        null=True,
        blank=True,
        verbose_name=_('Founded Date'),
        help_text=_('Date when the network was established')
    )
    headquarters_location = models.CharField(
        max_length=200,
        blank=True,
        verbose_name=_('Headquarters'),
        help_text=_('Location of network headquarters')
    )
    website_url = models.URLField(
        blank=True,
        verbose_name=_('Website URL'),
        help_text=_('Official website of the network')
    )
    logo_url = models.URLField(
        blank=True,
        verbose_name=_('Logo URL'),
        help_text=_('URL to the network logo image')
    )
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Detailed description of the network and its programming')
    )
    target_demographics = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('Target Demographics'),
        help_text=_('JSON data describing target audience demographics')
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether this network is currently active')
    )
    
    class Meta:
        verbose_name = _('Broadcast Network')
        verbose_name_plural = _('Broadcast Networks')
        ordering = ['network_type', 'name']
        indexes = [
            models.Index(fields=['network_type', 'is_active']),
            models.Index(fields=['parent_network']),
            models.Index(fields=['short_name']),
        ]
    
    def __str__(self):
        return f"{self.name} ({self.short_name})"
    
    def get_absolute_url(self):
        return reverse('channels:network_detail', kwargs={'pk': self.pk})
    
    @property
    def channel_count(self):
        """Get the number of channels associated with this network."""
        return self.channels.filter(is_active=True).count()
    
    def get_subsidiary_networks(self):
        """Get all subsidiary networks recursively."""
        subsidiaries = list(self.subsidiary_networks.filter(is_active=True))
        for subsidiary in self.subsidiary_networks.filter(is_active=True):
            subsidiaries.extend(subsidiary.get_subsidiary_networks())
        return subsidiaries


class TVChannel(BaseModel):
    """
    Television channels with comprehensive metadata and relationships.
    
    Represents individual TV channels with their broadcasting details,
    network affiliations, geographic coverage, and advertising inventory.
    """
    
    CHANNEL_TYPES = [
        ('terrestrial', _('Terrestrial/Over-the-Air')),
        ('cable', _('Cable Channel')),
        ('satellite', _('Satellite Channel')),
        ('iptv', _('IPTV Channel')),
        ('streaming', _('Streaming Channel')),
        ('radio', _('Radio Station')),
    ]
    
    CONTENT_CATEGORIES = [
        ('general', _('General Entertainment')),
        ('news', _('News & Information')),
        ('sports', _('Sports')),
        ('movies', _('Movies')),
        ('kids', _('Children\'s Programming')),
        ('lifestyle', _('Lifestyle & Reality')),
        ('documentary', _('Documentary')),
        ('music', _('Music')),
        ('religious', _('Religious')),
        ('educational', _('Educational')),
        ('shopping', _('Shopping')),
        ('adult', _('Adult Content')),
    ] 

    name = models.CharField(
        max_length=200,
        verbose_name=_('Channel Name'),
        help_text=_('Full name of the TV channel')
    )
    call_sign = models.CharField(
        max_length=20,
        unique=True,
        verbose_name=_('Call Sign'),
        help_text=_('Official call sign or identifier (e.g., "WABC-TV", "ESPN")')
    )
    channel_number = models.CharField(
        max_length=20,
        verbose_name=_('Channel Number'),
        help_text=_('Channel number on cable/satellite systems (e.g., "7.1", "ESPN")')
    )
    channel_type = models.CharField(
        max_length=20,
        choices=CHANNEL_TYPES,
        verbose_name=_('Channel Type'),
        help_text=_('Type of broadcasting technology')
    )
    network = models.ForeignKey(
        BroadcastNetwork,
        on_delete=models.CASCADE,
        related_name='channels',
        verbose_name=_('Network'),
        help_text=_('Broadcasting network that owns or operates this channel')
    )
    coverage_zones = models.ManyToManyField(
        GeographicZone,
        through='ChannelCoverage',
        related_name='channels',
        verbose_name=_('Coverage Zones'),
        help_text=_('Geographic zones where this channel is available')
    )
    primary_language = models.CharField(
        max_length=10,
        default='en',
        verbose_name=_('Primary Language'),
        help_text=_('Primary language of channel content (ISO 639-1 code)')
    )
    secondary_languages = models.JSONField(
        default=list,
        blank=True,
        verbose_name=_('Secondary Languages'),
        help_text=_('Additional languages supported by the channel')
    )
    content_category = models.CharField(
        max_length=20,
        choices=CONTENT_CATEGORIES,
        verbose_name=_('Content Category'),
        help_text=_('Primary content category of the channel')
    )
    target_demographics = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('Target Demographics'),
        help_text=_('JSON data describing target audience demographics')
    )
    hd_available = models.BooleanField(
        default=True,
        verbose_name=_('HD Available'),
        help_text=_('Whether the channel broadcasts in high definition')
    )
    
    website_url = models.URLField(
        blank=True,
        verbose_name=_('Website URL'),
        help_text=_('Official website of the channel')
    )
    logo_url = models.URLField(
        blank=True,
        verbose_name=_('Logo URL'),
        help_text=_('URL to the channel logo image')
    )
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Detailed description of the channel and its programming')
    )
    launch_date = models.DateField(
        null=True,
        blank=True,
        verbose_name=_('Launch Date'),
        help_text=_('Date when the channel was launched')
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether this channel is currently active and broadcasting')
    )
    is_premium = models.BooleanField(
        default=False,
        verbose_name=_('Premium Channel'),
        help_text=_('Whether this is a premium/pay channel')
    )
    advertising_enabled = models.BooleanField(
        default=True,
        verbose_name=_('Advertising Enabled'),
        help_text=_('Whether advertising is allowed on this channel')
    )
    
    class Meta:
        verbose_name = _('TV Channel')
        verbose_name_plural = _('TV Channels')
        ordering = ['channel_number', 'name']
        indexes = [
            models.Index(fields=['channel_type', 'is_active']),
            models.Index(fields=['network', 'is_active']),
            models.Index(fields=['content_category']),
            models.Index(fields=['call_sign']),
            models.Index(fields=['channel_number']),
        ]
    
    def __str__(self):
        return f"{self.name} ({self.call_sign})"
    
    def get_absolute_url(self):
        return reverse('channels:channel_detail', kwargs={'pk': self.pk})
    
    @property
    def total_coverage_population(self):
        """Calculate total population covered by this channel."""
        return sum(
            coverage.zone.population or 0 
            for coverage in self.coverage_details.filter(
                zone__is_active=True,
                is_active=True
            )
        )
    
    def get_coverage_zones_by_type(self, zone_type):
        """Get coverage zones filtered by type."""
        return self.coverage_zones.filter(
            zone_type=zone_type,
            is_active=True
        )


class ChannelCoverage(BaseModel):
    """
    Through model for Channel-Zone relationships with coverage details.
    
    Defines the specific coverage relationship between a TV channel and
    a geographic zone, including signal strength, availability, and
    subscriber information.
    """
    
    SIGNAL_STRENGTH_CHOICES = [
        ('excellent', _('Excellent (90-100%)')),
        ('good', _('Good (70-89%)')),
        ('fair', _('Fair (50-69%)')),
        ('poor', _('Poor (30-49%)')),
        ('marginal', _('Marginal (10-29%)')),
    ]
    
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        verbose_name=_('Coverage ID')
    )
    channel = models.ForeignKey(
        TVChannel,
        on_delete=models.CASCADE,
        related_name='coverage_details',
        verbose_name=_('Channel')
    )
    zone = models.ForeignKey(
        GeographicZone,
        on_delete=models.CASCADE,
        related_name='channel_coverage',
        verbose_name=_('Zone')
    )
    signal_strength = models.CharField(
        max_length=20,
        choices=SIGNAL_STRENGTH_CHOICES,
        default='good',
        verbose_name=_('Signal Strength'),
        help_text=_('Quality of signal reception in this zone')
    )
    coverage_percentage = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        validators=[MinValueValidator(0), MaxValueValidator(100)],
        default=Decimal('100.00'),
        verbose_name=_('Coverage Percentage'),
        help_text=_('Percentage of zone area covered by the channel')
    )
    subscriber_count = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name=_('Subscriber Count'),
        help_text=_('Number of subscribers in this zone')
    )
    penetration_rate = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        null=True,
        blank=True,
        validators=[MinValueValidator(0), MaxValueValidator(100)],
        verbose_name=_('Penetration Rate'),
        help_text=_('Percentage of households that subscribe to this channel')
    )
    launch_date_in_zone = models.DateField(
        null=True,
        blank=True,
        verbose_name=_('Launch Date in Zone'),
        help_text=_('Date when the channel became available in this zone')
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether this coverage is currently active')
    )
    notes = models.TextField(
        blank=True,
        verbose_name=_('Notes'),
        help_text=_('Additional notes about coverage in this zone')
    )
    
    class Meta:
        verbose_name = _('Channel Coverage')
        verbose_name_plural = _('Channel Coverage')
        unique_together = ['channel', 'zone']
        ordering = ['channel__name', 'zone__name']
        indexes = [
            models.Index(fields=['channel', 'zone']),
            models.Index(fields=['signal_strength']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self):
        return f"{self.channel.name} in {self.zone.name}"
    
    @property
    def estimated_viewers(self):
        """Estimate potential viewers based on zone population and penetration."""
        if self.zone.tv_households and self.penetration_rate:
            return int(self.zone.tv_households * (self.penetration_rate / 100))
        return None


class ContentSchedule(BaseModel):
    """
    Electronic Program Guide (EPG) integration for content scheduling.
    
    Manages the programming schedule for TV channels, including show times,
    content metadata, and advertising break opportunities.
    """
    
    CONTENT_TYPES = [
        ('live', _('Live Programming')),
        ('recorded', _('Pre-recorded')),
        ('rerun', _('Rerun/Repeat')),
        ('movie', _('Movie')),
        ('series', _('TV Series')),
        ('news', _('News Program')),
        ('sports', _('Sports Event')),
        ('commercial', _('Commercial Break')),
        ('infomercial', _('Infomercial')),
    ]
    
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        verbose_name=_('Schedule ID')
    )
    channel = models.ForeignKey(
        TVChannel,
        on_delete=models.CASCADE,
        related_name='schedule_entries',
        verbose_name=_('Channel')
    )
    title = models.CharField(
        max_length=300,
        verbose_name=_('Program Title'),
        help_text=_('Title of the scheduled program or content')
    )
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Detailed description of the program content')
    )
    content_type = models.CharField(
        max_length=20,
        choices=CONTENT_TYPES,
        verbose_name=_('Content Type'),
        help_text=_('Type of scheduled content')
    )
    start_time = models.DateTimeField(
        verbose_name=_('Start Time'),
        help_text=_('Scheduled start time of the program')
    )
    end_time = models.DateTimeField(
        verbose_name=_('End Time'),
        help_text=_('Scheduled end time of the program')
    )
    duration_minutes = models.PositiveIntegerField(
        verbose_name=_('Duration (Minutes)'),
        help_text=_('Program duration in minutes')
    )
    genre = models.CharField(
        max_length=100,
        blank=True,
        verbose_name=_('Genre'),
        help_text=_('Program genre or category')
    )
    rating = models.CharField(
        max_length=10,
        blank=True,
        verbose_name=_('Content Rating'),
        help_text=_('Content rating (G, PG, PG-13, R, etc.)')
    )
    target_demographics = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('Target Demographics'),
        help_text=_('JSON data describing target audience for this program')
    )
    advertising_breaks = models.PositiveIntegerField(
        default=0,
        verbose_name=_('Advertising Breaks'),
        help_text=_('Number of advertising breaks during this program')
    )
    ad_break_duration = models.PositiveIntegerField(
        default=0,
        verbose_name=_('Ad Break Duration'),
        help_text=_('Total duration of advertising breaks in seconds')
    )
    is_live = models.BooleanField(
        default=False,
        verbose_name=_('Live Program'),
        help_text=_('Whether this is a live broadcast')
    )
    is_premiere = models.BooleanField(
        default=False,
        verbose_name=_('Premiere'),
        help_text=_('Whether this is a premiere episode or first showing')
    )
    external_id = models.CharField(
        max_length=100,
        blank=True,
        verbose_name=_('External ID'),
        help_text=_('External system identifier for EPG integration')
    )
    
    class Meta:
        verbose_name = _('Content Schedule')
        verbose_name_plural = _('Content Schedules')
        ordering = ['channel', 'start_time']
        indexes = [
            models.Index(fields=['channel', 'start_time']),
            models.Index(fields=['start_time', 'end_time']),
            models.Index(fields=['content_type']),
            models.Index(fields=['is_live']),
            models.Index(fields=['external_id']),
        ]
    
    def __str__(self):
        return f"{self.title} on {self.channel.name} at {self.start_time}"
    
    def get_absolute_url(self):
        return reverse('channels:schedule_detail', kwargs={'pk': self.pk})
    
    @property
    def is_currently_airing(self):
        """Check if the program is currently airing."""
        from django.utils import timezone
        now = timezone.now()
        return self.start_time <= now <= self.end_time
    
    def clean(self):
        """Validate schedule entry data."""
        from django.core.exceptions import ValidationError
        
        if self.start_time and self.end_time:
            if self.start_time >= self.end_time:
                raise ValidationError(_('Start time must be before end time.'))
            
            # Calculate duration from start/end times
            calculated_duration = (self.end_time - self.start_time).total_seconds() / 60
            if abs(calculated_duration - self.duration_minutes) > 1:  # Allow 1 minute tolerance
                self.duration_minutes = int(calculated_duration)


class AudienceDemographics(BaseModel):
    """
    Audience demographics and viewership analytics for channels and zones.
    
    Stores demographic data and viewership statistics for targeted
    advertising and audience analysis.
    """
    
    AGE_GROUPS = [
        ('2-11', _('Children (2-11)')),
        ('12-17', _('Teens (12-17)')),
        ('18-24', _('Young Adults (18-24)')),
        ('25-34', _('Adults (25-34)')),
        ('35-44', _('Adults (35-44)')),
        ('45-54', _('Adults (45-54)')),
        ('55-64', _('Adults (55-64)')),
        ('65+', _('Seniors (65+)')),
    ]
    
    INCOME_BRACKETS = [
        ('under-25k', _('Under $25,000')),
        ('25k-50k', _('$25,000 - $50,000')),
        ('50k-75k', _('$50,000 - $75,000')),
        ('75k-100k', _('$75,000 - $100,000')),
        ('100k-150k', _('$100,000 - $150,000')),
        ('over-150k', _('Over $150,000')),
    ]
    

    channel = models.ForeignKey(
        TVChannel,
        on_delete=models.CASCADE,
        related_name='demographics',
        verbose_name=_('Channel')
    )
    zone = models.ForeignKey(
        GeographicZone,
        on_delete=models.CASCADE,
        related_name='demographics',
        verbose_name=_('Zone')
    )
    measurement_date = models.DateField(
        verbose_name=_('Measurement Date'),
        help_text=_('Date when these demographics were measured')
    )
    total_viewers = models.PositiveIntegerField(
        verbose_name=_('Total Viewers'),
        help_text=_('Total number of viewers in this demographic')
    )
    age_distribution = models.JSONField(
        default=dict,
        verbose_name=_('Age Distribution'),
        help_text=_('JSON data with age group percentages')
    )
    gender_distribution = models.JSONField(
        default=dict,
        verbose_name=_('Gender Distribution'),
        help_text=_('JSON data with gender percentages (male, female, other)')
    )
    income_distribution = models.JSONField(
        default=dict,
        verbose_name=_('Income Distribution'),
        help_text=_('JSON data with income bracket percentages')
    )
    education_distribution = models.JSONField(
        default=dict,
        verbose_name=_('Education Distribution'),
        help_text=_('JSON data with education level percentages')
    )
    viewing_hours_per_week = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        null=True,
        blank=True,
        verbose_name=_('Viewing Hours/Week'),
        help_text=_('Average viewing hours per week for this demographic')
    )
    peak_viewing_times = models.JSONField(
        default=list,
        blank=True,
        verbose_name=_('Peak Viewing Times'),
        help_text=_('JSON array of peak viewing time periods')
    )
    
    class Meta:
        verbose_name = _('Audience Demographics')
        verbose_name_plural = _('Audience Demographics')
        unique_together = ['channel', 'zone', 'measurement_date']
        ordering = ['-measurement_date', 'channel__name']
        indexes = [
            models.Index(fields=['channel', 'zone']),
            models.Index(fields=['measurement_date']),
            models.Index(fields=['total_viewers']),
        ]
    
    def __str__(self):
        return f"{self.channel.name} demographics in {self.zone.name} ({self.measurement_date})"
    
    @property
    def primary_age_group(self):
        """Get the age group with the highest percentage."""
        if not self.age_distribution:
            return None
        return max(self.age_distribution.items(), key=lambda x: x[1])[0]
    
    @property
    def viewer_engagement_score(self):
        """Calculate a viewer engagement score based on viewing hours."""
        if self.viewing_hours_per_week:
            # Normalize to a 0-100 scale (assuming max 40 hours/week)
            return min(float(self.viewing_hours_per_week) * 2.5, 100)
        return 0
