import random
from datetime import timedelta

from django.conf import settings
from django.contrib.auth.models import User
from django.db.models import Q
from django.utils import timezone
from rest_framework import status
from rest_framework.permissions import IsAdminUser, AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView

from admins.models import OTPMaster
from admins.serializers import *
from core.custom_exception import CustomAPIException
from core.logger import logger
from core.response import *
from core.response import success_response


# Create your views here.

def generate_otp():
    """
    Generate a 4-digit OTP as a string.
    """
    return str(random.randint(1000, 9999))


class AdminLoginView(APIView):
    """
    API view for admin login.
    """
    serializer_class = AdminLoginSerializer
    permission_classes = [AllowAny]

    def post(self, request, ):
        serializer = self.serializer_class(data=request.data, context={'request': request})
        serializer.is_valid(raise_exception=True)
        try:
            user = User.objects.filter(email__iexact=request.data['email']).first()
            if user:
                if not user.check_password(request.data['password']):
                    return Response(fail_response(code=status.HTTP_409_CONFLICT, message=incorrect_password),
                                    status=status.HTTP_409_CONFLICT)
                if not user.is_superuser:
                    return Response(fail_response(code=status.HTTP_400_BAD_REQUEST, message=only_admin_login),
                                    status=status.HTTP_400_BAD_REQUEST)
                data = AuthenticationSerializer(user, context=request).data
                return Response(success_response(data, code=status.HTTP_200_OK, message=login_success),
                                status=status.HTTP_200_OK)
            else:
                return Response(fail_response(code=status.HTTP_404_NOT_FOUND, message=incorrect_email),
                                status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            logger.error("Error in AdminLoginView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class AdminForgotPasswordView(APIView):
    """
    API view to request password reset and send OTP.
    """
    serializer_class = AdminForgotPasswordViewSerializer
    permission_classes = [AllowAny]

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        email = serializer.data.get("email")
        try:
            otp = settings.TESTING_OTP if settings.IS_TESTING else generate_otp()
            expire_time = timezone.now() + timedelta(minutes=10)

            OTPMaster.objects.update_or_create(email=email, defaults={"otp": otp, "expireAt": expire_time})

            user = User.objects.filter(email__iexact=email).first()
            if user:
                # if not settings.IS_TESTING:
                #     request_reset_email_async(email, user.username, otp)
                return Response(success_response(data={"email": email, "otp": otp}, code=status.HTTP_200_OK,
                                                 message=reset_password_success), status=status.HTTP_200_OK)
            else:
                return Response(fail_response(code=status.HTTP_404_NOT_FOUND, message=incorrect_email),
                                status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            logger.error("Error in Admin ResetPasswordRequestView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class AdminVerifyForgotPasswordOTPView(APIView):
    """
    API view for verifying OTP for registration or password reset.
    """
    serializer_class = AdminVerifyForgotPasswordOTPSerializer
    permission_classes = [AllowAny]

    def post(self, request):
        serializer = self.serializer_class(data=request.data, context={'request': self.request})
        serializer.is_valid(raise_exception=True)
        email = serializer.data.get("email")
        otp = serializer.data.get("otp")
        try:
            user = User.objects.filter(email__iexact=email).first()
            if user:
                check_otp = OTPMaster.objects.filter(email__iexact=email, otp=int(otp)).first()
                if check_otp:
                    check_otp.delete()
                    return Response(
                        success_response(data=None, code=status.HTTP_200_OK, message=otp_verified),
                        status=status.HTTP_200_OK)
                else:
                    return Response(fail_response(code=status.HTTP_404_NOT_FOUND, message=invalid_otp),
                                    status=status.HTTP_404_NOT_FOUND)
            else:
                return Response(fail_response(code=status.HTTP_404_NOT_FOUND, message=incorrect_email),
                                status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            logger.error("Error in Admin OTPVerificationView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class AdminSetNewPasswordView(APIView):
    """
    API view to set a new password for the user.
    """
    serializer_class = AdminSetNewPasswordSerializer
    permission_classes = [AllowAny]

    def post(self, request):
        serializer = self.serializer_class(data=request.data, context={'request': request})
        serializer.is_valid(raise_exception=True)
        email = serializer.data.get("email")
        try:
            user = User.objects.filter(email__iexact=email).first()
            if user:
                user.set_password(request.data.get('newPassword'))
                user.save()
                return Response(success_response(data={}, code=status.HTTP_200_OK, message=password_change_success),
                                status=status.HTTP_200_OK)
            else:
                return Response(fail_response(code=status.HTTP_404_NOT_FOUND, message=invalid_userId),
                                status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            logger.error("Error in Admin SetNewPasswordView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class AdminLogoutView(APIView):
    """
    API view for logout.
    """
    serializer_class = AdminLogoutSerializer
    permission_classes = [IsAdminUser]

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            refresh_token = serializer.data["refresh"]
            token = RefreshToken(refresh_token)
            token.blacklist()
            return Response(success_response(data={}, code=status.HTTP_200_OK, message=logout_success),
                            status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("Error in LogoutView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class CategoryListCreateView(APIView):
    permission_classes = [IsAdminUser]
    serializer_class = CategoryCreateSerializer

    def get(self, request):
        try:
            limit = request.query_params.get('limit', None)
            offset = request.query_params.get('offset', None)
            search_query = request.query_params.get('search', None)

            categories = Category.objects.all().order_by('pk')
            # total_categories = categories.count()

            if search_query:
                categories = categories.filter(Q(description__iexact=search_query.lower()))
                offset = 0
                if not categories:
                    return Response(
                        success_response(None, code=status.HTTP_200_OK, message=no_data), status=status.HTTP_200_OK)

            if limit and offset:
                try:
                    limit = int(limit)
                    offset = int(offset)
                    categories = categories[offset:offset + limit]
                except ValueError as e:
                    raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)

            data = CategoryGetSerializer(categories, context={"request": request}, many=True).data
            data = {
                'totalCategories': categories.count(),
                'data': data
            }
            return Response(
                success_response(data, code=status.HTTP_200_OK, message=category_list),
                status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("Error in Category Get", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data, context={'request': request})
        serializer.is_valid(raise_exception=True)
        try:
            category = serializer.save()
            for i in range(1, 11):
                Craptitude.objects.create(category=category, roundNumber=i)
            data = CategoryGetSerializer(category, context={"request": request}).data
            return Response(
                success_response(data=data, code=status.HTTP_200_OK, message=category_create),
                status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("Error in Category Create View", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class CategoryDetailView(APIView):
    permission_classes = [IsAdminUser]

    def put(self, request, pk):
        try:
            category = Category.objects.get(pk=pk)
            serializer = CategoryUpdateSerializer(category, data=request.data, partial=True)
            serializer.is_valid(raise_exception=True)
            serializer.save()

            data = CategoryGetSerializer(category, context={"request": request}).data
            return Response(
                success_response(data=data, code=status.HTTP_200_OK, message=category_update),
                status=status.HTTP_200_OK)
        except Category.DoesNotExist as e:
            logger.error("Error in Category Patch View:", exc_info=True)
            raise CustomAPIException(detail=str(e),
                                     status_code=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            logger.error("Error in Category Patch View:", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        try:
            category = Category.objects.get(pk=pk)
            category.delete()
            return Response(
                success_response(data=None, code=status.HTTP_200_OK, message=category_delete),
                status=status.HTTP_200_OK)
        except Category.DoesNotExist as e:
            logger.error("Error in Category Delete View:", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            logger.error("Error in Category Delete View:", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class CraptitudeListCreateView(APIView):
    permission_classes = [IsAdminUser]
    serializer_class = CraptitudeCreateSerializer

    def get(self, request):
        try:
            category = request.query_params.get('category', None)
            limit = request.query_params.get('limit', None)
            offset = request.query_params.get('offset', None)

            craptitudes = Craptitude.objects.filter(category_id=category).order_by('pk')
            total_craptitudes = craptitudes.count()

            if limit and offset:
                try:
                    limit = int(limit)
                    offset = int(offset)
                    craptitudes = craptitudes[offset:offset + limit]
                except ValueError as e:
                    raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)

            data = CraptitudeGetSerializer(craptitudes, context={"request": request}, many=True).data
            data = {
                'totalCraptitudes': total_craptitudes,
                'data': data
            }
            return Response(
                success_response(data, code=status.HTTP_200_OK, message=craptitude_list),
                status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("Error in Craptitude Get", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data, context={'request': request})
        serializer.is_valid(raise_exception=True)
        try:
            craptitude = serializer.save()
            data = CraptitudeGetSerializer(craptitude, context={"request": request}).data
            return Response(
                success_response(data=data, code=status.HTTP_200_OK, message=craptitude_create),
                status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("Error in Craptitude Create View", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class CraptitudeDetailView(APIView):
    permission_classes = [IsAdminUser]

    def put(self, request, pk):
        try:
            craptitude = Craptitude.objects.get(pk=pk)
            serializer = CraptitudeUpdateSerializer(craptitude, data=request.data, partial=True)
            serializer.is_valid(raise_exception=True)
            serializer.save()

            data = CraptitudeGetSerializer(craptitude, context={"request": request}).data
            return Response(
                success_response(data=data, code=status.HTTP_200_OK, message=craptitude_update),
                status=status.HTTP_200_OK)
        except Craptitude.DoesNotExist as e:
            logger.error("Error in Craptitude Patch View:", exc_info=True)
            raise CustomAPIException(detail=str(e),
                                     status_code=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            logger.error("Error in Craptitude Patch View:", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)
