# -*- coding: utf-8 -*-
"""
Notifications Forms

This module contains Django forms for the notifications app,
including forms for filtering, creating, and managing notifications
and user preferences.

Forms:
    - NotificationFilterForm: Filter notifications
    - NotificationPreferenceForm: Manage user preferences
    - NotificationCreateForm: Create notifications
    - BulkNotificationForm: Send bulk notifications
    - NotificationTemplateForm: Manage templates

Author: AdTlas Development Team
Version: 1.0.0
Last Updated: 2024
"""

import json
from datetime import datetime, timedelta
from django import forms
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.contrib.admin.widgets import AdminDateWidget, AdminTimeWidget
from django.forms.widgets import CheckboxSelectMultiple, SelectMultiple

from apps.core.forms import BaseForm
from .models import (
    NotificationType,
    Notification,
    NotificationPreference,
    NotificationTemplate,
    NotificationQueue,
)

User = get_user_model()


class NotificationFilterForm(BaseForm):
    """
    Form for filtering notifications in list views.
    
    This form provides various filtering options for notifications
    including type, read status, date range, and search.
    """
    
    notification_type = forms.ModelChoiceField(
        queryset=NotificationType.objects.active(),
        required=False,
        empty_label=_('All Types'),
        widget=forms.Select(attrs={
            'class': 'form-control',
            'id': 'filter-type'
        }),
        label=_('Notification Type')
    )
    
    read_status = forms.ChoiceField(
        choices=[
            ('', _('All')),
            ('read', _('Read')),
            ('unread', _('Unread')),
        ],
        required=False,
        widget=forms.Select(attrs={
            'class': 'form-control',
            'id': 'filter-status'
        }),
        label=_('Read Status')
    )
    
    show_archived = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input',
            'id': 'filter-archived'
        }),
        label=_('Show Archived')
    )
    
    date_from = forms.DateField(
        required=False,
        widget=forms.DateInput(attrs={
            'class': 'form-control',
            'type': 'date',
            'id': 'filter-date-from'
        }),
        label=_('From Date')
    )
    
    date_to = forms.DateField(
        required=False,
        widget=forms.DateInput(attrs={
            'class': 'form-control',
            'type': 'date',
            'id': 'filter-date-to'
        }),
        label=_('To Date')
    )
    
    search = forms.CharField(
        required=False,
        max_length=255,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': _('Search in title and message...'),
            'id': 'filter-search'
        }),
        label=_('Search')
    )
    
    def clean(self):
        """
        Validate the form data.
        
        Returns:
            dict: Cleaned data
        
        Raises:
            ValidationError: If validation fails
        """
        cleaned_data = super().clean()
        date_from = cleaned_data.get('date_from')
        date_to = cleaned_data.get('date_to')
        
        # Validate date range
        if date_from and date_to:
            if date_from > date_to:
                raise ValidationError(_('From date cannot be later than to date.'))
            
            # Check if date range is not too large (e.g., more than 1 year)
            if (date_to - date_from).days > 365:
                raise ValidationError(_('Date range cannot exceed 365 days.'))
        
        return cleaned_data


