import random
import secrets

import requests
from django.conf import settings
from django.contrib.auth.hashers import make_password, check_password
from django.utils import timezone
from django.utils.decorators import decorator_from_middleware_with_args
from django.utils.decorators import method_decorator
from google.auth.transport import requests as google_requests
from google.oauth2 import id_token
from rest_framework import status
from rest_framework.views import APIView

from authenticate import serializers as authenticate_serializers, conf
from authenticate.attemp_recaptcha import check_verification_attempts_and_recaptcha_helper, \
    check_wrong_verification_attempts_and_recaptcha_helper, check_wrong_password_attempts_and_recaptcha_helper
from authenticate.middleware import AuthorizatorMiddleware
from authenticate.models import VerificationAttempt, User, WrongPasswordAttempt, UserJWT, WrongVerificationAttempt
from authenticate.utils import get_client_ip
from collaborators import defined_roles as dr
from collaborators.models import Access, InviteAccess
from institute.models import Institute
from payment.models import UserBalanceHistory
from plan.models import InstitutePlan
from utils import pushers
from utils.myresponse import MyResponse
from utils.utils import compress_image, logs_adder
from wikiazma.middleware import SerializerValidationMiddleware

serializer_validation_middleware = decorator_from_middleware_with_args(
    SerializerValidationMiddleware)

user_token_or_server_token_or_server_API_key_validation_and_authorization = decorator_from_middleware_with_args(
    AuthorizatorMiddleware)


# TODO: we should check our APIs that they not leak any information about our users and other data; up to now we not check them exactly


@method_decorator(serializer_validation_middleware(authenticate_serializers.SendAVerificationCodeInputSerializer), name='post')
class SendAVerificationCode(APIView):
    """
    This API class is responsible for handling incoming requests for registering.
    it takes an email from user and after some checks creates a temp user record
    in database with that email and also add a random verification code for temp user.
    """

    def post(self, request):
        # ## ########################## Block 1 ######################################
        # Receive user inputs from serializer
        email = request.middleware_serializer_data.get('email')
        if email:
            email = email.lower()
        phone = request.middleware_serializer_data.get('phone')
        username = request.middleware_serializer_data.get('username')
        g_recaptcha_response = request.POST.get('g-recaptcha-response')
        # ## ########################## Block 2 ######################################
        ip = get_client_ip(request)
        number = secrets.randbelow(900000) + 100000

        result = check_verification_attempts_and_recaptcha_helper(
            ip, g_recaptcha_response, email, phone, username)
        if result is not None:
            return result

        if email:
            VerificationAttempt.objects.create(
                email=email, ip=ip, verification_code=number)
            email_content = pushers.register_verification_code_email_content_builder(
                request, number, email)
            pushers.send_html_email(email_content, email)
        elif phone:
            VerificationAttempt.objects.create(
                phone=phone, ip=ip, verification_code=number)

            pushers.send_verification_code_sms(phone, number)
        elif username:
            try:
                user = User.objects.get(username=username)
            except:
                return MyResponse(request, {'status': 'error', 'message': 'User with this username was not found', 'data': {}},
                                  status=status.HTTP_404_NOT_FOUND)
            if not user.recovery_phone:
                return MyResponse(request, {'status': 'error', 'message': 'There is\'t any known method for verifying this user', 'data': {}},
                                  status=status.HTTP_404_NOT_FOUND)
            VerificationAttempt.objects.create(
                username=username, recovery_phone=user.recovery_phone, ip=ip, verification_code=number)

            pushers.send_verification_code_sms(user.recovery_phone, number)
        else:
            data = {'status': 'error', 'message': 'Bad request'}
            return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

        data = {'status': 'ok', 'message': 'Successful'}
        return MyResponse(request, data, status=status.HTTP_200_OK)


@method_decorator(serializer_validation_middleware(authenticate_serializers.InputLoginOrVerificationCodeCheckSerializer),
                  name='post')
