import re
import json
import base64
import random

from django.conf import settings
from django.shortcuts import render
from django.db.models import Case, When, Value, BooleanField, F, Q
from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from api.apple import verify_apple_receipt, verify_apple_purchase
from api.googlepay import GooglePlayVerifier
from api.serializers import *
from core.base_views import *
from core.custom_exception import CustomAPIException
from core.email import send_support_email_async
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from datetime import datetime
from core.logger import logger


# Create your views here.


class DeviceRegisterView(CustomCreateAPIView):
    serializer_class = DeviceSerializer
    permission_classes = [AllowAny]
    pagination_class = None

    def get_queryset(self):
        return Device.objects.all()


class CategoryListView(CustomListAPIView):
    serializer_class = CategorySerializer
    permission_classes = [AllowAny]
    pagination_class = None

    def get_queryset(self):
        try:
            subscription = Transaction.objects.filter(device_id=self.kwargs['device']) \
                .filter(Q(expireAt__isnull=True) | Q(expireAt__gt=timezone.now())).order_by('-id').first()
            unlock_count = subscription.plan.unlockCategory
            return Category.objects.annotate(isUnlocked=Case(
                When(pk__lte=unlock_count, then=Value(True)),
                default=Value(False),
                output_field=BooleanField()))
        except Exception as e:
            print(e, '>>>>>>>>>>>>>>>>>>>>>>>>ERROR')
            logger.error("Error in CategoryListView", exc_info=True)
            return Category.objects.annotate(isUnlocked=Case(
                When(pk__lte=5, then=Value(True)),
                default=Value(False),
                output_field=BooleanField()))


class CraptitudeListView(CustomListAPIView):
    serializer_class = CraptitudeSerializer
    permission_classes = [AllowAny]
    pagination_class = None

    def get_queryset(self):
        category_id = self.kwargs['category']
        craptitude_history = CraptitudeHistory.objects.filter(session__device_id=self.kwargs['device'],
                                                              session__category_id=category_id).order_by('-id').first()

        if craptitude_history:
            unlock_level = craptitude_history.levelCompleted + 1  # next level unlocked
        else:
            unlock_level = 1

        return Craptitude.objects.filter(category_id=category_id).order_by('id').annotate(isUnlocked=Case(
            When(roundNumber__lte=unlock_level, then=Value(True)),
            default=Value(False),
            output_field=BooleanField()))


class TeamListView(CustomListAPIView):
    serializer_class = TeamSerializer
    permission_classes = [AllowAny]
    pagination_class = None

    def get_queryset(self):
        return Team.objects.all()


class GameSessionCreateView(CustomCreateAPIView):
    serializer_class = GameSessionSerializer
    permission_classes = [AllowAny]
    pagination_class = None

    def get_queryset(self):
        return GameSession.objects.all()


