# -*- coding: utf-8 -*-
"""
Accounts Serializers Module

This module contains serializers for the accounts app to handle
API data serialization and deserialization for users, roles, and profiles.

Author: Senior Django Developer
Date: 2024
"""

from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
from datetime import datetime

from .models import Role, User, Profile
from apps.authentication.utils import is_password_strong

# Get the custom user model
User = get_user_model()


class PermissionSerializer(serializers.ModelSerializer):
    """
    Serializer for Django permissions.
    
    Provides a clean representation of permissions for API responses.
    """
    
    class Meta:
        model = Permission
        fields = ('id', 'name', 'codename', 'content_type')
        read_only_fields = ('id', 'name', 'codename', 'content_type')
    
    def to_representation(self, instance):
        """
        Custom representation to include content type information.
        
        Args:
            instance: Permission instance
            
        Returns:
            dict: Serialized permission data
        """
        data = super().to_representation(instance)
        # Add content type app label for better understanding
        data['app_label'] = instance.content_type.app_label
        data['model'] = instance.content_type.model
        return data


class RoleSerializer(serializers.ModelSerializer):
    """
    Serializer for Role model.
    
    Handles role data serialization with permissions and user counts.
    """
    
    permissions = PermissionSerializer(many=True, read_only=True)
    user_count = serializers.SerializerMethodField()
    permission_ids = serializers.ListField(
        child=serializers.IntegerField(),
        write_only=True,
        required=False,
        help_text=_('List of permission IDs to assign to this role')
    )
    
    class Meta:
        model = Role
        fields = (
            'id', 'name', 'description', 'permissions', 'user_count',
            'permission_ids', 'created_at', 'updated_at'
        )
        read_only_fields = ('id', 'created_at', 'updated_at')
    
    def get_user_count(self, obj):
        """
        Get the number of users assigned to this role.
        
        Args:
            obj: Role instance
            
        Returns:
            int: Number of users with this role
        """
        return obj.users.count()
    
    def validate_name(self, value):
        """
        Validate role name uniqueness and format.
        
        Args:
            value: Role name value
            
        Returns:
            str: Validated role name
            
        Raises:
            ValidationError: If name is invalid
        """
        # Clean and format name
        value = value.strip().title()
        
        # Check uniqueness (excluding current instance if updating)
        queryset = Role.objects.filter(name=value)
        if self.instance:
            queryset = queryset.exclude(pk=self.instance.pk)
        
        if queryset.exists():
            raise serializers.ValidationError(
                _('A role with this name already exists.')
            )
        
        return value
    
    def validate_permission_ids(self, value):
        """
        Validate permission IDs exist.
        
        Args:
            value: List of permission IDs
            
        Returns:
            list: Validated permission IDs
        """
        if value:
            existing_permissions = Permission.objects.filter(id__in=value)
            if len(existing_permissions) != len(value):
                raise serializers.ValidationError(
                    _('One or more permission IDs are invalid.')
                )
        return value
    
    def create(self, validated_data):
        """
        Create a new role with permissions.
        
        Args:
            validated_data: Validated role data
            
        Returns:
            Role: Created role instance
        """
        permission_ids = validated_data.pop('permission_ids', [])
        role = Role.objects.create(**validated_data)
        
        if permission_ids:
            permissions = Permission.objects.filter(id__in=permission_ids)
            role.permissions.set(permissions)
        
        return role
    
    def update(self, instance, validated_data):
        """
        Update role with new permissions.
        
        Args:
            instance: Role instance to update
            validated_data: Validated role data
            
        Returns:
            Role: Updated role instance
        """
        permission_ids = validated_data.pop('permission_ids', None)
        
        # Update basic fields
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        
        # Update permissions if provided
        if permission_ids is not None:
            permissions = Permission.objects.filter(id__in=permission_ids)
            instance.permissions.set(permissions)
        
        return instance


