"""
Channel Core Models

This module contains the fundamental channel-related models that form
the backbone of the channel management system.

Models:
    - ChannelZone: Geographic zones for channel broadcasting
    - ChannelCodec: Video/Audio codec configurations
    - Channel: Main channel model with comprehensive metadata
"""
 
from django.db import models
from django.utils import timezone 
from django.utils.translation import gettext_lazy as _

from apps.common.models import BaseModel 
 

class ChannelZone(BaseModel):
    """
    Geographic zones for channel broadcasting.
    
    Represents different geographic regions where channels can broadcast,
    each with their own timezone and configuration settings.
    
    Attributes:
        name (str): Human-readable name of the zone
        code (str): Unique code identifier for the zone
        description (str): Optional detailed description
        timezone (str): Timezone identifier for the zone
        is_active (bool): Whether the zone is currently active
        
    Relationships:
        - channels: Many-to-many with Channel through ChannelZoneRelation
        
    Example:
        >>> zone = ChannelZone.objects.create(
        ...     name="North America East",
        ...     code="NAE",
        ...     timezone="America/New_York"
        ... )
    """
    
    name = models.CharField(
        max_length=255,
        verbose_name=_('Zone Name'),
        help_text=_('Human-readable name of the geographic zone')
    )
    code = models.CharField(
        max_length=10,
        unique=True,
        verbose_name=_('Zone Code'),
        help_text=_('Unique identifier code for the zone (e.g., "NAE", "EUR")')
    )
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Optional detailed description of the zone coverage')
    )
    timezone = models.CharField(
        max_length=50,
        default="UTC",
        verbose_name=_('Timezone'),
        help_text=_('Timezone identifier (e.g., "America/New_York", "Europe/London")')
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Is Active'),
        help_text=_('Whether this zone is currently accepting broadcasts')
    )
    
    class Meta:
        db_table = "channel_zones"
        ordering = ["name"]
        verbose_name = _('Channel Zone')
        verbose_name_plural = _('Channel Zones')
    
    def __str__(self):
        return f"{self.name} ({self.code})"
    
    def get_active_channels_count(self):
        """
        Get the number of active channels in this zone.
        
        Returns:
            int: Number of active channels broadcasting in this zone
        """
        return self.channels.filter(status='active').count()


class ChannelCodec(BaseModel):
    """
    Video and audio codec configurations for channels.
    
    Defines encoding parameters, quality settings, and FFmpeg options
    for different streaming scenarios and quality requirements.
    
    Attributes:
        name (str): Human-readable codec configuration name
        video_codec (str): Video codec identifier (e.g., "h264", "h265")
        audio_codec (str): Audio codec identifier (e.g., "aac", "mp3")
        resolution (str): Video resolution (e.g., "1920x1080", "1280x720")
        bitrate (int): Bitrate in kbps
        frame_rate (decimal): Frame rate in fps
        ffmpeg_options (dict): Additional FFmpeg encoding options
        
    Relationships:
        - channels: One-to-many (default codec for channels)
        - zone_relations: One-to-many (zone-specific codec overrides)
        
    Example:
        >>> codec = ChannelCodec.objects.create(
        ...     name="HD H264 AAC",
        ...     video_codec="h264",
        ...     audio_codec="aac",
        ...     resolution="1920x1080",
        ...     bitrate=5000,
        ...     frame_rate=29.97
        ... )
    """
    
    name = models.CharField(
        max_length=100,
        verbose_name=_('Codec Name'),
        help_text=_('Human-readable name for this codec configuration')
    )
    video_codec = models.CharField(
        max_length=50,
        verbose_name=_('Video Codec'),
        help_text=_('Video codec identifier (h264, h265, vp9, etc.)')
    )
    audio_codec = models.CharField(
        max_length=50,
        verbose_name=_('Audio Codec'),
        help_text=_('Audio codec identifier (aac, mp3, opus, etc.)')
    )
    resolution = models.CharField(
        max_length=20,
        blank=True,
        verbose_name=_('Resolution'),
        help_text=_('Video resolution in WIDTHxHEIGHT format (e.g., 1920x1080)')
    )
    bitrate = models.PositiveIntegerField(
        verbose_name=_('Bitrate'),
        help_text=_('Target bitrate in kbps')
    )
    frame_rate = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        null=True,
        blank=True,
        verbose_name=_('Frame Rate'),
        help_text=_('Target frame rate in fps (e.g., 29.97, 25.00)')
    )
    ffmpeg_options = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('FFmpeg Options'),
        help_text=_('Additional FFmpeg encoding options as JSON object')
    )
    
    class Meta:
        db_table = "channel_codecs"
        ordering = ["name"]
        verbose_name = _('Channel Codec')
        verbose_name_plural = _('Channel Codecs')
    
    def __str__(self):
        return f"{self.name} ({self.video_codec}/{self.audio_codec})"
    
    def get_quality_profile(self):
        """
        Determine quality profile based on resolution and bitrate.
        
        Returns:
            str: Quality profile (SD, HD, FHD, 4K)
        """
        if not self.resolution:
            return "Unknown"
        
        width = int(self.resolution.split('x')[0]) if 'x' in self.resolution else 0
        
        if width >= 3840:
            return "4K"
        elif width >= 1920:
            return "FHD"
        elif width >= 1280:
            return "HD"
        else:
            return "SD"
    
    def estimate_bandwidth_mbps(self):
        """
        Estimate required bandwidth in Mbps.
        
        Returns:
            float: Estimated bandwidth requirement in Mbps
        """
        # Add 20% overhead for container and network
        return (self.bitrate * 1.2) / 1000


