"""Accounts Validators

This module contains custom validators for the accounts app.
These validators provide additional validation for user-related fields and data.
"""

import re
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _ 


class CustomPasswordValidator:
    """Custom password validator with specific requirements."""
    
    def __init__(self, min_length=8):
        self.min_length = min_length
    
    def validate(self, password, user=None):
        """Validate password against custom requirements."""
        errors = []
        
        # Check minimum length
        if len(password) < self.min_length:
            errors.append(
                ValidationError(
                    _(f'Password must be at least {self.min_length} characters long.'),
                    code='password_too_short'
                )
            )
        
        # Check for at least one uppercase letter
        if not re.search(r'[A-Z]', password):
            errors.append(
                ValidationError(
                    _('Password must contain at least one uppercase letter.'),
                    code='password_no_upper'
                )
            )
        
        # Check for at least one lowercase letter
        if not re.search(r'[a-z]', password):
            errors.append(
                ValidationError(
                    _('Password must contain at least one lowercase letter.'),
                    code='password_no_lower'
                )
            )
        
        # Check for at least one digit
        if not re.search(r'\d', password):
            errors.append(
                ValidationError(
                    _('Password must contain at least one digit.'),
                    code='password_no_digit'
                )
            )
        
        # Check for at least one special character
        if not re.search(r'[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]', password):
            errors.append(
                ValidationError(
                    _('Password must contain at least one special character.'),
                    code='password_no_special'
                )
            )
        
        # Check for common patterns
        if self._has_common_patterns(password):
            errors.append(
                ValidationError(
                    _('Password contains common patterns that are not secure.'),
                    code='password_common_pattern'
                )
            )
        
        if errors:
            raise ValidationError(errors)
    
    def _has_common_patterns(self, password):
        """Check for common insecure patterns."""
        common_patterns = [
            r'123456',
            r'password',
            r'qwerty',
            r'abc123',
            r'admin',
            r'letmein',
            r'welcome',
        ]
        
        password_lower = password.lower()
        for pattern in common_patterns:
            if pattern in password_lower:
                return True
        
        return False
    
    def get_help_text(self):
        """Return help text for password requirements."""
        return _(
            f'Your password must be at least {self.min_length} characters long and contain '
            'at least one uppercase letter, one lowercase letter, one digit, '
            'and one special character.'
        )


class UsernameValidator(RegexValidator):
    """Validator for username format."""
    
    regex = r'^[a-zA-Z0-9_.-]+$'
    message = _(
        'Username can only contain letters, numbers, underscores, dots, and hyphens.'
    )
    code = 'invalid_username'
    
    def __init__(self, min_length=3, max_length=30):
        super().__init__()
        self.min_length = min_length
        self.max_length = max_length
    
    def __call__(self, value):
        """Validate username."""
        super().__call__(value)
        
        # Check length
        if len(value) < self.min_length:
            raise ValidationError(
                _(f'Username must be at least {self.min_length} characters long.'),
                code='username_too_short'
            )
        
        if len(value) > self.max_length:
            raise ValidationError(
                _(f'Username cannot be longer than {self.max_length} characters.'),
                code='username_too_long'
            )
        
        # Check for reserved usernames
        reserved_usernames = [
            'admin', 'administrator', 'root', 'system', 'user',
            'api', 'www', 'mail', 'ftp', 'support', 'help',
            'info', 'contact', 'about', 'privacy', 'terms',
            'login', 'logout', 'register', 'signup', 'signin'
        ]
        
        if value.lower() in reserved_usernames:
            raise ValidationError(
                _('This username is reserved and cannot be used.'),
                code='username_reserved'
            )


class PhoneNumberValidator(RegexValidator):
    """Validator for phone number format."""
    
    regex = r'^\+?1?\d{9,15}$'
    message = _(
        'Phone number must be entered in the format: "+999999999". '
        'Up to 15 digits allowed.'
    )
    code = 'invalid_phone_number'


class NoSpacesValidator:
    """Validator that ensures no spaces in the value."""
    
    message = _('This field cannot contain spaces.')
    code = 'no_spaces'
    
    def __call__(self, value):
        if ' ' in value:
            raise ValidationError(self.message, code=self.code)


class AlphanumericValidator:
    """Validator that ensures value contains only alphanumeric characters."""
    
    message = _('This field can only contain letters and numbers.')
    code = 'alphanumeric_only'
    
    def __call__(self, value):
        if not value.isalnum():
            raise ValidationError(self.message, code=self.code)


class EmailDomainValidator:
    """Validator for allowed email domains."""
    
    def __init__(self, allowed_domains=None, blocked_domains=None):
        self.allowed_domains = allowed_domains or []
        self.blocked_domains = blocked_domains or []
    
    def __call__(self, value):
        domain = value.split('@')[-1].lower()
        
        # Check blocked domains
        if self.blocked_domains and domain in self.blocked_domains:
            raise ValidationError(
                _(f'Email addresses from {domain} are not allowed.'),
                code='blocked_domain'
            )
        
        # Check allowed domains (if specified)
        if self.allowed_domains and domain not in self.allowed_domains:
            raise ValidationError(
                _('Email address must be from an allowed domain.'),
                code='domain_not_allowed'
            )


