from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError as DjangoValidationError
from djoser.serializers import UserCreateSerializer as BaseUserCreateSerializer
from djoser.serializers import UserSerializer as BaseUserSerializer
from Blog.models import Article

from .models import MembershipPeriod, PaymentReceipt

User = get_user_model()

class UserCreateSerializer(BaseUserCreateSerializer):
    class Meta(BaseUserCreateSerializer.Meta):
        model = User
        fields = (
            'id', 'email', 'password', 'first_name', 'last_name',
            'speciality', 'phone_number', 'document', 'profile_image'
        )


class UserRegistrationSerializer(serializers.ModelSerializer):
    """
    Serializer pour l'inscription des utilisateurs.
    Inclut la validation du mot de passe et des informations personnelles.
    """
    password = serializers.CharField(write_only=True, style={'input_type': 'password'})
    re_password = serializers.CharField(write_only=True, style={'input_type': 'password'})

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name', 'phone_number', 'password', 're_password')

    def validate(self, attrs):
        if attrs['password'] != attrs['re_password']:
            raise serializers.ValidationError({"password": "Les mots de passe ne correspondent pas."})
        return attrs

    def validate_phone_number(self, value):
        if value:
            cleaned = value.replace(' ', '').replace('-', '').replace('+', '')
            if not cleaned.isdigit():
                raise serializers.ValidationError(
                    "Le numéro de téléphone ne doit contenir que des chiffres, espaces, tirets ou le signe +."
                )
            if len(cleaned) < 8 or len(cleaned) > 15:
                raise serializers.ValidationError(
                    "Le numéro de téléphone doit contenir entre 8 et 15 chiffres."
                )
        return value

    def validate_first_name(self, value):
        if value and len(value.strip()) < 2:
            raise serializers.ValidationError("Le prénom doit contenir au moins 2 caractères.")
        return value.strip()

    def validate_last_name(self, value):
        if value and len(value.strip()) < 2:
            raise serializers.ValidationError("Le nom doit contenir au moins 2 caractères.")
        return value.strip()

    def create(self, validated_data):
        validated_data.pop('re_password')
        return User.objects.create_user(**validated_data)


class UserProfileUpdateSerializer(serializers.ModelSerializer):
    """
    Serializer dédié à la mise à jour du profil utilisateur.
    Seuls les champs modifiables par l'utilisateur sont exposés.
    Les champs sensibles (email, dates, statuts) sont en lecture seule.
    """

    class Meta:
        model = User
        fields = (
            'id', 'email', 'first_name', 'last_name',
            'speciality', 'phone_number', 'address', 'profile_image',
            'date_joined',
        )
        read_only_fields = (
            'id', 'email', 'date_joined',
        )

    def validate_phone_number(self, value):
        """Validation basique du numéro de téléphone."""
        if value:
            cleaned = value.replace(' ', '').replace('-', '').replace('+', '')
            if not cleaned.isdigit():
                raise serializers.ValidationError(
                    "Le numéro de téléphone ne doit contenir que des chiffres, espaces, tirets ou le signe +."
                )
            if len(cleaned) < 8 or len(cleaned) > 15:
                raise serializers.ValidationError(
                    "Le numéro de téléphone doit contenir entre 8 et 15 chiffres."
                )
        return value

    def validate_first_name(self, value):
        if value and len(value.strip()) < 2:
            raise serializers.ValidationError("Le prénom doit contenir au moins 2 caractères.")
        return value.strip()

    def validate_last_name(self, value):
        if value and len(value.strip()) < 2:
            raise serializers.ValidationError("Le nom doit contenir au moins 2 caractères.")
        return value.strip()


class PasswordChangeSerializer(serializers.Serializer):
    """
    Serializer pour le changement de mot de passe d'un utilisateur connecté.
    Exige le mot de passe actuel pour confirmer l'identité, puis valide
    la force du nouveau via AUTH_PASSWORD_VALIDATORS.
    """
    current_password = serializers.CharField(write_only=True, style={'input_type': 'password'})
    new_password = serializers.CharField(write_only=True, style={'input_type': 'password'})
    re_new_password = serializers.CharField(write_only=True, style={'input_type': 'password'})

    def validate_current_password(self, value):
        user = self.context['request'].user
        if not user.check_password(value):
            raise serializers.ValidationError("Mot de passe actuel incorrect.")
        return value

    def validate(self, attrs):
        if attrs['new_password'] != attrs['re_new_password']:
            raise serializers.ValidationError(
                {"re_new_password": "Les nouveaux mots de passe ne correspondent pas."}
            )

        if attrs['new_password'] == attrs['current_password']:
            raise serializers.ValidationError(
                {"new_password": "Le nouveau mot de passe doit être différent de l'actuel."}
            )

        user = self.context['request'].user
        try:
            validate_password(attrs['new_password'], user=user)
        except DjangoValidationError as e:
            raise serializers.ValidationError({"new_password": list(e.messages)})

        return attrs

    def save(self, **kwargs):
        user = self.context['request'].user
        user.set_password(self.validated_data['new_password'])
        user.save(update_fields=['password'])
        return user