class LoginOrVerificationCodeCheck(APIView):
    """
    This API class is just for checking the code validity which was created in Register API.
    it takes an email and a code from the client and checks if the code belongs to that email or not.
    """

    def post(self, request):
        # ## ########################## Block 1 ######################################
        # Receive user inputs from serializer
        email = request.middleware_serializer_data.get('email')
        phone = request.middleware_serializer_data.get('phone')
        code = request.middleware_serializer_data.get('code')
        # ## ########################## Block 2 ######################################
        # try to get verification_attempt and checks it's attempts
        ip = get_client_ip(request)
        g_recaptcha_response = request.POST.get('g-recaptcha-response')
        result = check_wrong_verification_attempts_and_recaptcha_helper(ip, g_recaptcha_response, email=email,
                                                                        phone=phone)
        if result is not None:
            return result
        if email:
            email = email.lower()
            try:
                VerificationAttempt.objects.get(
                    email=email, verification_code=code)
            except:
                WrongVerificationAttempt.objects.create(ip=ip, email=email)
                data = {'status': 'error', 'message': 'wrong code'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

            try:
                user = User.objects.get(email=email)
            except:
                return MyResponse(request, {'status': 'ok', 'message': 'Verification Code is valid. Please register.', 'data': {}},
                                  status=status.HTTP_200_OK)
        elif phone:
            try:
                VerificationAttempt.objects.get(
                    phone=phone, verification_code=code)
            except:
                WrongVerificationAttempt.objects.create(ip=ip, phone=phone)
                data = {'status': 'error', 'message': 'wrong code'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

            try:
                user = User.objects.get(phone=phone)
            except:
                return MyResponse(request, {'status': 'ok', 'message': 'Verification Code is valid. Please register.', 'data': {}},
                                  status=status.HTTP_200_OK)
        else:
            data = {'status': 'error', 'message': 'Bad request'}
            return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

        token = UserJWT.create_record(user)

        user_data = authenticate_serializers.UserSerializerForAuthenticateAndCollaboratorsAndInstitute(
            user).data

        data = {'status': 'ok', 'message': 'Successful',
                'data': {'token': token, 'user': user_data}}
        return MyResponse(request, data, status=status.HTTP_200_OK)


@method_decorator(serializer_validation_middleware(authenticate_serializers.InputRegisterUserSerializer), name='post')
class RegisterUser(APIView):
    """
    This API class is responsible for handling incoming requests for register verifying.
    it takes an email, a password and a code from user and checks if the code belongs to that email
    in temp user and then create a User in database
    """

    def post(self, request):
        # ## ########################## Block 1 ######################################
        # Receive user inputs from serializer
        username = request.middleware_serializer_data.get('username')
        email = request.middleware_serializer_data.get('email')
        phone = request.middleware_serializer_data.get('phone')
        password = request.middleware_serializer_data.get('password')
        code = request.middleware_serializer_data.get('code')
        first_name = request.middleware_serializer_data.get('first_name')
        last_name = request.middleware_serializer_data.get('last_name')
        image = request.middleware_serializer_data.get('image')
        ip = get_client_ip(request)
        g_recaptcha_response = request.POST.get('g-recaptcha-response')
        # ## ########################## Block 2 ######################################
        # try to get verification_attempt and checks it's attempts and then check the existance of code in verification_attempt verification_codes
        result = check_wrong_verification_attempts_and_recaptcha_helper(
            ip, g_recaptcha_response, email=email, phone=phone)
        if result is not None:
            return result
        # ## ########################## Block 3 ######################################
        if email:
            email = email.lower()
            try:
                verification_attempt = VerificationAttempt.objects.get(
                    email=email, verification_code=code)
            except:
                WrongVerificationAttempt.objects.create(ip=ip, email=email)
                data = {'status': 'error',
                        'message': 'Your verification code is incorrect'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

            try:
                User.objects.get(email=email)
                data = {'status': 'error', 'message': 'duplicate email'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)
            except:
                pass

            # ############################ Block 4 ######################################
            user = User.objects.create(
                email=email, last_name=last_name, first_name=first_name, image=image)

            if username:
                user.username = username

            if password:
                user.password = make_password(str(user.id) + password)
            if image:
                user.image = compress_image(
                    image, max_side_size=conf.maximum_side_size_compress_for_authenticate_profile_image)
                user.image_thumbnail = compress_image(image,
                                                      max_side_size=conf.maximum_side_size_compress_for_authenticate_profile_image_thumbnail)
            user.save()
        # ## ########################## Block 5 ######################################
        elif phone:
            try:
                verification_attempt = VerificationAttempt.objects.get(
                    phone=phone, verification_code=code)
            except:
                WrongVerificationAttempt.objects.create(ip=ip, phone=phone)
                data = {'status': 'error',
                        'message': 'Your verification code is incorrect'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)
            try:
                User.objects.get(phone=phone)
                data = {'status': 'error', 'message': 'Bad request'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)
            except:
                user = User.objects.create(
                    phone=phone, last_name=last_name, first_name=first_name, image=image)

                if username:
                    user.username = username

                if password:
                    user.password = make_password(str(user.id) + password)
                if image:
                    user.image = compress_image(
                        image, max_side_size=conf.maximum_side_size_compress_for_authenticate_profile_image)

                    user.image_thumbnail = compress_image(image,
                                                          max_side_size=conf.maximum_side_size_compress_for_authenticate_profile_image_thumbnail)
                user.save()
        else:
            data = {'status': 'error', 'message': 'Bad request'}
            return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

        token = UserJWT.create_record(user)

        UserBalanceHistory.create_initial_balance_history(user)

        # ############################ Block 6 ######################################
        sandbox_institute = Institute.objects.create(
            name='sandbox',
            user=user,
            sandbox=True
        )
        # TODO maybe create sandbox abstract plan?
        InstitutePlan.objects.create(
            institute=sandbox_institute,
            name='sandbox',
            start_date=timezone.now(),
            max_collaborators_per_institute=0,
            max_examinee_per_institute=0,
            assign_examinee_to_exam=False,
            assign_public_link_to_exam=False,
            enable_institute_api_key=False
        )
        Access.objects.create(
            user=user,
            institute=sandbox_institute,
            roles=[dr.__ROLE_OWNER__],
            institute_name=sandbox_institute.name,
            institute_created_at=sandbox_institute.created_at
        )
        verification_attempt.delete()

        # ############################ Block 7 ######################################
        # doc

        if phone:
            InviteAccess.objects.filter(invited_phone=phone, canceled=False, rejected=False, invited_user__isnull=True).update(
                invited_user=user)

        elif email:
            InviteAccess.objects.filter(invited_email=email, canceled=False, rejected=False, invited_user__isnull=True).update(
                invited_user=user)

        # ############################ Block 8 ######################################
        user_data = authenticate_serializers.UserSerializerForAuthenticateAndCollaboratorsAndInstitute(
            user).data
        return MyResponse(request, {'status': 'ok', 'message': 'Successful',
                                    'data': {'token': token, 'user': user_data}},
                          status=status.HTTP_200_OK)


@method_decorator(serializer_validation_middleware(authenticate_serializers.InputLoginWithPasswordSerializer), name='post')
class LoginWithPassword(APIView):
    """
    This API class is responsible for handling incoming requests for login.
    it takes an email and a password from user and checks if the code belongs to that email
    in temp user and then create a User in database
    """

    def post(self, request):
        # ## ########################## Block 1 ######################################
        # Receive user inputs from serializer
        username = request.middleware_serializer_data.get('username')
        email = request.middleware_serializer_data.get('email')
        phone = request.middleware_serializer_data.get('phone')
        password = request.middleware_serializer_data.get('password')
        ip = get_client_ip(request)
        g_recaptcha_response = request.POST.get('g-recaptcha-response')
        # ## ########################## Block 2 ######################################
        # get user ip and try to get or create a WrongPasswordAttempt record and check its attempts

        result = check_wrong_password_attempts_and_recaptcha_helper(
            ip, g_recaptcha_response, email=email, phone=phone, username=username)
        if result is not None:
            return result
        # ## ########################## Block 3 ######################################
        if email:
            email = email.lower()
            try:
                user = User.objects.get(email=email)
                if not check_password(str(user.id) + password, user.password):
                    WrongPasswordAttempt.objects.create(email=email, ip=ip)
                    data = {'status': 'error',
                            'message': 'Email or password is incorrect'}
                    return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)
            except:
                WrongPasswordAttempt.objects.create(email=email, ip=ip)
                data = {'status': 'error',
                        'message': 'Email or password is incorrect'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)
        elif phone:
            try:
                user = User.objects.get(phone=phone)
                if not check_password(str(user.id) + password, user.password):
                    WrongPasswordAttempt.objects.create(phone=phone, ip=ip)
                    data = {'status': 'error',
                            'message': 'Phone or password is incorrect'}
                    return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)
            except:
                WrongPasswordAttempt.objects.create(phone=phone, ip=ip)

                data = {'status': 'error',
                        'message': 'Phone or password is incorrect'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)
        elif username:
            try:
                user = User.objects.get(username=username)
                if not check_password(str(user.id) + password, user.password):
                    WrongPasswordAttempt.objects.create(
                        username=username, ip=ip)
                    data = {'status': 'error',
                            'message': 'Username or password is incorrect'}
                    return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)
            except:
                WrongPasswordAttempt.objects.create(username=username, ip=ip)

                data = {'status': 'error',
                        'message': 'Username or password is incorrect'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)
        else:
            data = {'status': 'error', 'message': 'Bad request'}
            return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

        token = UserJWT.create_record(user)
        user_data = authenticate_serializers.UserSerializerForAuthenticateAndCollaboratorsAndInstitute(
            user).data
        return MyResponse(request, {'status': 'ok', 'message': 'Successful',
                                    'data': {'token': token, 'user': user_data}}, status=status.HTTP_200_OK)


