from django.utils import timezone
from rest_framework import serializers

from .models import User


class RegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=False)
    login_pin = serializers.CharField(write_only=True, required=False)
    mobile_number = serializers.CharField(write_only=True, required=False, source='mobile')

    class Meta:
        model = User
        fields = ('id', 'email', 'mobile', 'mobile_number', 'first_name', 'last_name', 'password', 'login_pin', 'auth_provider')

    def validate_email(self, value):
        if value and User.objects.filter(email=value).exists():
            raise serializers.ValidationError('This email is already registered.')
        return value

    def validate_mobile(self, value):
        if value and User.objects.filter(mobile=value).exists():
            raise serializers.ValidationError('This mobile number is already registered.')
        return value

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        login_pin = validated_data.pop('login_pin', None)
        user = User.objects.create_user(password=password, login_pin=login_pin, **validated_data)
        return user


class LoginSerializer(serializers.Serializer):
    mobile = serializers.CharField(required=False)
    pin = serializers.CharField(write_only=True, required=False)
    email = serializers.EmailField(required=False)
    password = serializers.CharField(write_only=True, required=False)

    def validate(self, data):
        # Admin login: email + password
        email = data.get('email', '').strip()
        password = data.get('password', '')
        if email and password:
            try:
                user = User.objects.get(email=email)
            except User.DoesNotExist:
                raise serializers.ValidationError('Invalid credentials.')
            if not user.is_active:
                raise serializers.ValidationError('Account is not active. Please verify your email first.')
            if not user.is_staff and str(getattr(user, 'role', '') or '').upper() != 'ADMIN':
                raise serializers.ValidationError('Not an admin user.')
            if not user.check_password(password):
                raise serializers.ValidationError('Invalid credentials.')
            data['user'] = user
            return data

        # Mobile login: mobile + pin
        mobile = data.get('mobile', '').strip()
        pin = data.get('pin', '')
        if mobile and pin:
            try:
                user = User.objects.get(mobile=mobile)
            except User.DoesNotExist:
                raise serializers.ValidationError('Invalid credentials.')
            if not user.is_active:
                raise serializers.ValidationError('Account is not active. Please verify your email first.')
            if not user.check_login_pin(pin):
                raise serializers.ValidationError('Invalid credentials.')
            data['user'] = user
            return data

        raise serializers.ValidationError('Provide either (mobile + pin) or (email + password) for login.')


class SendOTPSerializer(serializers.Serializer):
    email = serializers.EmailField(required=False)
    mobile = serializers.CharField(required=False)


class VerifyOTPSerializer(serializers.Serializer):
    email = serializers.EmailField(required=False)
    mobile = serializers.CharField(required=False)
    otp = serializers.CharField()