class DisposableEmailValidator:
    """Validator that blocks disposable email addresses."""
    
    # Common disposable email domains
    DISPOSABLE_DOMAINS = {
        '10minutemail.com', 'guerrillamail.com', 'mailinator.com',
        'tempmail.org', 'throwaway.email', 'temp-mail.org',
        'getnada.com', 'maildrop.cc', 'yopmail.com',
        'sharklasers.com', 'grr.la', 'guerrillamailblock.com'
    }
    
    def __call__(self, value):
        domain = value.split('@')[-1].lower()
        
        if domain in self.DISPOSABLE_DOMAINS:
            raise ValidationError(
                _('Disposable email addresses are not allowed.'),
                code='disposable_email'
            )


class BioValidator:
    """Validator for user biography field."""
    
    def __init__(self, max_length=500, min_length=10):
        self.max_length = max_length
        self.min_length = min_length
    
    def __call__(self, value):
        if not value:
            return
        
        # Check length
        if len(value) < self.min_length:
            raise ValidationError(
                _(f'Biography must be at least {self.min_length} characters long.'),
                code='bio_too_short'
            )
        
        if len(value) > self.max_length:
            raise ValidationError(
                _(f'Biography cannot be longer than {self.max_length} characters.'),
                code='bio_too_long'
            )
        
        # Check for inappropriate content (basic)
        inappropriate_words = [
            'spam', 'scam', 'fraud', 'hack', 'virus'
        ]
        
        value_lower = value.lower()
        for word in inappropriate_words:
            if word in value_lower:
                raise ValidationError(
                    _('Biography contains inappropriate content.'),
                    code='bio_inappropriate'
                )


class AvatarValidator:
    """Validator for user avatar images."""
    
    def __init__(self, max_size=5*1024*1024, allowed_formats=None):
        self.max_size = max_size  # 5MB default
        self.allowed_formats = allowed_formats or ['JPEG', 'PNG', 'GIF', 'WEBP']
    
    def __call__(self, value):
        if not value:
            return
        
        # Check file size
        if value.size > self.max_size:
            raise ValidationError(
                _(f'Avatar file size cannot exceed {self.max_size // (1024*1024)}MB.'),
                code='avatar_too_large'
            )
        
        # Check file format
        try:
            from PIL import Image
            image = Image.open(value)
            
            if image.format not in self.allowed_formats:
                raise ValidationError(
                    _(f'Avatar must be in one of these formats: {", ".join(self.allowed_formats)}.'),
                    code='avatar_invalid_format'
                )
            
            # Check image dimensions
            max_dimension = 2000
            if image.width > max_dimension or image.height > max_dimension:
                raise ValidationError(
                    _(f'Avatar dimensions cannot exceed {max_dimension}x{max_dimension} pixels.'),
                    code='avatar_too_large_dimensions'
                )
            
        except Exception:
            raise ValidationError(
                _('Invalid image file.'),
                code='avatar_invalid'
            )


class TimezoneValidator:
    """Validator for timezone field."""
    
    def __call__(self, value):
        if not value:
            return
        
        try:
            import pytz
            pytz.timezone(value)
        except Exception:
            raise ValidationError(
                _('Invalid timezone.'),
                code='invalid_timezone'
            )


# Utility functions for validation

def validate_user_data(data):
    """Validate user registration/update data.
    
    Args:
        data: Dictionary containing user data
        
    Returns:
        Dictionary with validation results
    """
    errors = {}
    
    # Validate username
    if 'username' in data:
        try:
            username_validator = UsernameValidator()
            username_validator(data['username'])
        except ValidationError as e:
            errors['username'] = e.messages
    
    # Validate email
    if 'email' in data:
        try:
            disposable_validator = DisposableEmailValidator()
            disposable_validator(data['email'])
        except ValidationError as e:
            errors['email'] = e.messages
    
    # Validate password
    if 'password' in data:
        try:
            password_validator = CustomPasswordValidator()
            password_validator.validate(data['password'])
        except ValidationError as e:
            errors['password'] = [error.message for error in e.error_list]
    
    return {
        'is_valid': len(errors) == 0,
        'errors': errors
    }


def get_password_strength_score(password):
    """Calculate password strength score.
    
    Args:
        password: Password string
        
    Returns:
        Integer score from 0-5
    """
    score = 0
    
    # Length check
    if len(password) >= 8:
        score += 1
    
    # Character type checks
    if re.search(r'[a-z]', password):
        score += 1
    
    if re.search(r'[A-Z]', password):
        score += 1
    
    if re.search(r'\d', password):
        score += 1
    
    if re.search(r'[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]', password):
        score += 1
    
    return score