@method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
                  name='post')
class LogoutUser(APIView):
    """
    This API class is responsible for handling incoming requests for logout.
    it takes the user token and if valid, deletes it.
    """

    def post(self, request):
        request.middleware_token_record.delete()
        return MyResponse(request, {'status': 'ok', 'message': 'Successful'}, status=status.HTTP_200_OK)


@method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
                  name='post')
@method_decorator(serializer_validation_middleware(authenticate_serializers.InputChangeUserPasswordSerializer), name='post')
class ChangeUserPassword(APIView):
    """
    This API class is responsible for handling incoming datas for changing password.
    it takes the password and code from the user and if the code belongs to the user of incoming token,
    then hashes and sets the new password to the user.
    """

    def post(self, request):
        # ## ########################## Block 1 ######################################
        # Receive user inputs from serializer
        old_password = request.middleware_serializer_data.get('old_password')
        new_password = request.middleware_serializer_data.get('new_password')
        ip = get_client_ip(request)
        g_recaptcha_response = request.POST.get('g-recaptcha-response')
        # ############################ Block 2 ######################################
        user = request.middleware_user
        result = check_wrong_password_attempts_and_recaptcha_helper(
            ip, g_recaptcha_response, user.email, user.phone, user.username)
        if result is not None:
            return result
        if user.password is not None and check_password(str(user.id) + old_password, user.password):

            user.password = make_password(str(user.id) + new_password)
            user.save()
            return MyResponse(request, {'status': 'Successful', 'message': 'Password updated successfully'},
                              status=status.HTTP_200_OK)
        else:
            WrongPasswordAttempt.objects.create(
                phone=user.phone, email=user.email, ip=ip)
            return MyResponse(request, {'status': 'error', 'message': 'Wrong Old Password'},
                              status=status.HTTP_400_BAD_REQUEST)


