from rest_framework.views import APIView
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.generics import ListAPIView, CreateAPIView
from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT, HTTP_500_INTERNAL_SERVER_ERROR, HTTP_401_UNAUTHORIZED
from rest_framework import status
from django.contrib.auth import get_user_model
from ..settings import auth_init_check
from ..validators import mobile_number as mobile_validator
from ..serializers import OTPCodeSerializer, SessionSerializer, RegisterSerializer
from ..models import UserModel as User
from django.db import transaction, IntegrityError, OperationalError
from django.core.exceptions import ImproperlyConfigured
import traceback
import time
UserGet = get_user_model()
import logging
logger = logging.getLogger(__name__)



class Authentication(APIView):
    permission_classes = (AllowAny, )
    serializer_class = OTPCodeSerializer
    model = serializer_class.Meta.model

    def post(self, request):
        """
        prams:
            mobile_number:   str (len: 11)     (exp: 09211892425)
            code: str (len: otp_code_length()) (exp: 652479)
            group: str (len: 7)         (exp: service)

        response:
            HTTP_204_NO_CONTENT             {'error': [params requirements and validations]}
            HTTP_500_INTERNAL_SERVER_ERROR  {'error': 'contact the support..'}
            HTTP_200_OK                     {'access': '', 'refresh': ''}
        """
        try:
            if 'mobile_number' not in request.data:
                return Response({'error': 'mobile_number is required.'}, status=HTTP_204_NO_CONTENT)
            mobile_number = request.data['mobile_number']
            if not mobile_number:
                return Response({'error': 'mobile_number may not be blank.'}, status=HTTP_204_NO_CONTENT)
            mobile_number_isvalid = mobile_validator(mobile_number)
            if mobile_number_isvalid != True:
                return Response({'error': mobile_number_isvalid}, status=HTTP_204_NO_CONTENT)
            if 'code' not in request.data:
                return Response({'error': 'code is required.'}, status=HTTP_204_NO_CONTENT)
            
            icheck = auth_init_check()
            if icheck and isinstance(icheck, dict) and 'AUTH_SERVICE' in icheck and 'AUTH_METHOD' in icheck:
                otp_code = request.data['code']
                otp = self.model.objects.filter(mobile_number=mobile_number).filter(code=otp_code).first()
                if not otp:
                    return Response({'error': 'The code sent to this mobile number was not found.'}, status=HTTP_401_UNAUTHORIZED)
                
                if otp.check_code():
                    user = None
                    # login / signup
                    try:
                        group = int(request.data.get('group'), 0)
                    except (ValueError, TypeError):
                        group = 0

                    try:
                        with transaction.atomic():
                            user, created = User.objects.get_or_create(mobile=mobile_number, group=group)
                    
                    except IntegrityError as e:
                        logger.warning("IntegrityError on get_or_create for mobile=%s, group=%s: %s", mobile_number, group, e)
                        time.sleep(0.05)
                        try:
                            user = User.objects.get(mobile=mobile_number, group=group)
                            created = False
                        except User.DoesNotExist:
                            logger.error("After IntegrityError, user still not exists for mobile=%s. Trace: %s", mobile_number, traceback.format_exc())
                            return Response({'error': 'Server error while creating user.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
                    except OperationalError as e:
                        logger.error("OperationalError when accessing DB for mobile=%s: %s\n%s", mobile_number, e, traceback.format_exc())
                        return Response({'error': 'Database operational error, try again later.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
                    except Exception as e:
                        logger.exception("Unexpected error in user get_or_create for mobile=%s: %s", mobile_number, e)
                        return Response({'error': 'An internal error occurred.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

                    

                    # user, created = User.objects.get_or_create(mobile=mobile_number, group=group)
                    if user:
                        auth_method = icheck['AUTH_METHOD']
                        auth_service = icheck['AUTH_SERVICE']
                        if auth_method == 'OTP':
                            match auth_service:
                                case 'rest_framework_simplejwt':
                                    from rest_framework_simplejwt.tokens import RefreshToken, AccessToken, BlacklistedToken
                                    access_token = AccessToken.for_user(user=user.user)
                                    refresh_token = RefreshToken.for_user(user=user.user)
                                    return Response({'access': str(access_token), 'refresh': str(refresh_token)}, status=HTTP_200_OK)
                        else:
                            try:
                                raise ImproperlyConfigured('Authentication configurations in DJANGO_CHELSERU are not done correctly, specify AUTH_METHOD and AUTH_SERVICE.')
                            except ImproperlyConfigured as e:
                                print(f"Configuration Error: {e}")
                                raise   
        except AssertionError as e:
            return Response({'error': str(e)}, status=HTTP_204_NO_CONTENT)
        except Exception:
            logger.exception("Error while creating tokens for user (mobile=%s). user object: %r", mobile_number, user)
            return Response({'error': 'Token generation failed.'}, status=HTTP_500_INTERNAL_SERVER_ERROR)
        # return Response({'error': 'An error occurred while generating or sending the otpcode, please contact the www.chelseru.com support team.'}, status=HTTP_500_INTERNAL_SERVER_ERROR)


class SessionList(ListAPIView):
    permission_classes = (IsAuthenticated, )
    serializer_class = SessionSerializer
    queryset = serializer_class.Meta.model.objects.all()




# Authentication method : Passwd
class RegisterView(CreateAPIView):
    queryset = UserGet.objects.all()
    serializer_class = RegisterSerializer