"""
Django Forms for Streams Application

This module contains form definitions for creating and editing
streaming channels, configurations, and related models.
"""

import re
from django import forms
from django.core.validators import URLValidator
from django.utils.text import slugify
from apps.streams.models import Channel, VideoConfiguration, AudioConfiguration


class ChannelForm(forms.ModelForm):
    """
    Form for creating and editing streaming channels.
    
    Provides validation for channel configuration including
    URL validation, slug generation, and configuration defaults.
    """
    
    class Meta:
        model = Channel
        fields = [
            'name', 'hls_url', 'description', 'is_active',
            'output_directory', 'segment_duration', 'max_segments',
            'retry_attempts', 'retry_interval'
        ]
        
        widgets = {
            'name': forms.TextInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'placeholder': 'Enter channel name'
            }),
            'hls_url': forms.URLInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'placeholder': 'https://example.com/stream.m3u8'
            }),
            'description': forms.Textarea(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'rows': 3,
                'placeholder': 'Optional description of the channel'
            }),
            'output_directory': forms.HiddenInput(),
            'segment_duration': forms.NumberInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'min': 1,
                'max': 60
            }),
            'max_segments': forms.NumberInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'min': 1,
                'max': 100
            }),
            'retry_attempts': forms.NumberInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'min': 1,
                'max': 20
            }),
            'retry_interval': forms.NumberInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'min': 1,
                'max': 300
            }),
        }
        
        help_texts = {
            'name': 'Human-readable name for the channel',
            'hls_url': 'HTTP Live Streaming URL to capture',
            'segment_duration': 'Duration of each HLS segment in seconds (1-60)',
            'max_segments': 'Maximum number of segments to keep (1-100)',
            'retry_attempts': 'Number of retry attempts on failure (1-20)',
            'retry_interval': 'Seconds between retry attempts (1-300)',
        }
    
    def __init__(self, *args, **kwargs):
        """Initialize form with default values and custom styling."""
        super().__init__(*args, **kwargs)
        
        # Set default output directory if not provided
        if not self.instance.pk:
            # Set a temporary default that will be overridden in clean()
            self.initial['output_directory'] = '/app/media/streams/temp'
    
    def clean(self):
        """
        Generate slug and output directory from name during form validation.
        """
        cleaned_data = super().clean()
        name = cleaned_data.get('name')
        
        if name:
            # Auto-generate slug from name
            base_slug = slugify(name).lower()
            # Ensure slug meets validation requirements
            base_slug = re.sub(r'[^a-z0-9-]', '', base_slug)
            base_slug = re.sub(r'-+', '-', base_slug)  # Remove consecutive hyphens
            base_slug = base_slug.strip('-')  # Remove leading/trailing hyphens
            
            # If slug starts with number, prefix with 'channel-'
            if base_slug and base_slug[0].isdigit():
                base_slug = f"channel-{base_slug}"
            
            # Ensure minimum length
            if len(base_slug) < 3:
                base_slug = f"channel-{base_slug or 'new'}"[:50]
            
            # Ensure uniqueness by appending number if needed
            slug = base_slug
            counter = 1
            while True:
                existing = Channel.objects.filter(slug=slug)
                if self.instance.pk:
                    existing = existing.exclude(pk=self.instance.pk)
                
                if not existing.exists():
                    break
                    
                slug = f"{base_slug}-{counter}"
                counter += 1
            
            # Set the slug in cleaned_data
            cleaned_data['slug'] = slug
            
            # Auto-generate output directory
            cleaned_data['output_directory'] = f"/app/media/streams/{slug}"
        
        return cleaned_data
    
    def save(self, commit=True):
        """
        Save the channel with auto-generated slug and output directory.
        """
        channel = super().save(commit=False)
        
        # Set slug and output directory from cleaned data
        if hasattr(self, 'cleaned_data'):
            if 'slug' in self.cleaned_data:
                channel.slug = self.cleaned_data['slug']
            if 'output_directory' in self.cleaned_data:
                channel.output_directory = self.cleaned_data['output_directory']
        
        if commit:
            channel.save()
        
        return channel
    
    def clean_output_directory(self):
        """
        Auto-generate output directory based on channel name.
        
        Returns:
            str: Generated output directory path
        """
        name = self.cleaned_data.get('name')
        
        if name:
            # Generate directory path as media/streams/{channel_name}
            from django.utils.text import slugify
            channel_slug = slugify(name)
            return f"/app/media/streams/{channel_slug}"
        
        # Fallback to default if no name provided
        from django.conf import settings
        return settings.STREAM_CONFIG['OUTPUT_DIR']
    
    def clean_hls_url(self):
        """
        Validate HLS URL format and accessibility.
        
        Returns:
            str: Validated HLS URL
            
        Raises:
            ValidationError: If URL is invalid or inaccessible
        """
        url = self.cleaned_data.get('hls_url')
        
        if not url:
            return url
        
        # Basic URL validation
        validator = URLValidator()
        validator(url)
        
        # Check if URL ends with .m3u8 (HLS playlist)
        if not url.lower().endswith('.m3u8'):
            raise forms.ValidationError('HLS URL should end with .m3u8')
        
        return url


