# -*- coding: utf-8 -*-
"""
Enhanced Authentication Forms for Stream Processor

This module provides forms for enhanced authentication features including:
- Enhanced login form with 2FA support
- Password change form with strength validation
- 2FA setup and management forms
- User profile security settings

Author: Stream Processor Development Team
Version: 1.0.0
Created: 2025
"""

from django import forms
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django.contrib.auth import authenticate
from django.contrib.auth.password_validation import validate_password

from .authentication import UserProfile, validate_strong_password


class EnhancedLoginForm(AuthenticationForm):
    """
    Enhanced login form with 2FA support.
    """
    
    username = forms.CharField(
        max_length=150,
        widget=forms.TextInput(attrs={
            'class': 'appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 focus:z-10 sm:text-sm',
            'placeholder': 'Username',
            'autocomplete': 'username',
            'required': True
        })
    )
    
    password = forms.CharField(
        widget=forms.PasswordInput(attrs={
            'class': 'appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 focus:z-10 sm:text-sm',
            'placeholder': 'Password',
            'autocomplete': 'current-password',
            'required': True
        })
    )
    
    remember_me = forms.BooleanField(
        required=False,
        widget=forms.CheckboxInput(attrs={
            'class': 'h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded'
        })
    )
    
    def __init__(self, request=None, *args, **kwargs):
        super().__init__(request, *args, **kwargs)
        self.request = request
        self.user_cache = None
    
    def clean(self):
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')
        
        if username is not None and password:
            # Check if user exists and get profile
            try:
                user = User.objects.get(username=username)
                profile, created = UserProfile.objects.get_or_create(user=user)
                
                # Check if account is locked
                if profile.is_account_locked():
                    raise ValidationError(
                        _("Account is temporarily locked due to multiple failed login attempts. Please try again later."),
                        code='account_locked'
                    )
                
                # Authenticate user
                self.user_cache = authenticate(
                    self.request,
                    username=username,
                    password=password
                )
                
                if self.user_cache is None:
                    raise ValidationError(
                        _("Please enter a correct username and password. Note that both fields may be case-sensitive."),
                        code='invalid_login'
                    )
                
                # Check if user is active
                if not self.user_cache.is_active:
                    raise ValidationError(
                        _("This account is inactive."),
                        code='inactive'
                    )
                
                # Check if password change is required
                if profile.needs_password_change():
                    raise ValidationError(
                        _("Password change required. Please contact your administrator."),
                        code='password_change_required'
                    )
                
            except User.DoesNotExist:
                raise ValidationError(
                    _("Please enter a correct username and password. Note that both fields may be case-sensitive."),
                    code='invalid_login'
                )
        
        return self.cleaned_data