@method_decorator(serializer_validation_middleware(authenticate_serializers.InputForgetUserPasswordSerializer), name='post')
class ForgetUserPassword(APIView):
    """
    This API class is responsible for handling incoming datas for forgetting password.
    it takes the email, password and code from the user and if the code belongs to the user of incoming email,
    then hashes and sets the new password to the user.
    """

    def post(self, request):
        # ## ########################## Block 1 ######################################
        # Receive user inputs from serializer
        email = request.middleware_serializer_data.get('email')
        phone = request.middleware_serializer_data.get('phone')
        username = request.middleware_serializer_data.get('username')
        new_password = request.middleware_serializer_data.get('new_password')
        code = request.middleware_serializer_data.get('code')
        ip = get_client_ip(request)
        g_recaptcha_response = request.POST.get('g-recaptcha-response')
        # ############################ Block 2 ######################################
        result = check_verification_attempts_and_recaptcha_helper(
            ip, g_recaptcha_response, email, phone, username)
        if result is not None:
            return result
        if email:
            email = email.lower()
            try:
                VerificationAttempt.objects.get(
                    email=email, verification_code=code)
            except:
                WrongVerificationAttempt.objects.create(email=email, ip=ip)
                data = {'status': 'error',
                        'message': 'Your verification code is incorrect'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

            try:
                user = User.objects.get(email=email)
            except:
                data = {"status": "error", "message": "Register First"}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

            user.password = make_password(str(user.id) + new_password)
            user.save()
            token = UserJWT.create_record(user)
        elif phone:
            try:
                VerificationAttempt.objects.get(
                    phone=phone, verification_code=code)
            except:
                WrongVerificationAttempt.objects.create(phone=phone, ip=ip)
                data = {'status': 'error',
                        'message': 'Your verification code is incorrect'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

            try:
                user = User.objects.get(phone=phone)
            except:
                # This should never happen because the VerificationAttempt with this phone was already fetched from database
                logs_adder(f'CRITICAL: Invalid State #44875265927245')
                data = {"status": "error", "message": "Register First"}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

            user.password = make_password(str(user.id) + new_password)
            user.save()
            token = UserJWT.create_record(user)
        elif username:
            try:
                VerificationAttempt.objects.get(
                    username=username, verification_code=code)
            except:
                WrongVerificationAttempt.objects.create(
                    username=username, ip=ip)
                data = {'status': 'error',
                        'message': 'Your verification code is incorrect'}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

            try:
                user = User.objects.get(username=username)
            except:
                # This should never happen because the VerificationAttempt with this username was already fetched from database
                logs_adder(f'CRITICAL: Invalid State #44875265927245')
                data = {"status": "error", "message": "Register First"}
                return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

            user.password = make_password(str(user.id) + new_password)
            user.save()
            token = UserJWT.create_record(user)
        else:
            data = {'status': 'error', 'message': 'Bad request'}
            return MyResponse(request, data, status=status.HTTP_400_BAD_REQUEST)

        data = {'status': 'ok', 'message': 'Successful', 'token': token}
        return MyResponse(request, data, status=status.HTTP_200_OK)


@method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
                  name='post')
@method_decorator(serializer_validation_middleware(authenticate_serializers.InputEditUserSerializer), name='post')
class EditUser(APIView):

    def post(self, request):
        # ########################## Block 1 ################################
        # receiving user inputs from serializer
        first_name = request.middleware_serializer_data.get('first_name')
        last_name = request.middleware_serializer_data.get('last_name')
        info = request.middleware_serializer_data.get('info')
        image = request.middleware_serializer_data.get('image')
        poster = request.middleware_serializer_data.get('poster')
        delete_image = request.middleware_serializer_data.get('delete_image')
        delete_poster = request.middleware_serializer_data.get('delete_poster')
        # ########################## Block 2 ################################
        user = request.middleware_user

        if first_name or first_name == '':
            user.first_name = first_name

        if last_name or last_name == '':
            user.last_name = last_name

        if info or info == '':
            user.info = info

        if delete_image:
            user.image.delete()
            user.image_thumbnail.delete()

        if delete_poster:
            user.poster.delete()
            user.poster_thumbnail.delete()

        if image:
            user.image = compress_image(
                image, max_side_size=conf.maximum_side_size_compress_for_authenticate_profile_image)
            user.image_thumbnail = compress_image(image,
                                                  max_side_size=conf.maximum_side_size_compress_for_authenticate_profile_image_thumbnail)
        if poster:
            user.poster = compress_image(
                poster, max_side_size=conf.maximum_side_size_compress_for_authenticate_poster)
            user.poster_thumbnail = compress_image(
                poster, max_side_size=conf.maximum_side_size_compress_for_authenticate_poster_thumbnail)
        user.save()
        user_data = authenticate_serializers.UserSerializerForAuthenticateAndCollaboratorsAndInstitute(
            user).data
        return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'user': user_data},
                          status=status.HTTP_200_OK)


