# Adtlas TV Advertising Platform - Channels Forms
# Django forms for TV channels, networks, and coverage management

from django import forms
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.utils import timezone
from datetime import datetime, timedelta
import json
import re

from .models import (
    GeographicZone,
    BroadcastNetwork,
    TVChannel,
    ChannelCoverage,
    ContentSchedule,
    AudienceDemographics
)


class GeographicZoneForm(forms.ModelForm):
    """
    Form for creating and editing Geographic Zones.
    
    Includes validation for hierarchical relationships and geographic data.
    """
    
    class Meta:
        model = GeographicZone
        fields = [
            'name',
            'code',
            'zone_type',
            'parent_zone',
            'population',
            'tv_households',
            'latitude',
            'longitude',
            'timezone',
            'is_active'
        ]
        
        widgets = {
            'name': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Enter zone name')
            }),
            'code': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Enter zone code (e.g., US-CA-LA)')
            }),
            'zone_type': forms.Select(attrs={
                'class': 'form-select'
            }),
            'parent_zone': forms.Select(attrs={
                'class': 'form-select'
            }),
            'population': forms.NumberInput(attrs={
                'class': 'form-control',
                'placeholder': _('Total population')
            }),
            'tv_households': forms.NumberInput(attrs={
                'class': 'form-control',
                'placeholder': _('Number of TV households')
            }),
            'latitude': forms.NumberInput(attrs={
                'class': 'form-control',
                'step': 'any',
                'placeholder': _('Latitude coordinate')
            }),
            'longitude': forms.NumberInput(attrs={
                'class': 'form-control',
                'step': 'any',
                'placeholder': _('Longitude coordinate')
            }),
            'timezone': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Timezone (e.g., America/New_York)')
            }),
            'is_active': forms.CheckboxInput(attrs={
                'class': 'form-check-input'
            })
        }
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # Filter parent zones to prevent circular references
        if self.instance and self.instance.pk:
            # Exclude self and any descendants
            excluded_ids = [self.instance.pk]
            descendants = self.instance.get_all_descendants()
            excluded_ids.extend([desc.pk for desc in descendants])
            
            self.fields['parent_zone'].queryset = GeographicZone.objects.filter(
                is_active=True
            ).exclude(pk__in=excluded_ids)
        else:
            self.fields['parent_zone'].queryset = GeographicZone.objects.filter(
                is_active=True
            )
        
        # Make parent zone optional for top-level zones
        self.fields['parent_zone'].required = False
        self.fields['parent_zone'].empty_label = _('No parent zone (top-level)')
    
    def clean_code(self):
        """
        Validate zone code format and uniqueness.
        """
        code = self.cleaned_data.get('code')
        if code:
            # Validate format (letters, numbers, hyphens, underscores)
            if not re.match(r'^[A-Z0-9_-]+$', code.upper()):
                raise ValidationError(
                    _('Zone code can only contain letters, numbers, hyphens, and underscores.')
                )
            
            # Check uniqueness
            existing = GeographicZone.objects.filter(code__iexact=code)
            if self.instance and self.instance.pk:
                existing = existing.exclude(pk=self.instance.pk)
            
            if existing.exists():
                raise ValidationError(
                    _('A zone with this code already exists.')
                )
            
            return code.upper()
        return code
    
    def clean_tv_households(self):
        """
        Validate TV households against population.
        """
        tv_households = self.cleaned_data.get('tv_households')
        population = self.cleaned_data.get('population')
        
        if tv_households and population:
            if tv_households > population:
                raise ValidationError(
                    _('TV households cannot exceed total population.')
                )
            
            # Reasonable ratio check (assuming max 1 household per 2 people)
            if tv_households > population / 2:
                raise ValidationError(
                    _('TV households ratio seems unrealistic. Please verify the numbers.')
                )
        
        return tv_households
    
    def clean_parent_zone(self):
        """
        Validate parent zone to prevent circular references.
        """
        parent_zone = self.cleaned_data.get('parent_zone')
        
        if parent_zone and self.instance and self.instance.pk:
            # Check for circular reference
            current = parent_zone
            while current:
                if current.pk == self.instance.pk:
                    raise ValidationError(
                        _('Cannot set parent zone that would create a circular reference.')
                    )
                current = current.parent_zone
        
        return parent_zone