class ProfileSerializer(serializers.ModelSerializer):
    """
    Serializer for Profile model.
    
    Handles user profile data serialization.
    """
    
    avatar_url = serializers.SerializerMethodField()
    age = serializers.SerializerMethodField()
    
    class Meta:
        model = Profile
        fields = (
            'phone_number', 'bio', 'date_of_birth', 'avatar',
            'avatar_url', 'age', 'created_at', 'updated_at'
        )
        read_only_fields = ('created_at', 'updated_at')
    
    def get_avatar_url(self, obj):
        """
        Get the full URL for the user's avatar.
        
        Args:
            obj: Profile instance
            
        Returns:
            str: Avatar URL or None
        """
        if obj.avatar:
            request = self.context.get('request')
            if request:
                return request.build_absolute_uri(obj.avatar.url)
            return obj.avatar.url
        return None
    
    def get_age(self, obj):
        """
        Calculate user's age from date of birth.
        
        Args:
            obj: Profile instance
            
        Returns:
            int: User's age or None
        """
        if obj.date_of_birth:
            today = datetime.now().date()
            age = today.year - obj.date_of_birth.year
            # Adjust if birthday hasn't occurred this year
            if today < obj.date_of_birth.replace(year=today.year):
                age -= 1
            return age
        return None
    
    def validate_bio(self, value):
        """
        Validate bio length and content.
        
        Args:
            value: Bio text
            
        Returns:
            str: Validated bio
        """
        if value and len(value) > 500:
            raise serializers.ValidationError(
                _('Bio must be 500 characters or less.')
            )
        return value
    
    def validate_avatar(self, value):
        """
        Validate avatar file size and type.
        
        Args:
            value: Avatar file
            
        Returns:
            File: Validated avatar file
        """
        if value:
            # Check file size (max 5MB)
            if value.size > 5 * 1024 * 1024:
                raise serializers.ValidationError(
                    _('Avatar image must be smaller than 5MB.')
                )
            
            # Check file type
            allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
            if value.content_type not in allowed_types:
                raise serializers.ValidationError(
                    _('Avatar must be a JPEG, PNG, GIF, or WebP image.')
                )
        
        return value


class UserSerializer(serializers.ModelSerializer):
    """
    Serializer for User model.
    
    Handles user data serialization with profile and roles.
    """
    
    profile = ProfileSerializer(read_only=True)
    roles = RoleSerializer(many=True, read_only=True)
    role_ids = serializers.ListField(
        child=serializers.IntegerField(),
        write_only=True,
        required=False,
        help_text=_('List of role IDs to assign to this user')
    )
    full_name = serializers.SerializerMethodField()
    is_online = serializers.SerializerMethodField()
    
    class Meta:
        model = User
        fields = (
            'id', 'username', 'email', 'first_name', 'last_name',
            'full_name', 'is_active', 'is_staff', 'is_superuser',
            'is_verified', 'date_joined', 'last_login',
            'profile', 'roles', 'role_ids', 'is_online'
        )
        read_only_fields = (
            'id', 'username', 'date_joined', 'last_login', 'is_online'
        )
        extra_kwargs = {
            'email': {'required': True},
            'first_name': {'required': True},
            'last_name': {'required': True}
        }
    
    def get_full_name(self, obj):
        """
        Get user's full name.
        
        Args:
            obj: User instance
            
        Returns:
            str: User's full name
        """
        return obj.get_full_name()
    
    def get_is_online(self, obj):
        """
        Check if user is currently online.
        
        Args:
            obj: User instance
            
        Returns:
            bool: True if user is online
        """
        # Check if user has active sessions (simplified check)
        if obj.last_login:
            from django.utils import timezone
            from datetime import timedelta
            # Consider user online if last login was within 15 minutes
            return obj.last_login > timezone.now() - timedelta(minutes=15)
        return False
    
    def validate_email(self, value):
        """
        Validate email uniqueness.
        
        Args:
            value: Email address
            
        Returns:
            str: Validated email
        """
        value = value.lower().strip()
        
        # Check uniqueness (excluding current instance if updating)
        queryset = User.objects.filter(email=value)
        if self.instance:
            queryset = queryset.exclude(pk=self.instance.pk)
        
        if queryset.exists():
            raise serializers.ValidationError(
                _('A user with this email address already exists.')
            )
        
        return value
    
    def validate_role_ids(self, value):
        """
        Validate role IDs exist.
        
        Args:
            value: List of role IDs
            
        Returns:
            list: Validated role IDs
        """
        if value:
            existing_roles = Role.objects.filter(id__in=value)
            if len(existing_roles) != len(value):
                raise serializers.ValidationError(
                    _('One or more role IDs are invalid.')
                )
        return value
    
    def update(self, instance, validated_data):
        """
        Update user with new roles.
        
        Args:
            instance: User instance to update
            validated_data: Validated user data
            
        Returns:
            User: Updated user instance
        """
        role_ids = validated_data.pop('role_ids', None)
        
        # Update basic fields
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        
        # Set username to email if email changed
        if 'email' in validated_data:
            instance.username = validated_data['email']
        
        instance.save()
        
        # Update roles if provided
        if role_ids is not None:
            roles = Role.objects.filter(id__in=role_ids)
            instance.roles.set(roles)
        
        return instance


