from django.conf import settings
from django.db.models import Q
from djoser import serializers as djoser_serializers
from djoser.conf import settings as djoser_settings
from rest_framework import serializers
from rest_framework.exceptions import AuthenticationFailed

from .models import User, BonusProgramTransaction, BonusType
from .utils import verify_telegram_authentication


class UserSerializer(serializers.ModelSerializer):
    name = serializers.CharField(source='first_name')
    lastname = serializers.CharField(source='middle_name')
    surname = serializers.CharField(source='last_name')

    class Meta:
        model = User
        fields = ('id', 'email', 'phone', 'role', 'name', 'lastname', 'surname', 'balance', 'referral_code', 'is_draft_user')


class BonusProgramTransactionSerializer(serializers.ModelSerializer):
    order_id = serializers.StringRelatedField(source='order.id', allow_null=True)
    type = serializers.CharField(source='get_type_display')

    class Meta:
        model = BonusProgramTransaction
        fields = ('id', 'type', 'date', 'amount', 'order_id', 'comment', 'was_cancelled')


def non_zero_validator(value):
    if value == 0:
        raise serializers.ValidationError("Value cannot be zero")
    return value


class UserBalanceUpdateSerializer(BonusProgramTransactionSerializer):
    amount = serializers.IntegerField(validators=[non_zero_validator])
    type = serializers.SerializerMethodField()

    class Meta:
        model = BonusProgramTransactionSerializer.Meta.model
        fields = BonusProgramTransactionSerializer.Meta.fields
        read_only_fields = ('id', 'type', 'date')

    def get_type(self, instance):
        # Deposit or spent depending on value
        if instance['amount'] < 0:
            return BonusType.OTHER_WITHDRAWAL
        elif instance['amount'] > 0:
            return BonusType.OTHER_DEPOSIT


class SetInitialPasswordSerializer(djoser_serializers.PasswordSerializer):
    def validate(self, attrs):
        user = getattr(self, "user", None) or self.context["request"].user
        # why assert? There are ValidationError / fail everywhere
        assert user is not None

        if not user.is_superuser and not user.is_draft_user:
            raise serializers.ValidationError("To change password, use /users/change_password endpoint")

        return super().validate(attrs)


class UserCreateSerializer(djoser_serializers.UserCreateSerializer):
    email = serializers.EmailField(required=True)


class TokenCreateSerializer(serializers.Serializer):
    email_or_phone = serializers.CharField()
    password = serializers.CharField(required=False, style={"input_type": "password"})

    default_error_messages = {
        "invalid_credentials": djoser_settings.CONSTANTS.messages.INVALID_CREDENTIALS_ERROR,
        "inactive_account": djoser_settings.CONSTANTS.messages.INACTIVE_ACCOUNT_ERROR,
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.user = None

    def validate(self, attrs):
        email_or_phone = attrs.get('email_or_phone')
        password = attrs.get("password")

        user = User.objects.filter(Q(email=email_or_phone) | Q(phone=email_or_phone)).first()
        if not user or not user.check_password(password) or not user.is_active:
            raise AuthenticationFailed()

        self.user = user
        return attrs


class TelegramCallbackSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    first_name = serializers.CharField(allow_null=True)
    username = serializers.CharField(allow_null=True)
    photo_url = serializers.URLField(allow_null=True)
    auth_date = serializers.IntegerField()
    hash = serializers.CharField()

    def validate(self, attrs):
        verify_telegram_authentication(bot_token=settings.TG_BOT_TOKEN, request_data=attrs)
        return attrs