class BroadcastNetworkForm(forms.ModelForm):
    """
    Form for creating and editing Broadcast Networks.
    
    Includes validation for network hierarchy and metadata.
    """
    
    class Meta:
        model = BroadcastNetwork
        fields = [
            'name',
            'short_name',
            'network_type',
            'parent_network',
            'founded_date',
            'headquarters_location',
            'website_url',
            'logo_url',
            'description',
            'target_demographics',
            'is_active'
        ]
        
        widgets = {
            'name': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Enter network name')
            }),
            'short_name': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Enter short name or abbreviation')
            }),
            'network_type': forms.Select(attrs={
                'class': 'form-select'
            }),
            'parent_network': forms.Select(attrs={
                'class': 'form-select'
            }),
            'founded_date': forms.DateInput(attrs={
                'class': 'form-control',
                'type': 'date'
            }),
            'headquarters_location': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('City, State/Country')
            }),
            'website_url': forms.URLInput(attrs={
                'class': 'form-control',
                'placeholder': _('https://example.com')
            }),
            'logo_url': forms.URLInput(attrs={
                'class': 'form-control',
                'placeholder': _('https://example.com/logo.png')
            }),
            'description': forms.Textarea(attrs={
                'class': 'form-control',
                'rows': 4,
                'placeholder': _('Network description and background')
            }),
            'target_demographics': forms.Textarea(attrs={
                'class': 'form-control',
                'rows': 3,
                'placeholder': _('Target audience demographics')
            }),
            'is_active': forms.CheckboxInput(attrs={
                'class': 'form-check-input'
            })
        }
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # Filter parent networks to prevent circular references
        if self.instance and self.instance.pk:
            # Exclude self and any descendants
            excluded_ids = [self.instance.pk]
            descendants = self.instance.get_all_subsidiaries()
            excluded_ids.extend([desc.pk for desc in descendants])
            
            self.fields['parent_network'].queryset = BroadcastNetwork.objects.filter(
                is_active=True
            ).exclude(pk__in=excluded_ids)
        else:
            self.fields['parent_network'].queryset = BroadcastNetwork.objects.filter(
                is_active=True
            )
        
        # Make parent network optional
        self.fields['parent_network'].required = False
        self.fields['parent_network'].empty_label = _('No parent network (independent)')
    
    def clean_short_name(self):
        """
        Validate short name format and uniqueness.
        """
        short_name = self.cleaned_data.get('short_name')
        if short_name:
            # Check uniqueness
            existing = BroadcastNetwork.objects.filter(short_name__iexact=short_name)
            if self.instance and self.instance.pk:
                existing = existing.exclude(pk=self.instance.pk)
            
            if existing.exists():
                raise ValidationError(
                    _('A network with this short name already exists.')
                )
        
        return short_name
    
    def clean_founded_date(self):
        """
        Validate founded date is not in the future.
        """
        founded_date = self.cleaned_data.get('founded_date')
        if founded_date and founded_date > timezone.now().date():
            raise ValidationError(
                _('Founded date cannot be in the future.')
            )
        return founded_date