@method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
                  name='post')
@method_decorator(serializer_validation_middleware(authenticate_serializers.InputUserProfileSerializer), name='post')
class UserProfile(APIView):
    """
    This API class is responsible for handling incoming requests for accessing user profile data.
    it takes the user token and if valid, responses informations of the user of incoming token in desired format.
    """

    def post(self, request):
        # get user_id from serializer and try to get its user and return its profile
        user_id = request.middleware_serializer_data.get('user_id')
        try:
            user = User.objects.get(id=user_id)
        except:
            return MyResponse(request, {'status': 'error', 'message': 'User not found'},
                              status=status.HTTP_404_NOT_FOUND)
        user_data = authenticate_serializers.UserSerializerForAuthenticateAndCollaboratorsAndInstitute(
            user).data
        return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'user': user_data},
                          status=status.HTTP_200_OK)


@method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
                  name='post')
class UserAccount(APIView):
    """
    This API class is responsible for handling incoming requests for accessing user account data.
    it takes the user token and if valid, responses informations of the user of incoming token in desired format.
    """

    def post(self, request):
        user_data = authenticate_serializers.UserSerializerForAuthenticateAndCollaboratorsAndInstitute(
            request.middleware_user).data
        return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'user': user_data},
                          status=status.HTTP_200_OK)