class NotificationPreferenceForm(BaseForm):
    """
    Form for managing user notification preferences.
    
    This form allows users to configure their notification
    preferences for different notification types.
    """
    
    is_enabled = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        }),
        label=_('Enable Notifications')
    )
    
    email_enabled = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        }),
        label=_('Email Notifications')
    )
    
    in_app_enabled = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        }),
        label=_('In-App Notifications')
    )
    
    quiet_hours_start = forms.TimeField(
        required=False,
        widget=forms.TimeInput(attrs={
            'class': 'form-control',
            'type': 'time'
        }),
        label=_('Quiet Hours Start')
    )
    
    quiet_hours_end = forms.TimeField(
        required=False,
        widget=forms.TimeInput(attrs={
            'class': 'form-control',
            'type': 'time'
        }),
        label=_('Quiet Hours End')
    )
    
    timezone = forms.CharField(
        required=False,
        max_length=50,
        widget=forms.Select(attrs={
            'class': 'form-control'
        }),
        label=_('Timezone')
    )
    
    class Meta:
        model = NotificationPreference
        fields = [
            'is_enabled',
            'email_enabled',
            'in_app_enabled',
            'quiet_hours_start',
            'quiet_hours_end',
            'timezone',
        ]
    
    def __init__(self, *args, **kwargs):
        """
        Initialize the form.
        
        Args:
            *args: Additional arguments
            **kwargs: Additional keyword arguments
        """
        super().__init__(*args, **kwargs)
        
        # Add timezone choices
        import pytz
        timezone_choices = [(tz, tz) for tz in pytz.common_timezones]
        self.fields['timezone'].widget = forms.Select(
            choices=timezone_choices,
            attrs={'class': 'form-control'}
        )
        
        # Set default timezone
        if not self.instance.pk:
            self.fields['timezone'].initial = 'UTC'
    
    def clean(self):
        """
        Validate the form data.
        
        Returns:
            dict: Cleaned data
        
        Raises:
            ValidationError: If validation fails
        """
        cleaned_data = super().clean()
        quiet_start = cleaned_data.get('quiet_hours_start')
        quiet_end = cleaned_data.get('quiet_hours_end')
        
        # Validate quiet hours
        if quiet_start and not quiet_end:
            raise ValidationError(_('Please specify both start and end times for quiet hours.'))
        
        if quiet_end and not quiet_start:
            raise ValidationError(_('Please specify both start and end times for quiet hours.'))
        
        # Check if at least one delivery method is enabled
        if cleaned_data.get('is_enabled'):
            if not cleaned_data.get('email_enabled') and not cleaned_data.get('in_app_enabled'):
                raise ValidationError(_('At least one delivery method must be enabled.'))
        
        return cleaned_data


class NotificationCreateForm(BaseForm):
    """
    Form for creating individual notifications.
    
    This form allows administrators to create and send
    notifications to specific users.
    """
    
    recipient = forms.ModelChoiceField(
        queryset=User.objects.filter(is_active=True),
        widget=forms.Select(attrs={
            'class': 'form-control',
            'data-live-search': 'true'
        }),
        label=_('Recipient')
    )
    
    notification_type = forms.ModelChoiceField(
        queryset=NotificationType.objects.active(),
        widget=forms.Select(attrs={
            'class': 'form-control'
        }),
        label=_('Notification Type')
    )
    
    title = forms.CharField(
        max_length=255,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': _('Enter notification title...')
        }),
        label=_('Title')
    )
    
    message = forms.CharField(
        widget=forms.Textarea(attrs={
            'class': 'form-control',
            'rows': 4,
            'placeholder': _('Enter notification message...')
        }),
        label=_('Message')
    )
    
    action_url = forms.URLField(
        required=False,
        widget=forms.URLInput(attrs={
            'class': 'form-control',
            'placeholder': _('https://example.com/action')
        }),
        label=_('Action URL')
    )
    
    expires_at = forms.DateTimeField(
        required=False,
        widget=forms.DateTimeInput(attrs={
            'class': 'form-control',
            'type': 'datetime-local'
        }),
        label=_('Expires At')
    )
    
    send_immediately = forms.BooleanField(
        required=False,
        initial=True,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        }),
        label=_('Send Immediately')
    )
    
    class Meta:
        model = Notification
        fields = [
            'recipient',
            'notification_type',
            'title',
            'message',
            'action_url',
            'expires_at',
        ]
    
    def clean_expires_at(self):
        """
        Validate expiration date.
        
        Returns:
            datetime: Cleaned expiration date
        
        Raises:
            ValidationError: If expiration date is in the past
        """
        expires_at = self.cleaned_data.get('expires_at')
        
        if expires_at and expires_at <= timezone.now():
            raise ValidationError(_('Expiration date must be in the future.'))
        
        return expires_at
    
    def save(self, commit=True):
        """
        Save the notification.
        
        Args:
            commit: Whether to save to database
        
        Returns:
            Notification: Created notification
        """
        notification = super().save(commit=False)
        
        if commit:
            notification.save()
        
        return notification