class UserRegistrationSerializer(serializers.ModelSerializer):
    """
    Serializer for user registration via API.
    
    Handles user creation with password validation.
    """
    
    password = serializers.CharField(
        write_only=True,
        min_length=8,
        help_text=_('Password must be at least 8 characters long')
    )
    password_confirm = serializers.CharField(
        write_only=True,
        help_text=_('Confirm your password')
    )
    phone_number = serializers.CharField(
        required=False,
        allow_blank=True,
        help_text=_('Optional phone number')
    )
    
    class Meta:
        model = User
        fields = (
            'email', 'first_name', 'last_name', 'password',
            'password_confirm', 'phone_number'
        )
        extra_kwargs = {
            'email': {'required': True},
            'first_name': {'required': True},
            'last_name': {'required': True}
        }
    
    def validate_email(self, value):
        """
        Validate email uniqueness and format.
        
        Args:
            value: Email address
            
        Returns:
            str: Validated email
        """
        value = value.lower().strip()
        
        if User.objects.filter(email=value).exists():
            raise serializers.ValidationError(
                _('A user with this email address already exists.')
            )
        
        return value
    
    def validate_password(self, value):
        """
        Validate password strength.
        
        Args:
            value: Password
            
        Returns:
            str: Validated password
        """
        if not is_password_strong(value):
            raise serializers.ValidationError(
                _('Password must contain at least 8 characters with uppercase, '
                  'lowercase, numbers, and special characters.')
            )
        return value
    
    def validate(self, attrs):
        """
        Validate password confirmation.
        
        Args:
            attrs: Attribute dictionary
            
        Returns:
            dict: Validated attributes
        """
        if attrs['password'] != attrs['password_confirm']:
            raise serializers.ValidationError(
                {'password_confirm': _('Password confirmation does not match.')}
            )
        return attrs
    
    def create(self, validated_data):
        """
        Create a new user with profile.
        
        Args:
            validated_data: Validated user data
            
        Returns:
            User: Created user instance
        """
        # Remove password confirmation and phone number
        validated_data.pop('password_confirm')
        phone_number = validated_data.pop('phone_number', '')
        
        # Create user
        user = User.objects.create_user(
            username=validated_data['email'],
            email=validated_data['email'],
            first_name=validated_data['first_name'],
            last_name=validated_data['last_name'],
            password=validated_data['password']
        )
        
        # Set email verification status
        user.is_verified = False
        user.save()
        
        # Create or update profile with phone number
        if phone_number:
            profile, created = Profile.objects.get_or_create(
                user=user,
                defaults={'phone_number': phone_number}
            )
        
        return user


class UserListSerializer(serializers.ModelSerializer):
    """
    Simplified serializer for user lists.
    
    Provides minimal user information for list views.
    """
    
    full_name = serializers.SerializerMethodField()
    role_names = serializers.SerializerMethodField()
    
    class Meta:
        model = User
        fields = (
            'id', 'email', 'first_name', 'last_name', 'full_name',
            'is_active', 'is_staff', 'is_verified',
            'date_joined', 'last_login', 'role_names'
        )
        read_only_fields = fields
    
    def get_full_name(self, obj):
        """
        Get user's full name.
        
        Args:
            obj: User instance
            
        Returns:
            str: User's full name
        """
        return obj.get_full_name()
    
    def get_role_names(self, obj):
        """
        Get list of role names for the user.
        
        Args:
            obj: User instance
            
        Returns:
            list: List of role names
        """
        return [role.name for role in obj.roles.all()]


class PasswordChangeSerializer(serializers.Serializer):
    """
    Serializer for password change via API.
    
    Handles secure password changing.
    """
    
    current_password = serializers.CharField(
        write_only=True,
        help_text=_('Your current password')
    )
    new_password = serializers.CharField(
        write_only=True,
        min_length=8,
        help_text=_('Your new password')
    )
    new_password_confirm = serializers.CharField(
        write_only=True,
        help_text=_('Confirm your new password')
    )
    
    def __init__(self, *args, **kwargs):
        """
        Initialize serializer with user context.
        
        Args:
            *args: Variable length argument list
            **kwargs: Arbitrary keyword arguments
        """
        self.user = kwargs.pop('user', None)
        super().__init__(*args, **kwargs)
    
    def validate_current_password(self, value):
        """
        Validate current password.
        
        Args:
            value: Current password
            
        Returns:
            str: Validated current password
        """
        if not self.user or not self.user.check_password(value):
            raise serializers.ValidationError(
                _('Current password is incorrect.')
            )
        return value
    
    def validate_new_password(self, value):
        """
        Validate new password strength.
        
        Args:
            value: New password
            
        Returns:
            str: Validated new password
        """
        if not is_password_strong(value):
            raise serializers.ValidationError(
                _('Password must contain at least 8 characters with uppercase, '
                  'lowercase, numbers, and special characters.')
            )
        return value
    
    def validate(self, attrs):
        """
        Validate password confirmation.
        
        Args:
            attrs: Attribute dictionary
            
        Returns:
            dict: Validated attributes
        """
        if attrs['new_password'] != attrs['new_password_confirm']:
            raise serializers.ValidationError(
                {'new_password_confirm': _('Password confirmation does not match.')}
            )
        return attrs
    
    def save(self):
        """
        Save the new password.
        
        Returns:
            User: Updated user instance
        """
        password = self.validated_data['new_password']
        self.user.set_password(password)
        self.user.save()
        return self.user