@method_decorator(serializer_validation_middleware(authenticate_serializers.InputUserGoogleLoginSerializer), name='post')
class UserGoogleLogin(APIView):
    """
    This API class is responsible for handling incoming requests for login via Google.
    it takes a g_id_token and try to decode it. if it was successful, then we create a user with the incoming data,
    otherwise the client faces an 403 forbidden error!
    """

    def post(self, request):
        g_id_token = request.middleware_serializer_data.get('g_id_token')
        try:
            idinfo = id_token.verify_oauth2_token(
                g_id_token, google_requests.Request(), settings.GOOGLE_CLIENT_ID)
            if not idinfo['email_verified'] or idinfo['aud'] != settings.GOOGLE_CLIENT_ID or idinfo['iss'] not in [
                    'accounts.google.com', 'https://accounts.google.com']:
                return MyResponse(request, {'status': 'error', 'message': 'Something was wrong'},
                                  status=status.HTTP_403_FORBIDDEN)
            try:
                user = User.objects.get(email=idinfo['email'])
            except:
                user = User.objects.create(
                    first_name=idinfo['given_name'],
                    last_name=idinfo['family_name'],
                    email=idinfo['email'],
                    g_subject=idinfo['sub']
                )
                token = UserJWT.create_record(user)
                user_data = authenticate_serializers.UserSerializerForAuthenticateAndCollaboratorsAndInstitute(
                    user).data
                return MyResponse(request, {'status': 'ok', 'message': 'Successful',
                                            'data': {'token': token, 'user': user_data}}, status=status.HTTP_200_OK)
            if not user.g_subject:
                user.g_subject = idinfo['sub']
                user.save()
            else:
                if user.g_subject != idinfo['sub']:
                    return MyResponse(request, {'status': 'error', 'message': 'Something was wrong'},
                                      status=status.HTTP_403_FORBIDDEN)
            token = UserJWT.create_record(user)
            user_data = authenticate_serializers.UserSerializerForAuthenticateAndCollaboratorsAndInstitute(
                user).data
            return MyResponse(request,
                              {'status': 'ok', 'message': 'Successful',
                                  'data': {'token': token, 'user': user_data}},
                              status=status.HTTP_200_OK)
        except:
            # We can't add a logger to logs Google errors because we don't know where does it come from
            return MyResponse(request, {'status': 'error', 'message': 'Something was wrong'},
                              status=status.HTTP_403_FORBIDDEN)