class TwoFactorForm(forms.Form):
    """
    Form for 2FA token verification.
    """
    
    token = forms.CharField(
        max_length=6,
        min_length=6,
        widget=forms.TextInput(attrs={
            'class': 'appearance-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-md focus:outline-none focus:ring-primary-500 focus:border-primary-500 focus:z-10 sm:text-sm text-center',
            'placeholder': '000000',
            'autocomplete': 'one-time-code',
            'pattern': '[0-9]{6}',
            'inputmode': 'numeric',
            'maxlength': '6',
            'required': True
        })
    )
    
    def __init__(self, user, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.user = user
    
    def clean_token(self):
        token = self.cleaned_data.get('token')
        
        if not token:
            raise ValidationError(_("Please enter the 6-digit code from your authenticator app."))
        
        if not token.isdigit():
            raise ValidationError(_("Token must contain only digits."))
        
        # Verify token
        try:
            profile = UserProfile.objects.get(user=self.user)
            if not profile.verify_2fa_token(token):
                raise ValidationError(_("Invalid or expired token. Please try again."))
        except UserProfile.DoesNotExist:
            raise ValidationError(_("2FA is not set up for this account."))
        
        return token


class EnhancedPasswordChangeForm(PasswordChangeForm):
    """
    Enhanced password change form with strength validation.
    """
    
    old_password = forms.CharField(
        label=_("Current password"),
        strip=False,
        widget=forms.PasswordInput(attrs={
            'class': 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm',
            'autocomplete': 'current-password'
        })
    )
    
    new_password1 = forms.CharField(
        label=_("New password"),
        strip=False,
        widget=forms.PasswordInput(attrs={
            'class': 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm',
            'autocomplete': 'new-password'
        }),
        help_text=_(
            "Password must be at least 12 characters long and contain uppercase, "
            "lowercase, numbers, and special characters."
        )
    )
    
    new_password2 = forms.CharField(
        label=_("Confirm new password"),
        strip=False,
        widget=forms.PasswordInput(attrs={
            'class': 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm',
            'autocomplete': 'new-password'
        })
    )
    
    def clean_new_password1(self):
        password = self.cleaned_data.get('new_password1')
        
        if password:
            # Use Django's built-in validators
            validate_password(password, self.user)
            
            # Use our custom strong password validator
            validate_strong_password(password)
        
        return password


class Setup2FAForm(forms.Form):
    """
    Form for setting up 2FA.
    """
    
    verification_token = forms.CharField(
        max_length=6,
        min_length=6,
        label=_("Verification Code"),
        widget=forms.TextInput(attrs={
            'class': 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm text-center',
            'placeholder': '000000',
            'pattern': '[0-9]{6}',
            'inputmode': 'numeric',
            'maxlength': '6',
            'required': True
        }),
        help_text=_("Enter the 6-digit code from your authenticator app to verify setup.")
    )
    
    def __init__(self, user, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.user = user
    
    def clean_verification_token(self):
        token = self.cleaned_data.get('verification_token')
        
        if not token:
            raise ValidationError(_("Please enter the verification code."))
        
        if not token.isdigit():
            raise ValidationError(_("Verification code must contain only digits."))
        
        # Verify token with the secret that was generated
        try:
            profile = UserProfile.objects.get(user=self.user)
            if not profile.verify_2fa_token(token):
                raise ValidationError(_("Invalid verification code. Please check your authenticator app and try again."))
        except UserProfile.DoesNotExist:
            raise ValidationError(_("2FA setup not found. Please start the setup process again."))
        
        return token


class Disable2FAForm(forms.Form):
    """
    Form for disabling 2FA.
    """
    
    password = forms.CharField(
        label=_("Current Password"),
        strip=False,
        widget=forms.PasswordInput(attrs={
            'class': 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm',
            'autocomplete': 'current-password'
        }),
        help_text=_("Enter your current password to disable 2FA.")
    )
    
    confirmation = forms.BooleanField(
        label=_("I understand that disabling 2FA will make my account less secure"),
        widget=forms.CheckboxInput(attrs={
            'class': 'h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded'
        }),
        required=True
    )
    
    def __init__(self, user, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.user = user
    
    def clean_password(self):
        password = self.cleaned_data.get('password')
        
        if not self.user.check_password(password):
            raise ValidationError(_("Incorrect password."))
        
        return password


class SecuritySettingsForm(forms.ModelForm):
    """
    Form for managing user security settings.
    """
    
    class Meta:
        model = UserProfile
        fields = ['max_concurrent_sessions', 'session_timeout_minutes']
        widgets = {
            'max_concurrent_sessions': forms.NumberInput(attrs={
                'class': 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm',
                'min': '1',
                'max': '10'
            }),
            'session_timeout_minutes': forms.NumberInput(attrs={
                'class': 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm',
                'min': '30',
                'max': '1440'  # 24 hours
            })
        }
        labels = {
            'max_concurrent_sessions': _("Maximum Concurrent Sessions"),
            'session_timeout_minutes': _("Session Timeout (minutes)")
        }
        help_texts = {
            'max_concurrent_sessions': _("Maximum number of simultaneous login sessions (1-10)"),
            'session_timeout_minutes': _("How long sessions stay active without activity (30-1440 minutes)")
        }
    
    def clean_max_concurrent_sessions(self):
        value = self.cleaned_data.get('max_concurrent_sessions')
        if value < 1 or value > 10:
            raise ValidationError(_("Must be between 1 and 10 sessions."))
        return value
    
    def clean_session_timeout_minutes(self):
        value = self.cleaned_data.get('session_timeout_minutes')
        if value < 30 or value > 1440:
            raise ValidationError(_("Must be between 30 and 1440 minutes."))
        return value


class BackupCodesForm(forms.Form):
    """
    Form for using backup codes for 2FA.
    """
    
    backup_code = forms.CharField(
        max_length=8,
        min_length=8,
        label=_("Backup Code"),
        widget=forms.TextInput(attrs={
            'class': 'mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm text-center',
            'placeholder': 'XXXXXXXX',
            'style': 'text-transform: uppercase;',
            'maxlength': '8',
            'required': True
        }),
        help_text=_("Enter one of your backup codes.")
    )
    
    def __init__(self, user, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.user = user
    
    def clean_backup_code(self):
        code = self.cleaned_data.get('backup_code', '').upper()
        
        if not code:
            raise ValidationError(_("Please enter a backup code."))
        
        # Verify backup code
        try:
            profile = UserProfile.objects.get(user=self.user)
            if code not in profile.backup_codes:
                raise ValidationError(_("Invalid backup code."))
        except UserProfile.DoesNotExist:
            raise ValidationError(_("2FA is not set up for this account."))
        
        return code