class Channel(BaseModel):
    """
    Television channels with comprehensive metadata and relationships.
    
    Represents individual TV channels with their broadcasting details,
    network affiliations, geographic coverage, and advertising inventory.
    Each channel can broadcast in multiple zones with zone-specific configurations.
    
    Attributes:
        name (str): Full channel name
        display_name (str): Call sign or short identifier
        channel_number (str): Channel number in listings
        channel_type (str): Broadcasting technology type
        status (str): Current operational status
        logo (ImageField): Channel logo/branding image
        description (str): Channel description
        website (str): Official website URL
        language (str): Primary broadcast language
        category (str): Content category
        target_audience (str): Target demographic
        supports_dai (bool): Dynamic Ad Insertion support
        max_ad_duration (int): Maximum ad break duration in seconds
        min_ad_gap (int): Minimum gap between ads in seconds
        last_health_check (datetime): Last health monitoring check
        is_online (bool): Current online status
        
    Relationships:
        - zones: Many-to-many through ChannelZoneRelation
        - codec: Foreign key to default ChannelCodec
        - programs: One-to-many with EPGProgram
        - jingles: One-to-many with Jingle
        - schedules: One-to-many with ChannelSchedule
        - jingle_detections: One-to-many with JingleDetection
        
    Example:
        >>> channel = Channel.objects.create(
        ...     name="Example News Network",
        ...     display_name="ENN",
        ...     channel_number="24.1",
        ...     channel_type="cable",
        ...     category="News"
        ... )
    """
    
    CHANNEL_TYPES = [
        ('terrestrial', _('Terrestrial/Over-the-Air')),
        ('cable', _('Cable Channel')),
        ('satellite', _('Satellite Channel')),
        ('iptv', _('IPTV Channel')),
        ('streaming', _('Streaming Channel')),
        ('radio', _('Radio Station')),
    ]
    
    STATUS_CHOICES = [
        ("active", _("Active")),
        ("inactive", _("Inactive")),
        ("maintenance", _("Maintenance")),
        ("testing", _("Testing")),
    ]
    
    # Channel Identity
    name = models.CharField(
        max_length=200,
        verbose_name=_('Channel Name'),
        help_text=_('Full official name of the television channel')
    )
    display_name = models.CharField(
        max_length=20,
        unique=True,
        verbose_name=_('Call Sign'),
        help_text=_('Official call sign or short identifier (e.g., "WABC-TV", "ESPN")')
    )
    channel_number = models.CharField(
        max_length=20,
        verbose_name=_('Channel Number'),
        help_text=_('Channel number in cable/satellite listings (e.g., "7.1", "ESPN")')
    )
    
    # Channel Classification
    channel_type = models.CharField(
        max_length=20,
        choices=CHANNEL_TYPES,
        default="satellite",
        verbose_name=_('Channel Type'),
        help_text=_('Broadcasting technology and distribution method')
    )
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default="active",
        verbose_name=_('Channel Status'),
        help_text=_('Current operational status of the channel')
    )
    
    # Geographic Coverage
    zones = models.ManyToManyField(
        ChannelZone,
        through='ChannelZoneRelation',
        related_name='channels',
        blank=True,
        verbose_name=_('Broadcasting Zones'),
        help_text=_('Geographic zones where this channel is available (can be added later during editing)')
    )
 
    # Branding and Metadata
    logo = models.ImageField(
        upload_to="channels/logos/",
        blank=True,
        null=True,
        verbose_name=_('Channel Logo'),
        help_text=_('Channel logo or branding image')
    )
    description = models.TextField(
        blank=True,
        verbose_name=_('Channel Description'),
        help_text=_('Brief description of the channel and its content')
    )
    website = models.URLField(
        blank=True,
        verbose_name=_('Official Website'),
        help_text=_('Channel\'s official website URL')
    )
    
    # Content Classification
    language = models.CharField(
        max_length=250,
        blank=True,
        verbose_name=_('Primary Language'),
        help_text=_('Primary language of broadcast content')
    )
    category = models.CharField(
        max_length=250,
        blank=True,
        verbose_name=_('Content Category'),
        help_text=_('Primary content category (News, Sports, Entertainment, etc.)')
    )
    target_audience = models.CharField(
        max_length=100,
        blank=True,
        verbose_name=_('Target Audience'),
        help_text=_('Primary target demographic (Children, Adults, Seniors, etc.)')
    )
    
    # Advertising Configuration
    supports_dai = models.BooleanField(
        default=True,
        verbose_name=_('Supports DAI'),
        help_text=_('Whether the channel supports Dynamic Ad Insertion')
    ) 
    
    # Health Monitoring
    last_health_check = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=_('Last Health Check'),
        help_text=_('Timestamp of the most recent health monitoring check')
    )
    is_online = models.BooleanField(
        default=True,
        verbose_name=_('Is Online'),
        help_text=_('Current online/offline status of the channel')
    )
    
    class Meta:
        db_table = "channels"
        ordering = ["channel_number", "name"]
        verbose_name = _('Channel')
        verbose_name_plural = _('Channels')
        indexes = [
            models.Index(fields=["status", "is_online"]),
            models.Index(fields=["channel_type", "category"]),
            models.Index(fields=["last_health_check"]),
        ]
    
    def __str__(self):
        if self.channel_number:
            return f"{self.channel_number}. {self.display_name or self.name}"
        return self.display_name or self.name
    
    @property
    def is_active(self):
        """
        Check if channel is currently active and broadcasting.
        
        Returns:
            bool: True if channel is active and online
        """
        return self.status == "active" and self.is_online
    
    def update_health_status(self, is_online=True):
        """
        Update channel health monitoring status.
        
        Args:
            is_online (bool): Current online status
        """
        self.is_online = is_online
        self.last_health_check = timezone.now()
        self.save(update_fields=["is_online", "last_health_check"])
    
    def get_zone_relation(self, zone):
        """
        Get the zone-specific configuration for this channel.
        
        Args:
            zone (ChannelZone): The zone to get configuration for
            
        Returns:
            ChannelZoneRelation or None: Zone-specific configuration
        """
        try:
            return self.zone_relations.get(zone=zone)
        except:
            return None
    
    def get_stream_url_for_zone(self, zone):
        """
        Get the appropriate stream URL for a specific zone.
        
        Args:
            zone (ChannelZone): Target broadcasting zone
            
        Returns:
            str: Stream URL for the zone or default stream URL
        """
        zone_relation = self.get_zone_relation(zone)
        if zone_relation and zone_relation.stream_url:
            return zone_relation.stream_url
        return self.stream_url
    
    def get_codec_for_zone(self, zone):
        """
        Get the appropriate codec configuration for a specific zone.
        
        Args:
            zone (ChannelZone): Target broadcasting zone
            
        Returns:
            ChannelCodec: Codec configuration for the zone or default codec
        """
        zone_relation = self.get_zone_relation(zone)
        if zone_relation and zone_relation.codec:
            return zone_relation.codec
        return self.codec
    
    def get_active_zones(self):
        """
        Get all active zones where this channel broadcasts.
        
        Returns:
            QuerySet: Active ChannelZone objects
        """
        return self.zones.filter(
            channelzonerelation__is_active=True,
            is_active=True
        )
    
    def add_zone_with_config(self, zone, codec=None, stream_url=None, backup_stream_url=None, 
                           vpn_config=None, ftp_config=None, standalone_vpn=None, standalone_ftp=None):
        """
        Add a zone to this channel with optional configuration data.
        """
        from .channel_relations import ChannelZoneRelation
        
        relation, created = ChannelZoneRelation.objects.get_or_create(
            channel=self,
            zone=zone,
            defaults={
                'codec': codec,
                'stream_url': stream_url,
                'backup_stream_url': backup_stream_url,
                'vpn_config': vpn_config,
                'ftp_config': ftp_config,
                'standalone_vpn_config': standalone_vpn,
                'standalone_ftp_config': standalone_ftp,
            }
        )
        return relation
    
    def update_zone_config(self, zone, **config_data):
        """
        Update configuration for a specific zone relation.
        """
        from .channel_relations import ChannelZoneRelation
        
        try:
            relation = ChannelZoneRelation.objects.get(channel=self, zone=zone)
            for field, value in config_data.items():
                if hasattr(relation, field):
                    setattr(relation, field, value)
            relation.save()
            return relation
        except ChannelZoneRelation.DoesNotExist:
            return None
    
    def get_zone_configs(self):
        """
        Get all zone configurations for this channel.
        """
        from .channel_relations import ChannelZoneRelation
        return ChannelZoneRelation.objects.filter(channel=self).select_related(
            'zone', 'codec', 'vpn_config', 'ftp_config', 
            'standalone_vpn_config', 'standalone_ftp_config'
        )
    
    def has_complete_config(self):
        """
        Check if channel has at least one complete configuration (zone + stream URL).
        """
        return self.get_zone_configs().filter(
            stream_url__isnull=False
        ).exists()
    
    def get_active_streams(self):
        """
        Get all active stream URLs for this channel.
        """
        configs = self.get_zone_configs().filter(
            stream_url__isnull=False,
            zone__is_active=True
        )
        return [config.stream_url for config in configs if config.stream_url]