class PaymentReceiptSerializer(serializers.ModelSerializer):
    """
    Représentation publique d'un reçu de paiement pour l'historique
    affiché dans le dashboard du user.
    """
    receipt_file_url = serializers.SerializerMethodField()
    membership_period_label = serializers.CharField(
        source='membership_period.label',
        default=None,
        read_only=True,
    )
    membership_period_start = serializers.DateField(
        source='membership_period.start_date',
        default=None,
        read_only=True,
    )
    membership_period_end = serializers.DateField(
        source='membership_period.end_date',
        default=None,
        read_only=True,
    )

    class Meta:
        model = PaymentReceipt
        fields = (
            'id',
            'membership_period',
            'membership_period_label',
            'membership_period_start',
            'membership_period_end',
            'payment_date',
            'amount',
            'is_validated',
            'created_at',
            'receipt_file_url',
        )
        read_only_fields = fields

    def get_receipt_file_url(self, obj):
        if not obj.receipt_file:
            return None
        request = self.context.get('request')
        if request:
            return request.build_absolute_uri(obj.receipt_file.url)
        return obj.receipt_file.url


class UserAccountSerializer(BaseUserSerializer):
    is_membership_valid = serializers.ReadOnlyField()
    date_joined = serializers.DateTimeField(read_only=True)
    membership_activated_at = serializers.DateTimeField(read_only=True)
    membership_period_label = serializers.SerializerMethodField()
    membership_start_date = serializers.SerializerMethodField()
    membership_end_date = serializers.SerializerMethodField()
    current_period_start_date = serializers.SerializerMethodField()
    current_period_end_date = serializers.SerializerMethodField()
    payment_receipts = serializers.SerializerMethodField()
    private_articles_count = serializers.SerializerMethodField()
    action_articles_count = serializers.SerializerMethodField()
    active_members_count = serializers.SerializerMethodField()

    class Meta(BaseUserSerializer.Meta):
        model = User
        fields = (
            'id', 'email', 'first_name', 'last_name', 'speciality',
            'phone_number', 'profile_image', 'address', 'qr_code',
            'date_joined', 'membership_activated_at',
            'membership_period_label', 'membership_start_date', 'membership_end_date',
            'current_period_start_date', 'current_period_end_date',
            'is_membership_valid',
            'payment_receipts',
            'private_articles_count', 'action_articles_count', 'active_members_count'
        )
        read_only_fields = (
            'email', 'is_active',
            'date_joined', 'membership_activated_at',
            'membership_period_label', 'membership_start_date', 'membership_end_date',
            'current_period_start_date', 'current_period_end_date',
            'payment_receipts',
            'private_articles_count', 'action_articles_count', 'active_members_count'
        )

    @property
    def _current_period(self):
        """Cache la période globale active pour éviter les requêtes répétées."""
        if not hasattr(self, '_cached_current_period'):
            self._cached_current_period = MembershipPeriod.get_current()
        return self._cached_current_period

    def get_membership_period_label(self, obj):
        if obj.membership_period:
            return obj.membership_period.label
        return None

    def get_membership_start_date(self, obj):
        if obj.membership_period:
            return obj.membership_period.start_date
        return None

    def get_membership_end_date(self, obj):
        if obj.membership_period:
            return obj.membership_period.end_date
        return None

    def get_current_period_start_date(self, obj):
        period = self._current_period
        return period.start_date if period else None

    def get_current_period_end_date(self, obj):
        period = self._current_period
        return period.end_date if period else None

    def get_payment_receipts(self, obj):
        receipts = obj.payment_receipts.select_related('membership_period').all()
        return PaymentReceiptSerializer(
            receipts,
            many=True,
            context=self.context,
        ).data

    def get_private_articles_count(self, obj):
        return Article.objects.filter(is_adherent=True).count()

    def get_action_articles_count(self, obj):
        return Article.objects.filter(category__slug='action').count()

    def get_active_members_count(self, obj):
        return User.objects.filter(is_active=True).count()