class TVChannelForm(forms.ModelForm):
    """
    Form for creating and editing TV Channels.
    
    Comprehensive form with validation for channel specifications and metadata.
    """
    
    class Meta:
        model = TVChannel
        fields = [
            'name',
            'call_sign',
            'channel_number',
            'channel_type',
            'content_category',
            'network',
            'primary_language',
            'secondary_languages',
            'target_demographics',
            'description',
            'website_url',
            'logo_url',
            'hd_available',
            'uhd_4k_available',
            'is_premium',
            'advertising_enabled',
            'launch_date',
            'is_active'
        ]
        
        widgets = {
            'name': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Enter channel name')
            }),
            'call_sign': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Enter call sign (e.g., WABC)')
            }),
            'channel_number': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Channel number (e.g., 7.1, 102)')
            }),
            'channel_type': forms.Select(attrs={
                'class': 'form-select'
            }),
            'content_category': forms.Select(attrs={
                'class': 'form-select'
            }),
            'network': forms.Select(attrs={
                'class': 'form-select'
            }),
            'primary_language': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Primary language (e.g., English)')
            }),
            'secondary_languages': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Secondary languages (comma-separated)')
            }),
            'target_demographics': forms.Textarea(attrs={
                'class': 'form-control',
                'rows': 3,
                'placeholder': _('Target audience demographics')
            }),
            'description': forms.Textarea(attrs={
                'class': 'form-control',
                'rows': 4,
                'placeholder': _('Channel description and programming focus')
            }),
            'website_url': forms.URLInput(attrs={
                'class': 'form-control',
                'placeholder': _('https://example.com')
            }),
            'logo_url': forms.URLInput(attrs={
                'class': 'form-control',
                'placeholder': _('https://example.com/logo.png')
            }),
            'launch_date': forms.DateInput(attrs={
                'class': 'form-control',
                'type': 'date'
            }),
            'hd_available': forms.CheckboxInput(attrs={
                'class': 'form-check-input'
            }),
            'uhd_4k_available': forms.CheckboxInput(attrs={
                'class': 'form-check-input'
            }),
            'is_premium': forms.CheckboxInput(attrs={
                'class': 'form-check-input'
            }),
            'advertising_enabled': forms.CheckboxInput(attrs={
                'class': 'form-check-input'
            }),
            'is_active': forms.CheckboxInput(attrs={
                'class': 'form-check-input'
            })
        }
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # Filter networks to only active ones
        self.fields['network'].queryset = BroadcastNetwork.objects.filter(
            is_active=True
        ).order_by('name')
    
    def clean_call_sign(self):
        """
        Validate call sign format and uniqueness.
        """
        call_sign = self.cleaned_data.get('call_sign')
        if call_sign:
            # Validate format (3-6 letters, optionally followed by -TV, -FM, etc.)
            if not re.match(r'^[A-Z]{3,6}(-[A-Z]{1,3})?$', call_sign.upper()):
                raise ValidationError(
                    _('Call sign must be 3-6 letters, optionally followed by a suffix (e.g., WABC-TV).')
                )
            
            # Check uniqueness
            existing = TVChannel.objects.filter(call_sign__iexact=call_sign)
            if self.instance and self.instance.pk:
                existing = existing.exclude(pk=self.instance.pk)
            
            if existing.exists():
                raise ValidationError(
                    _('A channel with this call sign already exists.')
                )
            
            return call_sign.upper()
        return call_sign
    
    def clean_channel_number(self):
        """
        Validate channel number format and uniqueness within network.
        """
        channel_number = self.cleaned_data.get('channel_number')
        network = self.cleaned_data.get('network')
        
        if channel_number:
            # Validate format (number, optionally with decimal subchannel)
            if not re.match(r'^\d+(\.\d+)?$', channel_number):
                raise ValidationError(
                    _('Channel number must be a number, optionally with decimal subchannel (e.g., 7.1).')
                )
            
            # Check uniqueness within network
            if network:
                existing = TVChannel.objects.filter(
                    network=network,
                    channel_number=channel_number
                )
                if self.instance and self.instance.pk:
                    existing = existing.exclude(pk=self.instance.pk)
                
                if existing.exists():
                    raise ValidationError(
                        _('A channel with this number already exists in the selected network.')
                    )
        
        return channel_number
    
    def clean_launch_date(self):
        """
        Validate launch date is reasonable.
        """
        launch_date = self.cleaned_data.get('launch_date')
        if launch_date:
            # Don't allow dates too far in the future
            max_future_date = timezone.now().date() + timedelta(days=365)
            if launch_date > max_future_date:
                raise ValidationError(
                    _('Launch date cannot be more than one year in the future.')
                )
            
            # Don't allow dates before television was invented
            if launch_date.year < 1920:
                raise ValidationError(
                    _('Launch date seems unrealistic for television broadcasting.')
                )
        
        return launch_date
    
    def clean(self):
        """
        Cross-field validation.
        """
        cleaned_data = super().clean()
        
        # UHD requires HD
        hd_available = cleaned_data.get('hd_available')
        uhd_4k_available = cleaned_data.get('uhd_4k_available')
        
        if uhd_4k_available and not hd_available:
            raise ValidationError(
                _('UHD/4K availability requires HD to be available as well.')
            )
        
        return cleaned_data


