"""Accounts Utilities

This module contains utility functions and helpers for the accounts app.
These utilities provide common functionality used across the accounts app.
"""

import string 
import hashlib
import secrets
from typing import Optional, Dict, Any

from django.urls import reverse
from django.conf import settings
from django.utils import timezone
from django.core.mail import send_mail
from django.contrib.sites.models import Site
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
from django.utils.encoding import force_bytes, force_str
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode


from apps.accounts.models import User, UserSession


def generate_username(email: str) -> str:
    """Generate a unique username from email.
    
    Args:
        email: User's email address
        
    Returns:
        A unique username string
    """
    base_username = email.split('@')[0]
    base_username = ''.join(c for c in base_username if c.isalnum() or c in '-_')
    
    if not User.objects.filter(username=base_username).exists():
        return base_username
    
    # If username exists, append a number
    counter = 1
    while True:
        username = f"{base_username}{counter}"
        if not User.objects.filter(username=username).exists():
            return username
        counter += 1


def generate_secure_token(length: int = 32) -> str:
    """Generate a cryptographically secure random token.
    
    Args:
        length: Length of the token
        
    Returns:
        A secure random token string
    """
    alphabet = string.ascii_letters + string.digits
    return ''.join(secrets.choice(alphabet) for _ in range(length))


def hash_password_reset_token(token: str) -> str:
    """Hash a password reset token for secure storage.
    
    Args:
        token: The token to hash
        
    Returns:
        Hashed token string
    """
    return hashlib.sha256(token.encode()).hexdigest()


def get_client_ip(request) -> str:
    """Extract client IP address from request.
    
    Args:
        request: Django request object
        
    Returns:
        Client IP address string
    """
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0].strip()
    else:
        ip = request.META.get('REMOTE_ADDR', '')
    return ip


def get_user_agent(request) -> str:
    """Extract user agent from request.
    
    Args:
        request: Django request object
        
    Returns:
        User agent string (truncated to 500 chars)
    """
    return request.META.get('HTTP_USER_AGENT', '')[:500]


def is_safe_url(url: str, allowed_hosts: set = None) -> bool:
    """Check if a URL is safe for redirects.
    
    Args:
        url: URL to check
        allowed_hosts: Set of allowed hosts
        
    Returns:
        True if URL is safe, False otherwise
    """
    if not url:
        return False
    
    if allowed_hosts is None:
        allowed_hosts = set(settings.ALLOWED_HOSTS)
    
    # Simple check for relative URLs
    if url.startswith('/'):
        return True
    
    # Check for allowed hosts in absolute URLs
    from urllib.parse import urlparse
    parsed = urlparse(url)
    return parsed.netloc in allowed_hosts


def send_email_verification(user: User, request=None) -> bool:
    """Send email verification to user.
    
    Args:
        user: User instance
        request: Django request object (optional)
        
    Returns:
        True if email was sent successfully, False otherwise
    """
    try:
        # Generate verification token
        token = default_token_generator.make_token(user)
        uid = urlsafe_base64_encode(force_bytes(user.pk))
        
        # Build verification URL
        if request:
            domain = request.get_host()
            protocol = 'https' if request.is_secure() else 'http'
        else:
            site = Site.objects.get_current()
            domain = site.domain
            protocol = 'https' if getattr(settings, 'USE_HTTPS', False) else 'http'
        
        verification_url = f"{protocol}://{domain}{reverse('accounts:verify_email', kwargs={'uidb64': uid, 'token': token})}"
        
        # Prepare email context
        context = {
            'user': user,
            'verification_url': verification_url,
            'site_name': getattr(settings, 'SITE_NAME', 'Adtlas'),
        }
        
        # Send email
        subject = _('Verify your email address')
        message = render_to_string('accounts/emails/email_verification.txt', context)
        html_message = render_to_string('accounts/emails/email_verification.html', context)
        
        send_mail(
            subject=subject,
            message=message,
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[user.email],
            html_message=html_message,
            fail_silently=False,
        )
        
        return True
        
    except Exception:
        return False


def verify_email_token(uidb64: str, token: str) -> Optional[User]:
    """Verify email verification token.
    
    Args:
        uidb64: Base64 encoded user ID
        token: Verification token
        
    Returns:
        User instance if token is valid, None otherwise
    """
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = User.objects.get(pk=uid)
        
        if default_token_generator.check_token(user, token):
            return user
        
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
        pass
    
    return None