class BulkNotificationForm(BaseForm):
    """
    Form for sending bulk notifications.
    
    This form allows administrators to send notifications
    to multiple users at once.
    """
    
    recipients = forms.ModelMultipleChoiceField(
        queryset=User.objects.filter(is_active=True),
        widget=CheckboxSelectMultiple(attrs={
            'class': 'form-check-input'
        }),
        label=_('Recipients')
    )
    
    recipient_groups = forms.MultipleChoiceField(
        choices=[
            ('all_users', _('All Users')),
            ('active_users', _('Active Users')),
            ('staff_users', _('Staff Users')),
            ('premium_users', _('Premium Users')),
        ],
        required=False,
        widget=CheckboxSelectMultiple(attrs={
            'class': 'form-check-input'
        }),
        label=_('Recipient Groups')
    )
    
    notification_type = forms.ModelChoiceField(
        queryset=NotificationType.objects.active(),
        widget=forms.Select(attrs={
            'class': 'form-control'
        }),
        label=_('Notification Type')
    )
    
    title = forms.CharField(
        max_length=255,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': _('Enter notification title...')
        }),
        label=_('Title')
    )
    
    message = forms.CharField(
        widget=forms.Textarea(attrs={
            'class': 'form-control',
            'rows': 4,
            'placeholder': _('Enter notification message...')
        }),
        label=_('Message')
    )
    
    action_url = forms.URLField(
        required=False,
        widget=forms.URLInput(attrs={
            'class': 'form-control',
            'placeholder': _('https://example.com/action')
        }),
        label=_('Action URL')
    )
    
    expires_at = forms.DateTimeField(
        required=False,
        widget=forms.DateTimeInput(attrs={
            'class': 'form-control',
            'type': 'datetime-local'
        }),
        label=_('Expires At')
    )
    
    send_immediately = forms.BooleanField(
        required=False,
        initial=True,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        }),
        label=_('Send Immediately')
    )
    
    def clean(self):
        """
        Validate the form data.
        
        Returns:
            dict: Cleaned data
        
        Raises:
            ValidationError: If validation fails
        """
        cleaned_data = super().clean()
        recipients = cleaned_data.get('recipients')
        recipient_groups = cleaned_data.get('recipient_groups')
        
        # Check if at least one recipient is selected
        if not recipients and not recipient_groups:
            raise ValidationError(_('Please select at least one recipient or recipient group.'))
        
        # Validate expiration date
        expires_at = cleaned_data.get('expires_at')
        if expires_at and expires_at <= timezone.now():
            raise ValidationError(_('Expiration date must be in the future.'))
        
        return cleaned_data
    
    def get_recipients(self):
        """
        Get all recipients based on selected users and groups.
        
        Returns:
            QuerySet: All recipients
        """
        recipients = set()
        
        # Add individually selected recipients
        if self.cleaned_data.get('recipients'):
            recipients.update(self.cleaned_data['recipients'])
        
        # Add recipients from groups
        recipient_groups = self.cleaned_data.get('recipient_groups', [])
        
        if 'all_users' in recipient_groups:
            recipients.update(User.objects.all())
        
        if 'active_users' in recipient_groups:
            recipients.update(User.objects.filter(is_active=True))
        
        if 'staff_users' in recipient_groups:
            recipients.update(User.objects.filter(is_staff=True))
        
        if 'premium_users' in recipient_groups:
            # Assuming there's a premium user field or related model
            recipients.update(User.objects.filter(is_premium=True))
        
        return list(recipients)


