from django.contrib.auth import get_user_model from django.contrib.auth.password_validation import validate_password from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.serializers import TokenObtainPairSerializer from phonenumber_field.serializerfields import PhoneNumberField from apps.account.models.verification import VerificationDocument User = get_user_model() class RegisterRequestSerializer(serializers.Serializer): email = serializers.EmailField(required=True) password = serializers.CharField(write_only=True, required=True, min_length=6) fullname = serializers.CharField(required=True, max_length=255) phone_number = serializers.CharField(required=False, allow_blank=True, allow_null=True) user_type = serializers.ChoiceField( choices=User.UserType.choices, default=User.UserType.CLIENT, required=False ) languages = serializers.ListField( child=serializers.CharField(max_length=10), required=False, default=list ) skills = serializers.ListField( child=serializers.CharField(max_length=100), required=False, default=list ) country = serializers.CharField(required=False, allow_blank=True, max_length=255) city = serializers.CharField(required=False, allow_blank=True, max_length=255) def validate_email(self, value): normalized = value.strip().lower() if User.objects.filter(email__iexact=normalized).exists(): raise serializers.ValidationError("A user with this email address already exists.") return normalized def validate_password(self, value): validate_password(value) return value def create(self, validated_data): email = validated_data.pop('email') password = validated_data.pop('password') fullname = validated_data.pop('fullname') user_type = validated_data.pop('user_type', User.UserType.CLIENT) user = User.objects.create_user( email=email, password=password, fullname=fullname, username=email, user_type=user_type, **validated_data ) return user class UserMeSerializer(serializers.ModelSerializer): bio = serializers.CharField(source='info', allow_blank=True, required=False, allow_null=True) roles = serializers.SerializerMethodField() permissions = serializers.SerializerMethodField() verification_status = serializers.SerializerMethodField() is_verified = serializers.SerializerMethodField() class Meta: model = User fields = [ 'id', 'email', 'username', 'fullname', 'phone_number', 'user_type', 'avatar', 'bio', 'languages', 'skills', 'country', 'city', 'gender', 'birthdate', 'date_joined', 'is_active', 'is_staff', 'roles', 'permissions', 'verification_status', 'is_verified', ] read_only_fields = [ 'id', 'email', 'username', 'user_type', 'date_joined', 'is_active', 'is_staff', 'roles', 'permissions', 'verification_status', 'is_verified', ] def get_roles(self, obj) -> list: roles = [obj.user_type] if obj.user_type else [] for group in obj.groups.all(): roles.append(group.name) return list(set(roles)) def get_permissions(self, obj) -> list: perms = [] if obj.is_super_admin: perms.extend(['super_admin', 'manage_all', 'manage_institutions', 'manage_users', 'manage_content', 'approve_verifications']) elif obj.is_regional_admin: perms.extend(['regional_admin', 'manage_regional_institutions', 'manage_content', 'approve_verifications']) elif obj.is_institution_admin: perms.extend(['institution_admin', 'manage_institution_profile', 'manage_team', 'manage_events', 'manage_projects']) elif obj.is_editor: perms.extend(['editor', 'create_posts', 'manage_events', 'edit_projects']) else: perms.extend(['client_access', 'view_courses', 'submit_tickets', 'attend_events']) return perms def get_verification_status(self, obj) -> str: return obj.verification_status def get_is_verified(self, obj) -> bool: return obj.verification_status == VerificationDocument.Status.VERIFIED class UserMeUpdateSerializer(serializers.ModelSerializer): bio = serializers.CharField(source='info', allow_blank=True, required=False, allow_null=True) class Meta: model = User fields = [ 'fullname', 'phone_number', 'avatar', 'bio', 'languages', 'skills', 'country', 'city', 'gender', 'birthdate', ] def update(self, instance, validated_data): for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() return instance class RegisterResponseSerializer(serializers.Serializer): user = UserMeSerializer() access = serializers.CharField() refresh = serializers.CharField() message = serializers.CharField() class VerificationDocumentSerializer(serializers.ModelSerializer): document_type_display = serializers.CharField(source='get_document_type_display', read_only=True) status_display = serializers.CharField(source='get_status_display', read_only=True) reviewed_by_email = serializers.EmailField(source='reviewed_by.email', read_only=True, allow_null=True) class Meta: model = VerificationDocument fields = [ 'id', 'user', 'document_type', 'document_type_display', 'title', 'document_file', 'status', 'status_display', 'admin_notes', 'reviewed_by_email', 'uploaded_at', 'reviewed_at', ] read_only_fields = [ 'id', 'user', 'document_type_display', 'status', 'status_display', 'admin_notes', 'reviewed_by_email', 'uploaded_at', 'reviewed_at', ] class VerificationDocumentUploadSerializer(serializers.Serializer): document_type = serializers.ChoiceField( choices=VerificationDocument.DocumentType.choices, default=VerificationDocument.DocumentType.NATIONAL_ID ) title = serializers.CharField(required=False, allow_blank=True, max_length=255) document_file = serializers.FileField(required=True) class PasswordRecoverRequestSerializer(serializers.Serializer): email = serializers.EmailField(required=True) class PasswordResetRequestSerializer(serializers.Serializer): email = serializers.EmailField(required=True) token = serializers.CharField(required=True) new_password = serializers.CharField(required=True, min_length=6) def validate_new_password(self, value): validate_password(value) return value class CustomTokenObtainPairSerializer(TokenObtainPairSerializer): """ JWT token obtain serializer that accepts either 'username' or 'email' as the credential. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if 'email' in self.fields: self.fields['email'].required = False if 'username' not in self.fields: self.fields['username'] = serializers.CharField(required=False, write_only=True) def to_internal_value(self, data): if isinstance(data, dict): mutable_data = data.copy() identifier = mutable_data.get('email') or mutable_data.get('username') if identifier: mutable_data['email'] = identifier mutable_data['username'] = identifier return super().to_internal_value(mutable_data) return super().to_internal_value(data) def validate(self, attrs): identifier = attrs.get('email') or attrs.get('username') if not identifier: raise serializers.ValidationError({"email": _("This field is required.")}) attrs['email'] = identifier attrs['username'] = identifier return super().validate(attrs)