def cleanup_expired_sessions() -> int:
    """Clean up expired user sessions.
    
    Returns:
        Number of sessions cleaned up
    """
    expired_sessions = UserSession.objects.filter(
        expires_at__lt=timezone.now(),
        is_active=True
    )
    
    count = expired_sessions.count()
    expired_sessions.update(is_active=False)
    
    return count


def get_user_session_info(request) -> Dict[str, Any]:
    """Get comprehensive session information for a user.
    
    Args:
        request: Django request object
        
    Returns:
        Dictionary containing session information
    """
    if not request.user.is_authenticated:
        return {}
    
    try:
        current_session = UserSession.objects.get(
            user=request.user,
            session_key=request.session.session_key,
            is_active=True
        )
        
        active_sessions = UserSession.objects.filter(
            user=request.user,
            is_active=True
        ).count()
        
        return {
            'current_session': current_session,
            'active_sessions_count': active_sessions,
            'ip_address': get_client_ip(request),
            'user_agent': get_user_agent(request),
            'session_age': timezone.now() - current_session.created_at,
        }
        
    except UserSession.DoesNotExist:
        return {
            'active_sessions_count': 0,
            'ip_address': get_client_ip(request),
            'user_agent': get_user_agent(request),
        }


def terminate_user_sessions(user: User, exclude_session_key: str = None) -> int:
    """Terminate all user sessions except optionally one.
    
    Args:
        user: User instance
        exclude_session_key: Session key to exclude from termination
        
    Returns:
        Number of sessions terminated
    """
    sessions = UserSession.objects.filter(
        user=user,
        is_active=True
    )
    
    if exclude_session_key:
        sessions = sessions.exclude(session_key=exclude_session_key)
    
    count = sessions.count()
    sessions.update(is_active=False)
    
    return count


def generate_avatar_url(user: User, size: int = 80) -> str:
    """Generate avatar URL for user (Gravatar or default).
    
    Args:
        user: User instance
        size: Avatar size in pixels
        
    Returns:
        Avatar URL string
    """
    if user.avatar:
        return user.avatar.url
    
    # Generate Gravatar URL
    email_hash = hashlib.md5(user.email.lower().encode()).hexdigest()
    default_avatar = getattr(settings, 'DEFAULT_AVATAR', 'identicon')
    
    return f"https://www.gravatar.com/avatar/{email_hash}?s={size}&d={default_avatar}"


def validate_password_strength(password: str) -> Dict[str, Any]:
    """Validate password strength and return detailed feedback.
    
    Args:
        password: Password to validate
        
    Returns:
        Dictionary with validation results
    """
    result = {
        'is_valid': True,
        'score': 0,
        'feedback': [],
        'requirements': {
            'length': len(password) >= 8,
            'uppercase': any(c.isupper() for c in password),
            'lowercase': any(c.islower() for c in password),
            'digit': any(c.isdigit() for c in password),
            'special': any(c in '!@#$%^&*()_+-=[]{}|;:,.<>?' for c in password),
        }
    }
    
    # Calculate score
    score = 0
    if result['requirements']['length']:
        score += 1
    if result['requirements']['uppercase']:
        score += 1
    if result['requirements']['lowercase']:
        score += 1
    if result['requirements']['digit']:
        score += 1
    if result['requirements']['special']:
        score += 1
    
    result['score'] = score
    
    # Generate feedback
    if not result['requirements']['length']:
        result['feedback'].append(_('Password must be at least 8 characters long'))
        result['is_valid'] = False
    
    if not result['requirements']['uppercase']:
        result['feedback'].append(_('Password must contain at least one uppercase letter'))
    
    if not result['requirements']['lowercase']:
        result['feedback'].append(_('Password must contain at least one lowercase letter'))
    
    if not result['requirements']['digit']:
        result['feedback'].append(_('Password must contain at least one digit'))
    
    if not result['requirements']['special']:
        result['feedback'].append(_('Password must contain at least one special character'))
    
    # Set overall validity (minimum requirements)
    if score < 3:
        result['is_valid'] = False
    
    return result


def format_user_display_name(user: User) -> str:
    """Format user display name consistently.
    
    Args:
        user: User instance
        
    Returns:
        Formatted display name
    """
    if user.first_name and user.last_name:
        return f"{user.first_name} {user.last_name}"
    elif user.first_name:
        return user.first_name
    elif user.last_name:
        return user.last_name
    else:
        return user.username or user.email.split('@')[0]


def get_user_timezone(user: User) -> str:
    """Get user's timezone or default.
    
    Args:
        user: User instance
        
    Returns:
        Timezone string
    """
    if hasattr(user, 'timezone_field') and user.timezone_field:
        return user.timezone_field
    return getattr(settings, 'TIME_ZONE', 'UTC')