class NotificationTemplateForm(BaseForm):
    """
    Form for creating and editing notification templates.
    
    This form allows administrators to create reusable
    notification templates.
    """
    
    name = forms.CharField(
        max_length=100,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': _('Enter template name...')
        }),
        label=_('Template Name')
    )
    
    template_type = forms.ChoiceField(
        choices=NotificationTemplate.TEMPLATE_TYPES,
        widget=forms.Select(attrs={
            'class': 'form-control'
        }),
        label=_('Template Type')
    )
    
    description = forms.CharField(
        required=False,
        widget=forms.Textarea(attrs={
            'class': 'form-control',
            'rows': 2,
            'placeholder': _('Enter template description...')
        }),
        label=_('Description')
    )
    
    subject = forms.CharField(
        required=False,
        max_length=255,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': _('Enter email subject (for email templates)...')
        }),
        label=_('Email Subject')
    )
    
    html_content = forms.CharField(
        required=False,
        widget=forms.Textarea(attrs={
            'class': 'form-control',
            'rows': 10,
            'placeholder': _('Enter HTML content...')
        }),
        label=_('HTML Content')
    )
    
    text_content = forms.CharField(
        required=False,
        widget=forms.Textarea(attrs={
            'class': 'form-control',
            'rows': 6,
            'placeholder': _('Enter plain text content...')
        }),
        label=_('Text Content')
    )
    
    variables = forms.CharField(
        required=False,
        widget=forms.Textarea(attrs={
            'class': 'form-control',
            'rows': 4,
            'placeholder': _('Enter available variables as JSON...')
        }),
        label=_('Available Variables (JSON)')
    )
    
    is_active = forms.BooleanField(
        required=False,
        initial=True,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        }),
        label=_('Active')
    )
    
    class Meta:
        model = NotificationTemplate
        fields = [
            'name',
            'template_type',
            'description',
            'subject',
            'html_content',
            'text_content',
            'variables',
            'is_active',
        ]
    
    def clean_variables(self):
        """
        Validate variables JSON.
        
        Returns:
            dict: Cleaned variables
        
        Raises:
            ValidationError: If JSON is invalid
        """
        variables = self.cleaned_data.get('variables')
        
        if variables:
            try:
                parsed_variables = json.loads(variables)
                if not isinstance(parsed_variables, dict):
                    raise ValidationError(_('Variables must be a JSON object.'))
                return parsed_variables
            except json.JSONDecodeError:
                raise ValidationError(_('Invalid JSON format.'))
        
        return {}
    
    def clean(self):
        """
        Validate the form data.
        
        Returns:
            dict: Cleaned data
        
        Raises:
            ValidationError: If validation fails
        """
        cleaned_data = super().clean()
        template_type = cleaned_data.get('template_type')
        html_content = cleaned_data.get('html_content')
        text_content = cleaned_data.get('text_content')
        subject = cleaned_data.get('subject')
        
        # Validate content based on template type
        if template_type == 'email':
            if not subject:
                raise ValidationError(_('Email templates must have a subject.'))
            if not html_content and not text_content:
                raise ValidationError(_('Email templates must have either HTML or text content.'))
        
        elif template_type == 'in_app':
            if not html_content and not text_content:
                raise ValidationError(_('In-app templates must have content.'))
        
        return cleaned_data