class PlayerBulkCreateAPIView(APIView):
    def post(self, request, session, **kwargs):
        serializer = PlayerCreateSerializer(data=request.data, many=True)  # accept list
        serializer.is_valid(raise_exception=True)
        try:
            if not GameSession.objects.filter(pk=session).exists():
                return Response(
                    fail_response(code=status.HTTP_404_NOT_FOUND, message="Please enter valid session id"),
                    status=status.HTTP_404_NOT_FOUND)

            # Delete existing players for that session
            Player.objects.filter(session_id=session).delete()

            players = serializer.save(session_id=session)  # <-- set session here
            data = PlayerCreateSerializer(players, many=True).data
            return Response(success_response(data, status.HTTP_200_OK, createdMessage), status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("Error in PlayerBulkCreateAPIView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class SentenceBulkCreateAPIView(APIView):
    def post(self, request, session, **kwargs):
        serializer = SentenceCreateSerializer(data=request.data, many=True)  # accept list
        serializer.is_valid(raise_exception=True)
        try:
            if not GameSession.objects.filter(pk=session).exists():
                return Response(
                    fail_response(code=status.HTTP_404_NOT_FOUND, message="Please enter valid session id"),
                    status=status.HTTP_404_NOT_FOUND)

            # Delete existing sentences for that session
            Sentence.objects.filter(session_id=session).delete()

            players = serializer.save(session_id=session)  # <-- set session here
            data = SentenceCreateSerializer(players, many=True).data
            return Response(success_response(data, status.HTTP_200_OK, createdMessage), status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("Error in SentenceBulkCreateAPIView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class SentenceShuffleAPIView(APIView):
    def get(self, request, session, *args, **kwargs):
        """
        Shuffle teams first, then shuffle players inside each team.
        """
        sentences = list(Sentence.objects.filter(session_id=session))

        if not sentences:
            return Response(
                fail_response(code=status.HTTP_404_NOT_FOUND, message="No sentences found for this session"),
                status=status.HTTP_404_NOT_FOUND)

        try:
            # --- STEP 1: group by team ---
            teams = {}
            for s in sentences:
                teams.setdefault(s.team_id, []).append(s)

            team_ids = list(teams.keys())

            # Shuffle team mapping (derangement so no team stays the same)
            shuffled_team_ids = team_ids[:]
            while True:
                random.shuffle(shuffled_team_ids)
                if any(a != b for a, b in zip(team_ids, shuffled_team_ids)):
                    break

            team_mapping = {old: new for old, new in zip(team_ids, shuffled_team_ids)}

            shuffled_output = []

            # --- STEP 2: shuffle players inside new teams ---
            with transaction.atomic():
                for old_team, players in teams.items():
                    new_team = team_mapping[old_team]

                    # collect all player IDs from this group
                    player_ids = [p.player_id for p in players]
                    random.shuffle(player_ids)

                    for idx, sentence in enumerate(players):
                        sentence.team_id = new_team
                        sentence.player_id = player_ids[idx]
                        sentence.save()

                        shuffled_output.append({
                            "id": sentence.id,
                            "text": sentence.text,
                            "player": sentence.player_id,
                            "team": sentence.team_id,
                            "session": sentence.session_id
                        })

            return Response(success_response(shuffled_output, status.HTTP_200_OK, getMessage),
                            status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("Error in SentenceShuffleAPIView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class RatingBulkCreateAPIView(APIView):
    def post(self, request, session, **kwargs):
        serializer = RatingCreateSerializer(data=request.data, many=True)  # accept list
        serializer.is_valid(raise_exception=True)

        session = GameSession.objects.filter(pk=session).first()

        if not session:
            return Response(fail_response(code=status.HTTP_404_NOT_FOUND, message="Please enter valid session id"),
                            status=status.HTTP_404_NOT_FOUND)
        try:
            # Delete existing rating for that session
            Rating.objects.filter(session_id=session).delete()

            with transaction.atomic():
                # Create sentence rating
                serializer.save(session_id=session.pk)  # <-- set session here

                # Calculate max score of sentence pair and return
                ratings = Rating.objects.filter(session=session).annotate(
                    totalScore=F('diaperScore') + F('dumpsterScore'))
                max_rating = ratings.order_by('-totalScore').first()

                # Create completed craptitude history
                print(session, '>>>>>>>>>>>>>>session')
                craptitude = Craptitude.objects.filter(pk=session.craptitude_id).first()
                completedLevel = craptitude.roundNumber
                instance, created = CraptitudeHistory.objects.get_or_create(session=session)
                print(instance.levelCompleted, '>>>>>>>>>>>levelCompleted')
                print(completedLevel, '>>>>>>>>>>>>>>completedLevel')
                if instance.levelCompleted < completedLevel:
                    print('>>>>>>>>>>>>>>>>>>>>>>>>>>>in')
                    instance.levelCompleted = completedLevel
                    instance.save(update_fields=['levelCompleted'])

                data = RatingGetSerializer(max_rating).data
                return Response(success_response(data, status.HTTP_200_OK, createdMessage),
                                status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("Error in RatingBulkCreateAPIView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class RatingScoreGetAPIView(CustomListAPIView):
    serializer_class = RatingGetSerializer
    permission_classes = [AllowAny]
    pagination_class = None

    def get_queryset(self):
        return Rating.objects.filter(session_id=self.kwargs['session'])


class OptionsAPIView(APIView):
    serializer_class = OptionsSerializer

    def post(self, request, session, *args, **kwargs):
        """
        Shuffle teams first, then shuffle players inside each team.
        """
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        option = serializer.data.get("option")
        print(session, '>>>>>>>>>>>>>>>>>>>>>>>>>>>>session')
        print(option, '>>>>>>>>>>>>>>>>>>>>>>>>>>>>option')
        try:
            data = {}
            if option == OptionChoice.PART_TWO:
                print('>>>>>>>>>>>>>>>>>>>>1')
                Rating.objects.filter(session_id=session).delete()

            if option == OptionChoice.PART_ONE_AND_PART_TWO:
                print('>>>>>>>>>>>>>>>>>>>>2')
                Sentence.objects.filter(session_id=session).delete()
                Rating.objects.filter(session_id=session).delete()

            if option == OptionChoice.CRAPTITUDE:
                print('>>>>>>>>>>>>>>>>>>>>3')
                game_session = GameSession.objects.filter(pk=session).first()
                data["category"] = game_session.category_id if game_session else None
                Player.objects.filter(session_id=session).delete()
                Sentence.objects.filter(session_id=session).delete()
                Rating.objects.filter(session_id=session).delete()

            if option == OptionChoice.CATEGORY:
                print('>>>>>>>>>>>>>>>>>>>>4')
                Player.objects.filter(session_id=session).delete()
                Sentence.objects.filter(session_id=session).delete()
                Rating.objects.filter(session_id=session).delete()

            print(data, '>>>>>>>>>>>>>>>>>>>>>data')
            return Response(success_response(data, status.HTTP_200_OK, getMessage), status=status.HTTP_200_OK)
        except Exception as e:
            logger.error("Error in OptionsAPIView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


def SupportView(request):
    return render(request, "support.html")


@csrf_exempt
def SupportCreateView(request):
    if request.method != "POST":
        return JsonResponse({"success": False, "message": "Invalid request method"}, status=405)
    try:
        print('>>>>>>>>>>>>>>>in')
        # Accept JSON or form-encoded
        if request.content_type and "application/json" in request.content_type:
            try:
                data = json.loads(request.body.decode("utf-8"))
            except Exception:
                return JsonResponse({"message": "Invalid JSON body"}, status=400)
        else:
            data = request.POST

        print(data, '>>>>>>data')
        name = data.get("name")
        country_code = data.get("countryCode")
        phone = data.get("phone")
        email = data.get("email")
        subject = data.get("subject")
        message = data.get("message")

        if not all([name, email, subject, message]):
            return JsonResponse({"success": False, "message": "All fields are required."},
                                status=status.HTTP_400_BAD_REQUEST)

        send_support_email_async(name, country_code, phone, email, subject, message)
        return JsonResponse({"success": True, "message": "Email sent successfully!"}, status=status.HTTP_200_OK)

    except Exception as e:
        print(e, '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>E')
        logger.error("Error in send support mail", exc_info=True)
        return JsonResponse({"success": False, "message": f"Email sending failed: {e}"},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)


def TermsAndConditionView(request):
    return render(request, "termsCondition.html")


def PrivacyPolicyView(request):
    return render(request, "privacyPolicy.html")


class PlanListView(CustomListAPIView):
    serializer_class = PlanSerializer
    permission_classes = [AllowAny]
    pagination_class = None

    def get_queryset(self):
        subscription = Transaction.objects.filter(device_id=self.kwargs['device']) \
            .filter(Q(expireAt__isnull=True) | Q(expireAt__gt=timezone.now())).order_by('-id').first()
        plan_id = subscription.plan_id
        print('>>>>>>>>>>>>>>>>>>>>plan')
        return Plan.objects.annotate(isActive=Case(
            When(pk=plan_id, then=Value(True)),
            default=Value(False),
            output_field=BooleanField()))


class AndroidPlanPurchaseView(APIView):
    """
    Django version of android_plan_purchase
    """
    serializer_class = AndroidPurchaseSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        data = serializer.validated_data

        try:
            logger.info(f"Android Plan Purchase: {data}")
            # Request data
            device = data.get("device")
            orderId = data.get("orderId")
            productId = data.get("productId")
            purchaseToken = data.get("purchaseToken")
            reqType = data.get("type")

            # Prevent duplicates
            if Transaction.objects.filter(orderId=orderId, device=device).exists():
                logger.info(f"Your plan is already active.")
                return Response(fail_response(code=status.HTTP_409_CONFLICT, message="Your plan is already active."),
                                status=status.HTTP_409_CONFLICT)

            # Verify subscription receipt with Google Play
            verifier = GooglePlayVerifier()
            sub = verifier.verify_subscription(productId, purchaseToken)
            if not sub or not sub.get("isSuccessful"):
                logger.info(f"Invalid orderId")
                return Response(fail_response(code=status.HTTP_400_BAD_REQUEST, message="Invalid orderId"),
                                status=status.HTTP_400_BAD_REQUEST)

            subscription = sub["payload"]
            logger.info(f"Subscription Data >>>>>>>>> {subscription}")

            startAt = datetime.fromisoformat(subscription.get("startTime").replace("Z", "+00:00"))
            expireAt = datetime.fromisoformat(subscription["lineItems"][0]["expiryTime"].replace("Z", "+00:00"))
            purchaseOrderId = re.sub(r"\.\..*", "", subscription.get("latestOrderId", ""))
            user_profile_id = subscription.get("obfuscatedExternalAccountId")
            logger.info(f"user_profile_id >>>>>>>>> {user_profile_id}")

            # Validate orderId (only for new purchase)
            if purchaseOrderId != orderId:
                logger.info(f"Invalid order Id.")
                return Response(fail_response(code=status.HTTP_400_BAD_REQUEST, message="Invalid order Id."),
                                status=status.HTTP_400_BAD_REQUEST)
            # user_id = user_profile_id or request.user_id

            # Get plan from DB
            plan = Plan.objects.filter(productId=productId).first()
            if not plan:
                logger.info(f"Invalid plan selected")
                return Response(fail_response(code=status.HTTP_400_BAD_REQUEST, message="Invalid plan selected"),
                                status=status.HTTP_400_BAD_REQUEST)

            # Handle Android transaction entry
            with transaction.atomic():
                android_txn, created = AndroidTransaction.objects.get_or_create(device=device,
                                                                                purchaseToken=purchaseToken,
                                                                                notificationType=4,
                                                                                defaults=dict(
                                                                                    productId=plan.productId,
                                                                                    orderId=purchaseOrderId,
                                                                                    startAt=startAt,
                                                                                    expireAt=expireAt))

                # Update user subscription
                device.isSubscription = True
                device.expireAt = expireAt
                device.lastPurchaseToken = purchaseToken
                device.productId = productId
                device.save(update_fields=["isSubscription", "expireAt", "lastPurchaseToken", "productId"])

                # Create user transaction record
                if reqType == "purchase":
                    Transaction.objects.create(device=device,
                                               plan=plan,
                                               productId=plan.productId,
                                               purchaseToken=purchaseToken,
                                               orderId=purchaseOrderId,
                                               notificationType="4",
                                               amount=plan.price,
                                               startAt=startAt,
                                               expireAt=expireAt)

            return Response(success_response({"device": device.pk}, code=status.HTTP_200_OK,
                                             message="Plan purchased successfully."), status=status.HTTP_200_OK)
        except Exception as e:
            logger.info(f"Error in Android PlanPurchase: {e}")
            logger.error("Error in Android PlanPurchase", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class ApplePlanPurchaseView(APIView):
    serializer_class = ApplePurchaseSerializer

    def post(self, request):
        try:
            serializer = self.serializer_class(data=request.data)
            serializer.is_valid(raise_exception=True)
            data = serializer.validated_data
            logger.info(f"Apple Plan Purchase: {data}")

            # Request data
            device = data.get("device")
            productId = data.get("productId")
            appleTransactionId = data.get("appleTransactionId")
            originalTransactionId = data.get("originalTransactionId")
            appleReceipt = data.get("appleReceipt", "")

            if appleReceipt:
                logger.info(f"If receive appleReceipt")
                try:
                    sub = verify_apple_receipt(appleReceipt)
                    subscription = sub["data"]

                    latest_info = subscription["data"]["latest_receipt_info"][0]
                    logger.info(f"Lastest receipt info: {latest_info}")
                    expires_date_str = latest_info["expires_date"]
                    expireAt = datetime.strptime(
                        expires_date_str.split(" ")[0] + " " + expires_date_str.split(" ")[1], "%Y-%m-%d %H:%M:%S")
                except Exception as e:
                    print(e, '>>>>>>>>>>>>Error in verify receipt')
                    logger.info(f"verifyAppleReceipt error: {str(e)}")
                    logger.error(f"verifyAppleReceipt error: {str(e)}")
                    return Response(
                        fail_response(code=status.HTTP_400_BAD_REQUEST, message="Receipt verification failed"),
                        status=status.HTTP_400_BAD_REQUEST)

            today = timezone.now()
            startAt = today
            expireAt = None

            # Get plan from DB
            plan = Plan.objects.filter(productId=productId).first()
            if not plan:
                logger.info(f"Invalid plan selected")
                return Response(fail_response(code=status.HTTP_404_NOT_FOUND, message="Invalid plan selected"),
                                status=status.HTTP_404_NOT_FOUND)

            print('>>>>>>>>>>>>>>>>>>>>>>>>1')
            device_data = DeviceGetSerializer(device).data
            existing_user = Device.objects.filter(originalTransactionId=originalTransactionId,
                                                  productId=productId).first()
            if existing_user and existing_user.pk != device.id:
                other = Device.objects.filter(pk=existing_user.pk)
                if not other:
                    logger.info(f"This subscription is already active on another user.")
                    return Response(success_response(data=device_data, code=status.HTTP_200_OK,
                                                     message="This subscription is already active on another user."),
                                    status=status.HTTP_200_OK)
            print('>>>>>>>>>>>>>>>>>>>>>>>>2')

            apple_txn = AppleTransaction.objects.filter(originalTransactionId=originalTransactionId,
                                                        productId=productId,
                                                        notificationType="EXPIRED").first()
            if apple_txn and apple_txn.device and apple_txn.expireAt and apple_txn.expireAt >= today:
                if device.id != apple_txn.device.id:
                    logger.info(f"This subscription is already active on another user.>>>>>>>>>>>>>")
                    return Response(success_response(data=device_data, code=status.HTTP_200_OK,
                                                     message="This subscription is already active on another user."),
                                    status=status.HTTP_200_OK)
                elif device == apple_txn.device and device.isSubscription:
                    logger.info(f"Your plan is already active.")
                    return Response(
                        fail_response(code=status.HTTP_409_CONFLICT, message="Your plan is already active."),
                        status=status.HTTP_409_CONFLICT)

            print('>>>>>>>>>>>>>>>>>>>>>>>>3')

            if expireAt is None and plan.period == PlanPeriod.YEAR:
                expireAt = startAt + timedelta(days=365)

            txn_data = {
                "notificationType": "",
                "appleTransactionId": appleTransactionId or "",
                "originalTransactionId": originalTransactionId or "",
                "webOrderLineItemId": "",
                "startAt": startAt,
                "expireAt": expireAt,
                "environment": "",
                "transactionReason": "",
                "productId": productId,
                "device": device,
            }

            check_txn = AppleTransaction.objects.filter(originalTransactionId=originalTransactionId,
                                                        productId=productId).first()
            if not check_txn:
                AppleTransaction.objects.create(**txn_data)
            else:
                if check_txn.expireAt >= today:
                    expireAt = check_txn.expireAt
                else:
                    check_txn.expireAt = expireAt
                    check_txn.save(update_fields=["expireAt"])

            # Update user subscription info
            device.productId = productId
            # device.appleTransactionId = appleTransactionId
            device.originalTransactionId = originalTransactionId
            device.isSubscription = True
            device.expireAt = expireAt
            device.save(update_fields=["productId", "originalTransactionId", "isSubscription", "expireAt"])

            Transaction.objects.create(device=device,
                                       plan=plan,
                                       productId=productId,
                                       appleTransactionId=appleTransactionId,
                                       originalTransactionId=originalTransactionId,
                                       amount=plan.price,
                                       startAt=startAt,
                                       expireAt=expireAt)

            return Response(
                success_response({"device": device.pk}, code=status.HTTP_200_OK, message="Plan purchased successfully"),
                status=status.HTTP_200_OK)
        except Exception as e:
            print(e, '>>>>>>>>>>>>>>>>>Error in apple ')
            logger.info(f"Error in ApplePlanPurchase: {e}")
            logger.error("Error in ApplePlanPurchase", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class AndroidPlanRestoreView(APIView):
    serializer_class = AndroidPlanRestoreSerializer

    def post(self, request, *args, **kwargs):
        """Restore Android subscription for a user based on the purchase token."""
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        validated_data = serializer.validated_data

        try:
            logger.info(f"Android Plan Restore: {validated_data}")
            device = validated_data["device"]
            purchaseToken = validated_data["purchaseToken"]
            productId = validated_data["productId"]

            today = timezone.now()

            if device.isSubscription and device.expireAt and device.expireAt >= today and device.lastPurchaseToken == purchaseToken:
                device_data = DeviceGetSerializer(device).data
                logger.info(f"Plan restored successfully>>>>>>>>>>>>>>>>if")
                return Response(success_response(data=device_data, code=status.HTTP_200_OK,
                                                 message="Plan restored successfully"), status=status.HTTP_200_OK)
            else:
                logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>first else")
                verifier = GooglePlayVerifier()
                sub = verifier.verify_subscription(productId, purchaseToken)
                if not sub or not sub.get("isSuccessful"):
                    logger.info(f"Invalid orderId")
                    return Response(fail_response(code=status.HTTP_400_BAD_REQUEST, message="Invalid orderId"),
                                    status=status.HTTP_400_BAD_REQUEST)

                subscription = sub["payload"]
                logger.info(f"verify_subscription data- {subscription}")

                startAt = datetime.fromisoformat(subscription.get("startTime").replace("Z", "+00:00"))
                expireAt = datetime.fromisoformat(subscription["lineItems"][0]["expiryTime"].replace("Z", "+00:00"))
                purchaseOrderId = re.sub(r"\.\..*", "", subscription.get("latestOrderId", ""))

                if expireAt <= today:
                    logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Expired")
                    device.isSubscription = False
                    device.expireAt = None
                    device.lastPurchaseToken = None
                    device.originalTransactionId = None
                    device.appleTransactionId = None
                    device.productId = None
                    device.save(
                        update_fields=["isSubscription", "expireAt", "lastPurchaseToken", "originalTransactionId",
                                       "appleTransactionId", "productId"])
                    return Response(fail_response(code=status.HTTP_400_BAD_REQUEST, message="Your plan is expired"),
                                    status=status.HTTP_400_BAD_REQUEST)

                plan = AndroidTransaction.objects.filter(purchaseToken=purchaseToken).first()
                if not plan:
                    logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Not plan")
                    AndroidTransaction.objects.create(device=device,
                                                      purchaseToken=purchaseToken,
                                                      notificationType=4,
                                                      productId=productId,
                                                      orderId=purchaseOrderId,
                                                      startAt=startAt,
                                                      expireAt=expireAt)

                    # Update user subscription
                    device.isSubscription = True
                    device.expireAt = expireAt
                    device.lastPurchaseToken = purchaseToken
                    device.productId = productId
                    device.save(update_fields=["isSubscription", "expireAt", "lastPurchaseToken", "productId"])
                    device_data = DeviceGetSerializer(device).data
                    return Response(success_response(data=device_data, code=status.HTTP_200_OK,
                                                     message="Plan restored successfully"), status=status.HTTP_200_OK)

                if device != plan.device:
                    other_device = Device.objects.filter(pk=plan.device_id).first()
                    if other_device:
                        logger.info(f"This subscription is already active on another user.")
                        return Response(fail_response(code=status.HTTP_400_BAD_REQUEST,
                                                      message="This subscription is already active on another user."),
                                        status=status.HTTP_400_BAD_REQUEST)

                AndroidTransaction.objects.filter(purchaseToken=purchaseToken).update(device=device)
                device.isSubscription = True
                device.productId = productId
                device.lastPurchaseToken = purchaseToken
                device.expireAt = expireAt
                device.save(update_fields=["isSubscription", "productId", "lastPurchaseToken", "expireAt"])

            device_data = DeviceGetSerializer(device).data
            return Response(success_response(data=device_data, code=status.HTTP_200_OK,
                                             message="Plan restored successfully"), status=status.HTTP_200_OK)
        except Exception as e:
            print(e, '>>>>>>>>>>>>>>>>>>error in android restore view')
            logger.info(f"Error in AndroidPlanRestorePurchase - {e}")
            logger.error("Error in AndroidPlanRestorePurchase", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class ApplePlanRestoreView(APIView):
    serializer_class = ApplePlanRestoreSerializer

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        data = serializer.validated_data

        try:
            logger.info(f"Apple Plan Restore: {data}")
            device = data.get("device")
            productId = data.get("productId")
            appleTransactionId = data.get("appleTransactionId")
            originalTransactionId = data.get("originalTransactionId")
            is_new_user = data.get("isNewUser", False)
            appleReceipt = data.get("appleReceipt", "")

            expireAt = None
            if appleReceipt:
                logger.info(f"If receive appleReceipt")
                try:
                    sub = verify_apple_receipt(appleReceipt)
                    subscription = sub["data"]

                    latest_info = subscription["data"]["latest_receipt_info"][0]
                    expires_date_str = latest_info["expires_date"]
                    expireAt = datetime.strptime(
                        expires_date_str.split(" ")[0] + " " + expires_date_str.split(" ")[1], "%Y-%m-%d %H:%M:%S")
                except Exception as e:
                    print(e, '>>>>>>>>>>>>Error in verify_apple_receipt')
                    logger.info(f"verifyAppleReceipt error: {str(e)}")
                    return Response(
                        fail_response(code=status.HTTP_400_BAD_REQUEST, message="Receipt verification failed"),
                        status=status.HTTP_400_BAD_REQUEST)

            today = timezone.now()
            if device.isSubscription and device.expireAt and device.expireAt >= today:
                logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>>>>>>first>>>>>>>>>>>if")
                if is_new_user and device.productId != productId:
                    logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>No subscription found for this transaction")
                    return Response(fail_response(code=327,
                                                  message="No subscription found for this transaction"),
                                    status=327)
                else:
                    logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Plan restored successfully")
                    device_data = DeviceGetSerializer(device).data
                    return Response(success_response(data=device_data, code=status.HTTP_200_OK,
                                                     message="Plan restored successfully"), status=status.HTTP_200_OK)

            # Fetch most recent transaction for this originalTransactionId
            apple_transaction = AppleTransaction.objects.filter(originalTransactionId=originalTransactionId,
                                                                productId=productId).order_by("-createdAt").first()
            if not apple_transaction:
                logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>not apple_transaction")
                device.isSubscription = False
                device.expireAt = None
                device.productId = None
                device.appleTransactionId = None
                device.originalTransactionId = None
                device.save(update_fields=["isSubscription", "expireAt", "productId", "appleTransactionId",
                                           "originalTransactionId"])

                if is_new_user:
                    logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Your plan is expired")
                    return Response(fail_response(code=327,message="Your plan is expired."), status=327)
                else:
                    logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>No subscription found for this transaction")
                    return Response(fail_response(code=status.HTTP_404_NOT_FOUND,
                                                  message="No subscription found for this transaction"),
                                    status=status.HTTP_404_NOT_FOUND)

            if apple_transaction.expireAt <= today:
                logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>apple_transaction expire")
                device.isSubscription = False
                device.expireAt = None
                device.productId = None
                device.appleTransactionId = None
                device.originalTransactionId = None
                device.save(update_fields=["isSubscription", "expireAt", "productId", "appleTransactionId",
                                           "originalTransactionId"])
                if is_new_user:
                    logger.info(">>>>>>>>>>>>>>>if is_new_user>>>>>>>>>>>>>>>>>Subscription is expired.")
                    return Response(fail_response(code=327, message="Subscription is expired."), status=327)
                else:
                    logger.info(">>>>>>>>>>>>>>>>else>>>>>>>>>>>>>>>>Subscription is expired.")
                    device_data = DeviceGetSerializer(device).data
                    return Response(success_response(data=device_data, code=status.HTTP_200_OK,
                                                     message="Subscription is expired."),
                                    status=status.HTTP_200_OK)

            if not expireAt:
                expireAt = apple_transaction.expireAt

            logger.info(f"Deviceid>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>{device.id}")
            plan_device_id = apple_transaction.device.pk if apple_transaction.device else None
            logger.info(f"plan_device_id>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>{plan_device_id}")
            if not plan_device_id or (plan_device_id and str(device.pk) == str(plan_device_id)):
                logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>if not")
                device.isSubscription = True
                device.productId = apple_transaction.productId
                device.appleTransactionId = appleTransactionId
                device.originalTransactionId = originalTransactionId
                device.expireAt = expireAt
                device.save(update_fields=[
                    "isSubscription", "productId", "appleTransactionId", "originalTransactionId", "expireAt"])

                AppleTransaction.objects.filter(originalTransactionId=originalTransactionId,
                                                productId=productId).update(device=device)
                device_data = DeviceGetSerializer(device).data
                return Response(success_response(data=device_data, code=status.HTTP_200_OK,
                                                 message="Plan restored successfully"), status=status.HTTP_200_OK)
            else:
                logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>>>>ELSE")
                other_device = Device.objects.filter(pk=plan_device_id).first()
                if other_device:
                    logger.info(f"This subscription is already active on another user.")
                    return Response(fail_response(code=status.HTTP_400_BAD_REQUEST,
                                                  message="This subscription is already active on another user."),
                                    status=status.HTTP_400_BAD_REQUEST)
                else:
                    device.isSubscription = True
                    device.productId = apple_transaction.productId
                    device.appleTransactionId = appleTransactionId
                    device.originalTransactionId = originalTransactionId
                    device.expireAt = apple_transaction.expireAt
                    device.save(update_fields=[
                        "isSubscription", "productId", "appleTransactionId", "originalTransactionId", "expireAt"])

                    AppleTransaction.objects.filter(originalTransactionId=originalTransactionId,
                                                    productId=productId).update(device=device)
                    device_data = DeviceGetSerializer(device).data
                    return Response(success_response(data=device_data, code=status.HTTP_200_OK,
                                                     message="Plan restored successfully"), status=status.HTTP_200_OK)
        except Exception as e:
            logger.info(f"Error in ApplePlanRestorePurchase - {e}")
            logger.error("Error in ApplePlanRestorePurchase", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)


class AndroidNotificationView(APIView):
    def post(self, request, *args, **kwargs):
        try:
            logger.info(f"Received Android Notification.>>>>>>>>>>>>")
            # Decode Base64 data from Google Play
            encoded_data = request.data.get("message", {}).get("data")
            if not encoded_data:
                logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>payload not found")
                return Response(fail_response(code=status.HTTP_400_BAD_REQUEST, message="Missing message data"),
                                status=status.HTTP_400_BAD_REQUEST)

            data = json.loads(base64.b64decode(encoded_data).decode("utf-8"))
            logger.info(f"Android Data>>>>>>>>>>>>>>>>>>> - {data}")

            subscription_data = data["subscriptionNotification"]
            productId = subscription_data["subscriptionId"]
            purchaseToken = subscription_data["purchaseToken"]
            notificationType = int(subscription_data["notificationType"])

            # Fetch subscription details from Google Play
            verifier = GooglePlayVerifier()
            sub = verifier.verify_subscription(productId, purchaseToken)
            if not sub or not sub.get("isSuccessful"):
                logger.info(f"Invalid order id")
                return Response(fail_response(code=status.HTTP_400_BAD_REQUEST, message="Invalid order id"),
                                status=status.HTTP_400_BAD_REQUEST)

            # Parse dates
            subscription = sub["payload"]
            logger.info(f"Subscription Data: {subscription}")
            startAt = datetime.fromisoformat(subscription.get("startTime").replace("Z", "+00:00"))
            expireAt = datetime.fromisoformat(subscription["lineItems"][0]["expiryTime"].replace("Z", "+00:00"))
            orderId = re.sub(r"\.\..*", "", subscription.get("latestOrderId", ""))
            # user_profile_id = subscription.get("externalAccountIdentifiers", {}).get(
            #     "obfuscatedExternalAccountId", "0")

            # Check if transaction exists
            check_txn = AndroidTransaction.objects.filter(purchaseToken=purchaseToken,
                                                          notificationType=4).order_by("-createdAt").first()
            logger.info(f"Android Transaction>>>>>>>>>>>>>>>>>>: {check_txn}")
            user_profile_id = subscription.get("obfuscatedExternalAccountId")
            device_id = None
            if not user_profile_id:
                logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>if not user_profile_id")
                if check_txn:
                    logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>if check_txn")
                    device_id = check_txn.device.id
                    logger.info(">>>>>>>>>>>>>device_id")
            else:
                device_id = user_profile_id

            logger.info(f"Notification Type {notificationType} processed for device {device_id}")

            # Get plan from DB
            plan = Plan.objects.filter(productId=productId).first()

            with transaction.atomic():
                new_txn = {
                    "device_id": device_id,
                    "productId": productId,
                    "orderId": orderId,
                    "purchaseToken": purchaseToken,
                    "notificationType": notificationType,
                    "startAt": startAt,
                    "expireAt": expireAt
                }

                logger.info(f"notificationType >>>>>>>>>>>>>>>>>>>>>>>>>> {notificationType}")

                # Handle notification types
                if notificationType == 4:
                    txn_data = {**new_txn, "plan": plan} # new purchase
                    Transaction.objects.create(**txn_data)
                    Device.objects.filter(id=device_id).update(isSubscription=True,
                                                               expireAt=expireAt,
                                                               lastPurchaseToken=purchaseToken,
                                                               productId=productId)

                    if not check_txn:
                        AndroidTransaction.objects.create(**new_txn)

                elif notificationType in [2, 3]: # renew or recover
                    txn_data = {**new_txn, "plan": plan} # new purchase
                    Transaction.objects.create(**txn_data)
                    device = Device.objects.filter(id=device_id).first()
                    if device and device.lastPurchaseToken == purchaseToken:
                        device.isSubscription = True
                        device.expireAt = expireAt
                        device.productId = productId
                        device.lastPurchaseToken = purchaseToken
                        device.save(update_fields=["isSubscription", "expireAt", "productId", "lastPurchaseToken"])
                        AndroidTransaction.objects.filter(purchaseToken=purchaseToken).update(expireAt=expireAt)

                elif notificationType == 13:  # expire
                    existing = Transaction.objects.filter(purchaseToken=purchaseToken,
                                                          notificationType=13,
                                                          device_id=device_id,
                                                          orderId=orderId).first()

                    if not existing:
                        device = Device.objects.filter(id=device_id).first()
                        if device and device.lastPurchaseToken == purchaseToken:
                            device.isSubscription = False
                            device.expireAt = None
                            device.productId = None
                            device.lastPurchaseToken = None
                            device.save(update_fields=["isSubscription", "expireAt", "productId", "lastPurchaseToken"])

            return Response(
                success_response([], code=status.HTTP_200_OK, message="Webhook handled successfully."),
                status=status.HTTP_200_OK)
        except Exception as e:
            logger.info("Error in Android Notification >>>>>>>>>>", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_200_OK)


def decode_base64_payload(payload: str) -> str:
    try:
        # Attempt normal decode
        try:
            return base64.urlsafe_b64decode(payload).decode("utf-8")
        except Exception:
            pass

        # Padding fix
        try:
            padded_payload = payload + "=" * (4 - len(payload) % 4)
            return base64.urlsafe_b64decode(padded_payload).decode("utf-8")
        except Exception:
            pass

        # Alternate padding fix
        fixed_payload = payload + "=" * ((4 - len(payload) % 4) % 4)
        return base64.urlsafe_b64decode(fixed_payload).decode("utf-8")

    except Exception as e:
        logger.error("decode_base64_payload", exc_info=True)
        raise ValueError(f"Decoding failed: {e}")


class AppleNotificationView(APIView):
    """
    Handle Apple App Store Server Notifications
    """

    def post(self, request, *args, **kwargs):
        try:
            logger.info(f"Received AppleNotification")
            signed_payload = request.data.get("signedPayload", "")
            if not signed_payload:
                logger.info(f">>>>>>>>>>>>>>>>>>>>>>>Missing signedPayload")
                return Response({"message": "Missing signedPayload"}, status=status.HTTP_400_BAD_REQUEST)

            # Step 1: Verify Apple-signed payload
            try:
                json_tinfo_payload = verify_apple_purchase(
                    signed_payload=signed_payload,
                    server_api={
                        "p8": "",
                        "keyId": settings.KEY_ID,
                        "issuerId": "",
                        "bundleId": settings.BUNDLE_ID,
                    },
                )
                if not json_tinfo_payload or not json_tinfo_payload.get("transaction"):
                    logger.info("Invalid Apple transaction payload")
                    return Response({"message": "Invalid order ID"}, status=status.HTTP_200_OK)
            except Exception as e:
                logger.info(f"verify_apple_purchase error - {e}")
                logger.error("verify_apple_purchase error", exc_info=True)
                return Response({"message": str(e)}, status=status.HTTP_200_OK)

            logger.info(f"Done done verification>>>>>>>>>>>>>>>>>>>>>>>>>{json_tinfo_payload}")

            transaction_data = json_tinfo_payload.get("transaction")
            originalTransactionId = transaction_data.get("originalTransactionId")
            appleTransactionId = transaction_data.get("transactionId")
            productId = transaction_data.get("productId")
            notificationType = json_tinfo_payload.get("notificationType", "")
            startAt = transaction_data.get("startAt")
            expireAt = transaction_data.get("expireAt")
            environment = json_tinfo_payload.get("environment", "")
            transaction_reason = transaction_data.get("transactionReason")
            web_order_line_item_id = transaction_data.get("webOrderLineItemId")

            with transaction.atomic():
                check_transaction = AppleTransaction.objects.filter(originalTransactionId=originalTransactionId,
                                                                    productId=productId).first()

                get_user_transaction = AppleTransaction.objects.filter(originalTransactionId=originalTransactionId,
                                                                       productId=productId,
                                                                       device__isnull=False).first()

                logger.info(f"AppleTransaction ---> {check_transaction}")
                logger.info(f"getUserTransaction ---> {get_user_transaction}")

                insert_data = {
                    "appleTransactionId": appleTransactionId,
                    "notificationType": notificationType,
                    "originalTransactionId": originalTransactionId,
                    "webOrderLineItemId": web_order_line_item_id,
                    "startAt": startAt,
                    "expireAt": expireAt,
                    "environment": environment,
                    "transactionReason": transaction_reason,
                    "productId": productId
                }

                if not check_transaction:
                    logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>>if check_transaction")
                    AppleTransaction.objects.create(**insert_data)
                else:
                    logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>>else")
                    device = get_user_transaction.device if get_user_transaction else None

                    Transaction.objects.filter(originalTransactionId=originalTransactionId,
                                               productId=productId).update(startAt=startAt, expireAt=expireAt)

                    if notificationType == "EXPIRED":
                        logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>>EXPIRED>>>>>>>>>>>>>>>if")
                        AppleTransaction.objects.filter(id=check_transaction.pk).update(**insert_data)
                        if device:
                            device.isSubscription = False
                            device.expireAt = None
                            device.productId = None
                            device.originalTransactionId = None
                            device.appleTransactionId = None
                            device.save(
                                update_fields=["isSubscription", "expireAt", "productId", "originalTransactionId",
                                               "appleTransactionId"])
                    else:
                        logger.info(f">>>>>>>>>>>>>>>>>>>>>>>>>EXPIRED>>>>>>>>>>>>>>>else")
                        AppleTransaction.objects.filter(id=check_transaction.pk).update(**insert_data)
                        if device:
                            device.isSubscription = True
                            device.expireAt = expireAt
                            device.productId = productId
                            device.originalTransactionId = originalTransactionId
                            device.appleTransactionId = appleTransactionId
                            device.save(
                                update_fields=["isSubscription", "expireAt", "productId", "originalTransactionId",
                                               "appleTransactionId"])

            return Response(
                success_response({}, code=status.HTTP_200_OK, message="Plan purchase successfully"),
                status=status.HTTP_200_OK)

        except Exception as e:
            logger.info(f"Error in AppleNotificationView - {e}")
            logger.error("Error in AppleNotificationView", exc_info=True)
            raise CustomAPIException(detail=str(e), status_code=status.HTTP_400_BAD_REQUEST)