class ChannelCoverageForm(forms.ModelForm):
    """
    Form for managing Channel Coverage relationships.
    
    Handles the many-to-many relationship between channels and zones.
    """
    
    class Meta:
        model = ChannelCoverage
        fields = [
            'channel',
            'zone',
            'signal_strength',
            'coverage_percentage',
            'subscriber_count',
            'penetration_rate',
            'launch_date_in_zone',
            'notes',
            'is_active'
        ]
        
        widgets = {
            'channel': forms.Select(attrs={
                'class': 'form-select'
            }),
            'zone': forms.Select(attrs={
                'class': 'form-select'
            }),
            'signal_strength': forms.Select(attrs={
                'class': 'form-select'
            }),
            'coverage_percentage': forms.NumberInput(attrs={
                'class': 'form-control',
                'min': 0,
                'max': 100,
                'step': 0.1,
                'placeholder': _('Coverage percentage (0-100)')
            }),
            'subscriber_count': forms.NumberInput(attrs={
                'class': 'form-control',
                'min': 0,
                'placeholder': _('Number of subscribers')
            }),
            'penetration_rate': forms.NumberInput(attrs={
                'class': 'form-control',
                'min': 0,
                'max': 100,
                'step': 0.1,
                'placeholder': _('Penetration rate (0-100)')
            }),
            'launch_date_in_zone': forms.DateInput(attrs={
                'class': 'form-control',
                'type': 'date'
            }),
            'notes': forms.Textarea(attrs={
                'class': 'form-control',
                'rows': 3,
                'placeholder': _('Additional notes about coverage')
            }),
            'is_active': forms.CheckboxInput(attrs={
                'class': 'form-check-input'
            })
        }
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # Filter to only active channels and zones
        self.fields['channel'].queryset = TVChannel.objects.filter(
            is_active=True
        ).order_by('network__name', 'channel_number')
        
        self.fields['zone'].queryset = GeographicZone.objects.filter(
            is_active=True
        ).order_by('zone_type', 'name')
    
    def clean_coverage_percentage(self):
        """
        Validate coverage percentage is within valid range.
        """
        coverage_percentage = self.cleaned_data.get('coverage_percentage')
        if coverage_percentage is not None:
            if coverage_percentage < 0 or coverage_percentage > 100:
                raise ValidationError(
                    _('Coverage percentage must be between 0 and 100.')
                )
        return coverage_percentage
    
    def clean_penetration_rate(self):
        """
        Validate penetration rate is within valid range.
        """
        penetration_rate = self.cleaned_data.get('penetration_rate')
        if penetration_rate is not None:
            if penetration_rate < 0 or penetration_rate > 100:
                raise ValidationError(
                    _('Penetration rate must be between 0 and 100.')
                )
        return penetration_rate
    
    def clean(self):
        """
        Cross-field validation for coverage data.
        """
        cleaned_data = super().clean()
        
        channel = cleaned_data.get('channel')
        zone = cleaned_data.get('zone')
        subscriber_count = cleaned_data.get('subscriber_count')
        
        # Check for duplicate coverage
        if channel and zone:
            existing = ChannelCoverage.objects.filter(
                channel=channel,
                zone=zone
            )
            if self.instance and self.instance.pk:
                existing = existing.exclude(pk=self.instance.pk)
            
            if existing.exists():
                raise ValidationError(
                    _('Coverage relationship between this channel and zone already exists.')
                )
        
        # Validate subscriber count against zone households
        if subscriber_count and zone and zone.tv_households:
            if subscriber_count > zone.tv_households:
                raise ValidationError(
                    _('Subscriber count cannot exceed total TV households in the zone.')
                )
        
        return cleaned_data