class NotificationSearchForm(BaseForm):
    """
    Form for advanced notification search.
    
    This form provides advanced search capabilities
    for notifications with multiple criteria.
    """
    
    query = forms.CharField(
        required=False,
        max_length=255,
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': _('Search notifications...'),
            'autocomplete': 'off'
        }),
        label=_('Search Query')
    )
    
    notification_types = forms.ModelMultipleChoiceField(
        queryset=NotificationType.objects.active(),
        required=False,
        widget=CheckboxSelectMultiple(attrs={
            'class': 'form-check-input'
        }),
        label=_('Notification Types')
    )
    
    read_status = forms.ChoiceField(
        choices=[
            ('', _('Any')),
            ('read', _('Read')),
            ('unread', _('Unread')),
        ],
        required=False,
        widget=forms.Select(attrs={
            'class': 'form-control'
        }),
        label=_('Read Status')
    )
    
    archived_status = forms.ChoiceField(
        choices=[
            ('', _('Any')),
            ('archived', _('Archived')),
            ('active', _('Active')),
        ],
        required=False,
        widget=forms.Select(attrs={
            'class': 'form-control'
        }),
        label=_('Archive Status')
    )
    
    date_range = forms.ChoiceField(
        choices=[
            ('', _('Any time')),
            ('today', _('Today')),
            ('yesterday', _('Yesterday')),
            ('week', _('This week')),
            ('month', _('This month')),
            ('quarter', _('This quarter')),
            ('year', _('This year')),
            ('custom', _('Custom range')),
        ],
        required=False,
        widget=forms.Select(attrs={
            'class': 'form-control',
            'id': 'date-range-select'
        }),
        label=_('Date Range')
    )
    
    custom_date_from = forms.DateField(
        required=False,
        widget=forms.DateInput(attrs={
            'class': 'form-control',
            'type': 'date',
            'id': 'custom-date-from'
        }),
        label=_('From Date')
    )
    
    custom_date_to = forms.DateField(
        required=False,
        widget=forms.DateInput(attrs={
            'class': 'form-control',
            'type': 'date',
            'id': 'custom-date-to'
        }),
        label=_('To Date')
    )
    
    sort_by = forms.ChoiceField(
        choices=[
            ('-created_at', _('Newest first')),
            ('created_at', _('Oldest first')),
            ('-read_at', _('Recently read')),
            ('title', _('Title A-Z')),
            ('-title', _('Title Z-A')),
        ],
        required=False,
        initial='-created_at',
        widget=forms.Select(attrs={
            'class': 'form-control'
        }),
        label=_('Sort By')
    )
    
    def get_date_range(self):
        """
        Get the actual date range based on selection.
        
        Returns:
            tuple: (start_date, end_date) or (None, None)
        """
        date_range = self.cleaned_data.get('date_range')
        
        if date_range == 'custom':
            return (
                self.cleaned_data.get('custom_date_from'),
                self.cleaned_data.get('custom_date_to')
            )
        
        now = timezone.now()
        
        if date_range == 'today':
            start = now.replace(hour=0, minute=0, second=0, microsecond=0)
            return (start.date(), now.date())
        
        elif date_range == 'yesterday':
            yesterday = now - timedelta(days=1)
            start = yesterday.replace(hour=0, minute=0, second=0, microsecond=0)
            return (start.date(), start.date())
        
        elif date_range == 'week':
            start = now - timedelta(days=now.weekday())
            start = start.replace(hour=0, minute=0, second=0, microsecond=0)
            return (start.date(), now.date())
        
        elif date_range == 'month':
            start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
            return (start.date(), now.date())
        
        elif date_range == 'quarter':
            quarter_start_month = ((now.month - 1) // 3) * 3 + 1
            start = now.replace(month=quarter_start_month, day=1, hour=0, minute=0, second=0, microsecond=0)
            return (start.date(), now.date())
        
        elif date_range == 'year':
            start = now.replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0)
            return (start.date(), now.date())
        
        return (None, None)


class NotificationExportForm(BaseForm):
    """
    Form for exporting notifications.
    
    This form allows users to export their notifications
    in various formats with filtering options.
    """
    
    export_format = forms.ChoiceField(
        choices=[
            ('csv', _('CSV')),
            ('json', _('JSON')),
            ('pdf', _('PDF')),
        ],
        widget=forms.Select(attrs={
            'class': 'form-control'
        }),
        label=_('Export Format')
    )
    
    include_read = forms.BooleanField(
        required=False,
        initial=True,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        }),
        label=_('Include Read Notifications')
    )
    
    include_archived = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'form-check-input'
        }),
        label=_('Include Archived Notifications')
    )
    
    date_from = forms.DateField(
        required=False,
        widget=forms.DateInput(attrs={
            'class': 'form-control',
            'type': 'date'
        }),
        label=_('From Date')
    )
    
    date_to = forms.DateField(
        required=False,
        widget=forms.DateInput(attrs={
            'class': 'form-control',
            'type': 'date'
        }),
        label=_('To Date')
    )
    
    notification_types = forms.ModelMultipleChoiceField(
        queryset=NotificationType.objects.active(),
        required=False,
        widget=CheckboxSelectMultiple(attrs={
            'class': 'form-check-input'
        }),
        label=_('Notification Types')
    )
    
    def clean(self):
        """
        Validate the form data.
        
        Returns:
            dict: Cleaned data
        
        Raises:
            ValidationError: If validation fails
        """
        cleaned_data = super().clean()
        date_from = cleaned_data.get('date_from')
        date_to = cleaned_data.get('date_to')
        
        # Validate date range
        if date_from and date_to:
            if date_from > date_to:
                raise ValidationError(_('From date cannot be later than to date.'))
            
            # Check if date range is not too large
            if (date_to - date_from).days > 365:
                raise ValidationError(_('Date range cannot exceed 365 days.'))
        
        return cleaned_data