class UserSerializer(serializers.ModelSerializer):
    avatar_url = serializers.SerializerMethodField()
    can_access_vendor_portal = serializers.SerializerMethodField()
    can_access_franchise_portal = serializers.SerializerMethodField()
    vendor_portal_status = serializers.SerializerMethodField()

    class Meta:
        model = User
        fields = (
            'id', 'email', 'mobile', 'first_name', 'last_name',
            'auth_provider', 'is_email_verified', 'is_mobile_verified',
            'is_onboarding_complete', 'date_joined', 'role', 'is_staff',
            'is_active', 'pan_number', 'date_of_birth', 'pincode', 'location',
            'address_line1', 'address_line2', 'city', 'state', 'country', 'occupation',
            'google_avatar_url', 'profile_image', 'avatar_url',
            'accept_terms', 'referred_by',
            'must_change_login_pin',
            # compatibility / computed fields
            'email_verified', 'created_at', 'location_approval_status',
            # referral fields
            'referral_code', 'referred_by_code', 'referred_users_count', 'referrer_info',
            # display helpers
            'occupation_name', 'pincode_code',
            # portal entitlements
            'can_access_vendor_portal', 'can_access_franchise_portal', 'vendor_portal_status',
        )
        read_only_fields = fields

    email_verified = serializers.SerializerMethodField()
    created_at = serializers.SerializerMethodField()
    location_approval_status = serializers.SerializerMethodField()
    referred_by_code = serializers.SerializerMethodField()
    referred_users_count = serializers.SerializerMethodField()
    referrer_info = serializers.SerializerMethodField()
    occupation_name = serializers.SerializerMethodField()
    pincode_code = serializers.SerializerMethodField()

    def get_email_verified(self, obj):
        return getattr(obj, 'is_email_verified', False)

    def get_created_at(self, obj):
        # Provide a `created_at` alias for frontend compatibility
        return obj.date_joined

    def get_location_approval_status(self, obj):
        # No separate Location model: treat non-empty location as approved.
        loc = getattr(obj, 'location', None)
        return 'approved' if loc else 'pending'

    def get_referred_by_code(self, obj):
        if getattr(obj, 'referred_by', None):
            return getattr(obj.referred_by, 'referral_code', None)
        return None

    def get_referred_users_count(self, obj):
        try:
            return obj.referrals.count()
        except Exception:
            return 0

    def get_referrer_info(self, obj):
        referrer = getattr(obj, 'referred_by', None)
        if not referrer:
            return None

        first_name = (getattr(referrer, 'first_name', '') or '').strip()
        last_name = (getattr(referrer, 'last_name', '') or '').strip()
        full_name = f"{first_name} {last_name}".strip()

        return {
            'id': str(referrer.id),
            'name': full_name or (referrer.mobile or referrer.email or 'Referrer'),
            'mobile': getattr(referrer, 'mobile', '') or '',
            'email': getattr(referrer, 'email', '') or '',
            'referral_code': getattr(referrer, 'referral_code', '') or '',
        }

    def get_occupation_name(self, obj):
        occ = getattr(obj, 'occupation', None)
        return getattr(occ, 'name', None) if occ else None

    def get_pincode_code(self, obj):
        pin = getattr(obj, 'pincode', None)
        return getattr(pin, 'code', None) if pin else None

    def get_avatar_url(self, obj):
        request = self.context.get('request') if hasattr(self, 'context') else None
        profile_image = getattr(obj, 'profile_image', None)
        if profile_image and getattr(profile_image, 'name', None):
            try:
                image_url = profile_image.url
                return request.build_absolute_uri(image_url) if request else image_url
            except Exception:
                pass

        return getattr(obj, 'google_avatar_url', None)

    def get_can_access_vendor_portal(self, obj):
        try:
            vendor = getattr(obj, 'vendor_profile', None)
            if vendor:
                return bool(getattr(vendor, 'is_active', False) and str(getattr(vendor, 'status', '')).upper() == 'APPROVED')
        except Exception:
            pass
        return str(getattr(obj, 'role', '') or '').upper() == 'VENDOR'

    def get_vendor_portal_status(self, obj):
        try:
            vendor = getattr(obj, 'vendor_profile', None)
            if vendor:
                return {
                    'has_profile': True,
                    'status': getattr(vendor, 'status', None),
                    'is_active': bool(getattr(vendor, 'is_active', False)),
                }
        except Exception:
            pass
        return {
            'has_profile': False,
            'status': None,
            'is_active': False,
        }

    def get_can_access_franchise_portal(self, obj):
        try:
            from apps.geo.models import FranchiseAssignment

            return FranchiseAssignment.objects.filter(user=obj, is_active=True).exists()
        except Exception:
            pass
        return str(getattr(obj, 'role', '') or '').upper() == 'FRANCHISE'


class GoogleTokenLoginSerializer(serializers.Serializer):
    token = serializers.CharField(help_text='Google ID token from client-side OAuth flow')


class LinkMobileSerializer(serializers.Serializer):
    mobile = serializers.CharField(max_length=30)
    otp = serializers.CharField(max_length=10)

    def validate_mobile(self, value):
        value = value.strip()
        if not value:
            raise serializers.ValidationError('Mobile number is required.')
        if User.objects.filter(mobile=value).exists():
            raise serializers.ValidationError('This mobile number is already in use.')
        return value