class ContentScheduleForm(forms.ModelForm):
    """
    Form for managing Content Schedule (EPG) entries.
    
    Handles programming schedule with advertising break information.
    """
    
    class Meta:
        model = ContentSchedule
        fields = [
            'channel',
            'title',
            'description',
            'content_type',
            'genre',
            'rating',
            'start_time',
            'duration_minutes',
            'target_demographics',
            'advertising_breaks',
            'ad_break_duration',
            'is_live',
            'is_premiere',
            'external_id'
        ]
        
        widgets = {
            'channel': forms.Select(attrs={
                'class': 'form-select'
            }),
            'title': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Program title')
            }),
            'description': forms.Textarea(attrs={
                'class': 'form-control',
                'rows': 4,
                'placeholder': _('Program description')
            }),
            'content_type': forms.Select(attrs={
                'class': 'form-select'
            }),
            'genre': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Genre (e.g., Drama, Comedy, News)')
            }),
            'rating': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Content rating (e.g., TV-PG, TV-14)')
            }),
            'start_time': forms.DateTimeInput(attrs={
                'class': 'form-control',
                'type': 'datetime-local'
            }),
            'duration_minutes': forms.NumberInput(attrs={
                'class': 'form-control',
                'min': 1,
                'placeholder': _('Duration in minutes')
            }),
            'target_demographics': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('Target demographics')
            }),
            'advertising_breaks': forms.NumberInput(attrs={
                'class': 'form-control',
                'min': 0,
                'placeholder': _('Number of ad breaks')
            }),
            'ad_break_duration': forms.NumberInput(attrs={
                'class': 'form-control',
                'min': 0,
                'placeholder': _('Total ad duration in seconds')
            }),
            'is_live': forms.CheckboxInput(attrs={
                'class': 'form-check-input'
            }),
            'is_premiere': forms.CheckboxInput(attrs={
                'class': 'form-check-input'
            }),
            'external_id': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': _('External system ID (optional)')
            })
        }
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # Filter to only active channels
        self.fields['channel'].queryset = TVChannel.objects.filter(
            is_active=True
        ).order_by('network__name', 'channel_number')
    
    def clean_duration_minutes(self):
        """
        Validate duration is reasonable.
        """
        duration_minutes = self.cleaned_data.get('duration_minutes')
        if duration_minutes:
            if duration_minutes < 1:
                raise ValidationError(
                    _('Duration must be at least 1 minute.')
                )
            if duration_minutes > 1440:  # 24 hours
                raise ValidationError(
                    _('Duration cannot exceed 24 hours.')
                )
        return duration_minutes
    
    def clean(self):
        """
        Cross-field validation for schedule data.
        """
        cleaned_data = super().clean()
        
        channel = cleaned_data.get('channel')
        start_time = cleaned_data.get('start_time')
        duration_minutes = cleaned_data.get('duration_minutes')
        advertising_breaks = cleaned_data.get('advertising_breaks', 0)
        ad_break_duration = cleaned_data.get('ad_break_duration', 0)
        
        # Calculate end time
        if start_time and duration_minutes:
            end_time = start_time + timedelta(minutes=duration_minutes)
            
            # Check for scheduling conflicts
            if channel:
                conflicts = ContentSchedule.objects.filter(
                    channel=channel,
                    start_time__lt=end_time,
                    end_time__gt=start_time
                )
                
                if self.instance and self.instance.pk:
                    conflicts = conflicts.exclude(pk=self.instance.pk)
                
                if conflicts.exists():
                    raise ValidationError(
                        _('This program conflicts with existing schedule entries.')
                    )
        
        # Validate advertising data consistency
        if advertising_breaks > 0 and ad_break_duration == 0:
            raise ValidationError(
                _('Ad break duration must be specified when advertising breaks are present.')
            )
        
        if advertising_breaks == 0 and ad_break_duration > 0:
            raise ValidationError(
                _('Number of advertising breaks must be specified when ad duration is set.')
            )
        
        # Validate ad duration is reasonable
        if ad_break_duration and duration_minutes:
            max_ad_duration = duration_minutes * 60 * 0.3  # Max 30% ads
            if ad_break_duration > max_ad_duration:
                raise ValidationError(
                    _('Advertising duration seems excessive for program length.')
                )
        
        return cleaned_data


class ChannelSearchForm(forms.Form):
    """
    Form for searching and filtering channels.
    
    Provides comprehensive search and filtering options for channel listings.
    """
    
    search = forms.CharField(
        required=False,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': _('Search channels, call signs, or numbers...'),
            'autocomplete': 'off'
        })
    )
    
    channel_type = forms.ChoiceField(
        required=False,
        choices=[('', _('All Types'))] + list(TVChannel.CHANNEL_TYPE_CHOICES),
        widget=forms.Select(attrs={
            'class': 'form-select'
        })
    )
    
    content_category = forms.ChoiceField(
        required=False,
        choices=[('', _('All Categories'))] + list(TVChannel.CONTENT_CATEGORY_CHOICES),
        widget=forms.Select(attrs={
            'class': 'form-select'
        })
    )
    
    network = forms.ModelChoiceField(
        required=False,
        queryset=BroadcastNetwork.objects.filter(is_active=True).order_by('name'),
        empty_label=_('All Networks'),
        widget=forms.Select(attrs={
            'class': 'form-select'
        })
    )
    
    zone = forms.ModelChoiceField(
        required=False,
        queryset=GeographicZone.objects.filter(
            is_active=True,
            zone_type__in=['country', 'state', 'city']
        ).order_by('zone_type', 'name'),
        empty_label=_('All Zones'),
        widget=forms.Select(attrs={
            'class': 'form-select'
        })
    )
    
    hd_available = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        })
    )
    
    uhd_4k_available = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        })
    )
    
    is_premium = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        })
    )
    
    advertising_enabled = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        })
    )
    
    sort_by = forms.ChoiceField(
        required=False,
        choices=[
            ('channel_number', _('Channel Number')),
            ('name', _('Name')),
            ('launch_date', _('Launch Date (Oldest First)')),
            ('-launch_date', _('Launch Date (Newest First)')),
            ('network__name', _('Network Name'))
        ],
        initial='channel_number',
        widget=forms.Select(attrs={
            'class': 'form-select'
        })
    )