from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import AllowAny
from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth import get_user_model
from google.oauth2 import id_token
from google.auth.transport import requests as google_requests
import random
import string
import requests
from django.conf import settings
from django.db import transaction

# Serializer import
from apps.users.serializers.google_auth import GoogleAuthSerializer

User = get_user_model()

# Google-এর public signing certificate যাতে প্রতিটা লগইনে নতুন করে নেটওয়ার্ক কল না লাগে,
# তার জন্য প্রকৃত HTTP response caching (cachecontrol) ব্যবহার করা হচ্ছে — এটা Google-এর
# নিজস্ব সুপারিশকৃত পদ্ধতি। plain requests.Session() শুধু TCP connection পুনরায় ব্যবহার
# করে, কিন্তু response cache করে না — তাই আগের ফিক্সেও প্রতিটা লগইনে নেটওয়ার্ক কল হচ্ছিল।
# cachecontrol সার্টিফিকেট রেসপন্সের Cache-Control হেডার মেনে কয়েক ঘন্টার জন্য cache করে
# রাখবে, ফলে প্রথমবারের পর প্রতিটা লগইনে আর নেটওয়ার্ক কলই লাগবে না।
try:
    from cachecontrol import CacheControl
    _google_auth_session = CacheControl(requests.Session())
except ImportError:
    # cachecontrol ইনস্টল করা না থাকলে অন্তত plain session ব্যবহার হবে (caching ছাড়া)
    _google_auth_session = requests.Session()


class _TimedGoogleAuthRequest(google_requests.Request):
    """
    google-auth লাইব্রেরির ডিফল্ট টাইমআউট অনেক বেশি (~120 সেকেন্ড)। নেটওয়ার্ক স্লো/ব্লকড
    হলে এই একটা রিকোয়েস্টই worker process অনেকক্ষণ আটকে রাখতে পারে, যার প্রভাবে পুরো
    সাইট (অন্য সব রিকোয়েস্ট) স্লো/হ্যাং হয়ে যেতে পারে। তাই একটা ছোট (৮ সেকেন্ড) টাইমআউট
    জোর করে বেঁধে দেওয়া হচ্ছে, যাতে নেটওয়ার্ক সমস্যা হলেও দ্রুত ফেইল করে।
    """
    def __call__(self, url, method='GET', body=None, headers=None, timeout=8, **kwargs):
        return super().__call__(url, method=method, body=body, headers=headers, timeout=timeout, **kwargs)


_google_auth_request = _TimedGoogleAuthRequest(session=_google_auth_session)

class GoogleLoginView(APIView):
    permission_classes = [AllowAny]
    
    def post(self, request):
        token = request.data.get('access_token') or request.data.get('token')
        
        if not token:
            return Response({"success": False, "message": "Token is required"}, status=400)

        incoming_ref = request.query_params.get('ref')
        
        # Verify Google token
        user_info = self.verify_google_token(token)
        
        if not user_info:
            return Response({
                "success": False,
                "message": "Invalid Google token or expired. Please login again.",
                "error_code": "4010"
            }, status=status.HTTP_401_UNAUTHORIZED)
        
        try:
            with transaction.atomic():
                user, created = self.get_or_create_user(user_info, incoming_ref)
                
                refresh = RefreshToken.for_user(user)
                access = refresh.access_token
                
                # Custom claims
                access['email'] = user.email
                access['name'] = user.name
                access['role'] = user.role
                
                user.last_login = timezone.now()
                user.save()
                
                return Response({
                    "success": True,
                    "message": f"Welcome, {user.name}!" if created else f"Welcome back, {user.name}!",
                    "data": {
                        "refresh": str(refresh),
                        "access": str(access),
                        "user": self.get_user_data(user)
                    },
                    "timestamp": timezone.now().strftime("%d-%m-%Y %H:%M:%S")
                }, status=status.HTTP_200_OK)
                
        except Exception as e:
            return Response({
                "success": False,
                "message": str(e),
                "error_code": "5000"
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def verify_google_token(self, token):
        CLIENT_ID = getattr(settings, 'GOOGLE_CLIENT_ID', None)

        try:
            idinfo = id_token.verify_oauth2_token(
                token, _google_auth_request, CLIENT_ID
            )
            return {
                'email': idinfo.get('email'),
                'name': idinfo.get('name'),
                'sub': idinfo.get('sub')
            }
        except Exception as e:
            print(f"ID Token error: {e}")

            try:
                response = requests.get(
                    'https://www.googleapis.com/oauth2/v3/userinfo',
                    params={'access_token': token},
                    timeout=5  
                )
                if response.status_code == 200:
                    user_data = response.json()
                    return {
                        'email': user_data.get('email'),
                        'name': user_data.get('name'),
                        'sub': user_data.get('sub')
                    }
            except Exception as e:
                print(f"Access token fallback error: {e}")

        return None

    def get_or_create_user(self, user_info, incoming_ref=None):
        email = user_info.get('email').lower().strip()
        created = False
        
        user, created = User.objects.get_or_create(
            email=email,
            defaults={
                'name': user_info.get('name', 'Google User'),
                'role': 'user',
                'is_email_verified': True,
                'is_active': True,
                'refer_code': self.generate_unique_refer_code()
            }
        )

        if created:
            # পাসওয়ার্ড সেট করা যাতে ইউজারের লগইন সিকিউর থাকে
            user.set_unusable_password()
            from apps.core.permissions.role_permission import get_role_based_permissions
            user.permissions = get_role_based_permissions('user')
            user.save()

            # রেফারেল প্রসেসিং
            if incoming_ref:
                self.process_referral(user, incoming_ref)
        else:
            # এক্সিস্টিং ইউজার হলে তথ্য আপডেট
            user.is_active = True
            user.save()
        
        return user, created

    def generate_unique_refer_code(self):
        while True:
            chars = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
            code = f"REF{chars}"
            if not User.objects.filter(refer_code=code).exists():
                return code

    def process_referral(self, new_user, ref_code):
        try:
            referrer = User.objects.get(refer_code=ref_code)
            if referrer.id != new_user.id:
                from apps.profiles.models import Profile
                profile, _ = Profile.objects.get_or_create(user=new_user)
                profile.super_refer_code = referrer
                profile.is_referred = True
                profile.save()
        except User.DoesNotExist:
            print(f"Referral code {ref_code} not found.")

    def get_user_data(self, user):
        return {
            "id": str(user.id),
            "name": user.name,
            "email": user.email,
            "role": user.role,
            "refer_code": user.refer_code,
            "is_active": user.is_active,
            "balance": getattr(user, 'balance', 0), 
        }