class VideoConfigurationForm(forms.ModelForm):
    """
    Form for creating and editing video encoding configurations.
    """
    
    class Meta:
        model = VideoConfiguration
        fields = [
            'name', 'resolution', 'aspect_ratio', 'frame_rate',
            'min_bitrate', 'max_bitrate', 'codec', 'preset',
            'profile', 'level'
        ]
        
        widgets = {
            'name': forms.TextInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'placeholder': 'Configuration name'
            }),
            'resolution': forms.TextInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'placeholder': '1920x1080'
            }),
            'aspect_ratio': forms.TextInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'placeholder': '16:9'
            }),
            'frame_rate': forms.NumberInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'min': 1,
                'max': 120
            }),
            'min_bitrate': forms.TextInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'placeholder': '2000k'
            }),
            'max_bitrate': forms.TextInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'placeholder': '4000k'
            }),
            'codec': forms.Select(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500'
            }),
            'preset': forms.Select(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500'
            }),
            'profile': forms.Select(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500'
            }),
            'level': forms.TextInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'placeholder': '3.1'
            }),
        }
    
    def clean_resolution(self):
        """
        Validate resolution format (WIDTHxHEIGHT).
        
        Returns:
            str: Validated resolution
            
        Raises:
            ValidationError: If resolution format is invalid
        """
        resolution = self.cleaned_data.get('resolution')
        
        if resolution:
            parts = resolution.split('x')
            if len(parts) != 2:
                raise forms.ValidationError('Resolution must be in format WIDTHxHEIGHT (e.g., 1920x1080)')
            
            try:
                width, height = int(parts[0]), int(parts[1])
                if width <= 0 or height <= 0:
                    raise ValueError
            except ValueError:
                raise forms.ValidationError('Resolution dimensions must be positive integers')
        
        return resolution


class AudioConfigurationForm(forms.ModelForm):
    """
    Form for creating and editing audio encoding configurations.
    """
    
    class Meta:
        model = AudioConfiguration
        fields = [
            'name', 'codec', 'bitrate', 'sample_rate',
            'channels', 'normalize'
        ]
        
        widgets = {
            'name': forms.TextInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'placeholder': 'Configuration name'
            }),
            'codec': forms.Select(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500'
            }),
            'bitrate': forms.TextInput(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500',
                'placeholder': '128k'
            }),
            'sample_rate': forms.Select(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500'
            }),
            'channels': forms.Select(attrs={
                'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500'
            }),
            'normalize': forms.CheckboxInput(attrs={
                'class': 'focus:ring-primary-500 h-4 w-4 text-primary-600 border-gray-300 rounded'
            }),
        }
    
    def clean_bitrate(self):
        """
        Validate audio bitrate format.
        
        Returns:
            str: Validated bitrate
            
        Raises:
            ValidationError: If bitrate format is invalid
        """
        bitrate = self.cleaned_data.get('bitrate')
        
        if bitrate:
            # Check if bitrate ends with 'k' and has a valid number
            if not bitrate.lower().endswith('k'):
                raise forms.ValidationError('Bitrate must end with "k" (e.g., 128k)')
            
            try:
                rate = int(bitrate[:-1])
                if rate <= 0:
                    raise ValueError
            except ValueError:
                raise forms.ValidationError('Bitrate must be a positive number followed by "k"')
        
        return bitrate


class StreamStartForm(forms.Form):
    """
    Form for starting a stream with optional configuration selection.
    """
    
    video_config = forms.ModelChoiceField(
        queryset=VideoConfiguration.objects.all(),
        required=False,
        empty_label="Use default video settings",
        widget=forms.Select(attrs={
            'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500'
        })
    )
    
    audio_config = forms.ModelChoiceField(
        queryset=AudioConfiguration.objects.all(),
        required=False,
        empty_label="Use default audio settings",
        widget=forms.Select(attrs={
            'class': 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500'
        })
    )