@method_decorator(serializer_validation_middleware(authenticate_serializers.InputUserFacebookLoginSerializer), name='post')
class UserFacebookLogin(APIView):
    """
    This API class is responsible for handling incoming requests for login via Facebook.
    it takes a g_id_token and try to decode it. if it was successful, then we create a user with the incoming data,
    otherwise the client faces an 403 forbidden error!
    """

    def post(self, request):
        fb_code = request.middleware_serializer_data.get('fb_code')
        try:
            fb_data = {
                'code': fb_code,
                'client_id': settings.FACEBOOK_CLIENT_ID,
                'redirect_uri': settings.FACEBOOK_REDIRECT_URI,
                'client_secret': settings.FACEBOOK_CLIENT_SECRET
            }
            first_response = requests.get(
                'https://graph.facebook.com/v7.0/oauth/access_token', params=fb_data)
            if first_response.status_code != 200:
                return MyResponse(request, {'status': 'error', 'message': 'Something was wrong'},
                                  status=status.HTTP_403_FORBIDDEN)
            second_response = requests.get('https://graph.facebook.com/me?fields=first_name,last_name,email', params={
                'access_token': first_response.json()['access_token']})
            if second_response.status_code != 200:
                return MyResponse(request, {'status': 'error', 'message': 'Something was wrong'},
                                  status=status.HTTP_403_FORBIDDEN)
            client_data = second_response.json()
            try:
                user = User.objects.get(email=client_data['email'])
            except:
                user = User.objects.create(
                    first_name=client_data['first_name'],
                    last_name=client_data['last_name'],
                    email=client_data['email'],
                    fb_id=client_data['id']
                )
                token = UserJWT.create_record(user)
                user_data = authenticate_serializers.UserSerializerForAuthenticateAndCollaboratorsAndInstitute(
                    user).data
                return MyResponse(request, {'status': 'ok', 'message': 'Successful',
                                            'data': {'token': token, 'user': user_data}}, status=status.HTTP_200_OK)
            if not user.fb_id:
                user.fb_id = client_data['id']
                user.save()
            else:
                if user.fb_id != client_data['id']:
                    return MyResponse(request, {'status': 'error', 'message': 'Something was wrong'},
                                      status=status.HTTP_403_FORBIDDEN)
            token = UserJWT.create_record(user)
            user_data = authenticate_serializers.UserSerializerForAuthenticateAndCollaboratorsAndInstitute(
                user).data
            return MyResponse(request,
                              {'status': 'ok', 'message': 'Successful',
                                  'data': {'token': token, 'user': user_data}},
                              status=status.HTTP_200_OK)
        except:
            return MyResponse(request, {'status': 'error', 'message': 'Something was wrong'},
                              status=status.HTTP_403_FORBIDDEN)


@method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
                  name='post')
class UserBalance(APIView):
    def post(self, request):
        caller = request.middleware_user
        balance = caller.balance
        return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'balance': balance}}, status=status.HTTP_200_OK)
