from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from apps.core.views.base import BaseViewSet
from django.db import transaction
from django.contrib.auth import get_user_model
from .models import Profile, RankCategory, GlobalRank, ProfileRank
from django.db.models import Window, F
from .serializers import (
    ProfileSerializer, 
    BalanceUpdateSerializer, 
    ClaimReferralSerializer,
    GlobalRankSerializer,
    ProfileRankSerializer,
    RankCategorySerializer,
    ClaimRankSerializer  
)
from rest_framework.permissions import IsAuthenticated
from django.db import models
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models.functions import DenseRank, RowNumber

User = get_user_model()


class ProfileViewSet(BaseViewSet):
    queryset = Profile.objects.select_related('user', 'super_refer_code').prefetch_related('packages')

    permission_app_name = 'profile'
    permission_model_name = 'profile'
    
    view_permissions = [
        "list",                       
        "retrieve",                    
        "update",                      
        "toggle_status",               
        "destroy",                     
        "update_balance",                
        "partial_update",               
        "claim_referral",                
        "me",                           
        "user_details",                 
        "payment_create",                
        "payment_verify",                
        "claim_rank",                    
        "global_rank",                     
        "rank_categories",                  
        "category_ranking",                 
        "rank_category_detail",
        "rank_progress"                
    ]
    
    search_fields = ['user__name', 'user__email']

    def get_queryset(self):
        user = self.request.user
        queryset = super().get_queryset().exclude(user__is_superuser=True)
        # Show only own profile if not Admin or Superuser
        if not (user.is_superuser or getattr(user, 'role', None) == 'admin'):
            return queryset.filter(user=user)
        return queryset

    def get_serializer_class(self):
        if self.action == 'update_balance':
            return BalanceUpdateSerializer
        if self.action == 'claim_referral':
            return ClaimReferralSerializer
        if self.action == 'rank_categories':
            return RankCategorySerializer
        if self.action == 'rank_category_detail':
            return RankCategorySerializer
        if self.action == 'global_rank':
            return GlobalRankSerializer
        if self.action == 'claim_rank':
            return ClaimRankSerializer
        return ProfileSerializer

    @action(detail=False, methods=['get'], url_path='me')
    def me(self, request):
        """নিজের প্রোফাইল দেখার জন্য"""
        try:
            profile = Profile.objects.select_related('user', 'super_refer_code').get(user=request.user)
            serializer = self.get_serializer(profile)
            return Response({
                "success": True,
                "message": "My profile retrieved successfully",
                "data": serializer.data
            }, status=status.HTTP_200_OK)
        except Profile.DoesNotExist:
            return Response({
                "success": False, 
                "message": "Profile not found", 
                "error_code": "404"
            }, status=status.HTTP_404_NOT_FOUND)

    @action(detail=False, methods=['get', 'post'], url_path='claim_referral')
    def claim_referral(self, request):
        """রেফারেল কোড ক্লেইম করার জন্য"""
        if request.method == 'GET':
            return Response({
                "message": "Please submit referral code using POST method",
                "example": {"refer_code": "REFERENCE_CODE"}
            }, status=status.HTTP_200_OK)
        
        serializer = self.get_serializer(data=request.data)
        if not serializer.is_valid():
            return Response({
                "success": False,
                "message": "Validation failed",
                "errors": serializer.errors
            }, status=status.HTTP_400_BAD_REQUEST)

        refer_code = serializer.validated_data.get('refer_code')
        user = request.user

        try:
            with transaction.atomic():
                profile, _ = Profile.objects.get_or_create(user=user)
                
                if profile.is_referred:
                    return Response({
                        "success": False,
                        "message": "You have already claimed a referral code."
                    }, status=status.HTTP_400_BAD_REQUEST)

                try:
                    referrer = User.objects.get(refer_code=refer_code)
                except User.DoesNotExist:
                    return Response({
                        "success": False,
                        "message": "The referral code you entered is invalid."
                    }, status=status.HTTP_404_NOT_FOUND)

                if referrer == user:
                    return Response({
                        "success": False,
                        "message": "You cannot claim your own referral code."
                    }, status=status.HTTP_400_BAD_REQUEST)

                profile.super_refer_code = referrer
                profile.is_referred = True
                profile.save()

                return Response({
                    "success": True,
                    "message": f"Referral code claimed successfully! Referred by {referrer.name}.",
                    "data": {
                        "referrer_name": referrer.name
                    }
                }, status=status.HTTP_200_OK)
        except Exception as e:
            return self.handle_exception(e)

    @action(detail=True, methods=['get', 'patch'], url_path='update-balance')
    def update_balance(self, request, pk=None):
        """ব্যালেন্স আপডেট করার জন্য (শুধু অ্যাডমিন)"""
        if not (request.user.is_superuser or getattr(request.user, 'role', None) == 'admin'):
            return Response({
                "success": False, 
                "message": "Access denied", 
                "error_code": "403"
            }, status=status.HTTP_403_FORBIDDEN)
        
        profile = self.get_object()
        serializer = self.get_serializer(profile, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response({
                "success": True, 
                "message": "Balance updated successfully",
                "data": serializer.data
            }, status=status.HTTP_200_OK)
            
        return Response({
            "success": False, 
            "errors": serializer.errors
        }, status=status.HTTP_400_BAD_REQUEST)
    
    @action(detail=False, methods=['post'], url_path='claim_rank', permission_classes=[IsAuthenticated])
    def claim_rank(self, request):
        """বর্তমান ইউজারের জন্য র্যাঙ্ক ক্লেইম করার API"""
        try:
            profile = Profile.objects.get(user=request.user)
        except Profile.DoesNotExist:
            return Response({
                'success': False,
                'error': 'Profile not found'
            }, status=status.HTTP_404_NOT_FOUND)
        
        claim_serializer = ClaimRankSerializer(data=request.data)
        
        if not claim_serializer.is_valid():
            return Response({
                'success': False,
                'errors': claim_serializer.errors
            }, status=status.HTTP_400_BAD_REQUEST)
        
        rank_category_id = claim_serializer.validated_data.get('rank_category_id')
        
        try:
            rank_category = RankCategory.objects.get(id=rank_category_id)
        except RankCategory.DoesNotExist:
            return Response({
                'success': False,
                'error': 'Rank category not found'
            }, status=status.HTTP_404_NOT_FOUND)
        
        # স্পেন্ডিং চেক করুন
        total_spending = profile.get_total_package_price()
        if total_spending < rank_category.min_spending:
            return Response({
                'success': False,
                'error': f'You need ${rank_category.min_spending} total spending to claim this rank',
                'current_spending': float(total_spending),
                'needed': float(rank_category.min_spending - total_spending)
            }, status=status.HTTP_400_BAD_REQUEST)
        
        # চেক করুন ইউজার ইতিমধ্যে এই র্যাঙ্ক নিয়েছে কিনা
        if ProfileRank.objects.filter(profile=profile, rank_category=rank_category).exists():
            return Response({
                'success': False,
                'error': 'You already have this rank'
            }, status=status.HTTP_400_BAD_REQUEST)
        
        with transaction.atomic():
            # এই ক্যাটাগরিতে সর্বশেষ rank কত ছিল?
            last_rank = ProfileRank.objects.filter(
                rank_category=rank_category
            ).aggregate(max_rank=models.Max('category_rank'))
            
            next_rank = (last_rank['max_rank'] or 0) + 1
            
            # র্যাঙ্ক তৈরি করুন
            profile_rank = ProfileRank.objects.create(
                profile=profile,
                rank_category=rank_category,
                is_active=True,
                category_rank=next_rank
            )
            
            # গ্লোবাল র্যাঙ্ক আপডেট করুন
            self.update_global_rank(profile)
        
        return Response({
            'success': True,
            'message': f'Successfully claimed {rank_category.name} rank! You are #{next_rank} in {rank_category.name} category.',
            'data': ProfileRankSerializer(profile_rank).data
        }, status=status.HTTP_201_CREATED)
        
    def update_global_rank(self, profile):
        """গ্লোবাল র্যাঙ্কিং আপডেট করার ফাংশন"""
        total_spending = profile.get_total_package_price()
        
        # গ্লোবাল র্যাঙ্ক তৈরি বা আপডেট করুন
        global_rank, created = GlobalRank.objects.get_or_create(profile=profile)
        global_rank.total_spending = total_spending
        
        # র্যাঙ্ক পজিশন ক্যালকুলেট করুন
        higher_count = GlobalRank.objects.filter(
            total_spending__gt=total_spending
        ).count()
        global_rank.global_rank = higher_count + 1
        global_rank.save()
        
        # সবার র্যাঙ্ক আপডেট করুন
        all_ranks = GlobalRank.objects.all().order_by('-total_spending')
        for idx, rank in enumerate(all_ranks, 1):
            if rank.global_rank != idx:
                rank.global_rank = idx
                rank.save()
    

    @action(detail=False, methods=['get'], permission_classes=[IsAuthenticated])
    def global_rank(self, request):
        # Use GlobalRank model with proper related field lookups
        global_ranks = GlobalRank.objects.select_related(
            'profile', 
            'profile__user'
        ).prefetch_related(
            'profile__profile_ranks'  # Only prefetch to check existence
        ).order_by('-total_spending')
        
        results = []
        current_rank = 1
        
        for index, gr in enumerate(global_ranks):
            # Handle ties - same spending = same rank
            if index > 0 and gr.total_spending == global_ranks[index-1].total_spending:
                rank = results[-1]['global_rank']
            else:
                rank = current_rank
            
            # Access user data through profile
            profile = gr.profile
            user = profile.user if profile else None
            
            # Check if user has any active rank (NFT badge eligibility)
            nft_badge = False
            if profile:
                nft_badge = profile.profile_ranks.filter(is_active=True).exists()
            
            results.append({
                'id': gr.id,
                'profile_id': str(profile.id) if profile else None,
                'user_id': str(user.id) if user else None,
                'user_name': user.name if user else None,
                'user_email': user.email if user else None,
                'total_spending': float(gr.total_spending),
                'global_rank': rank,
                'nft_badge': nft_badge,  # true if has any active rank, false otherwise
            })
            
            current_rank += 1
        
        return Response({
            'success': True,
            'count': len(results),
            'results': results
        })
    
    @action(detail=False, methods=['get', 'post'], url_path='rank-categories')
    def rank_categories(self, request):
        if request.method == 'GET':
            ranks = RankCategory.objects.all().order_by('min_spending')
            serializer = RankCategorySerializer(ranks, many=True)
            return Response({
                'success': True,
                'data': serializer.data
            })
        
        elif request.method == 'POST':
            if not (request.user.is_superuser or getattr(request.user, 'role', None) == 'admin'):
                return Response({
                    'success': False,
                    'message': 'Only admins can create rank categories'
                }, status=status.HTTP_403_FORBIDDEN)
            
            serializer = RankCategorySerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response({
                    'success': True,
                    'message': 'Rank category created successfully',
                    'data': serializer.data
                }, status=status.HTTP_201_CREATED)
            return Response({
                'success': False,
                'errors': serializer.errors
            }, status=status.HTTP_400_BAD_REQUEST)
    
    @action(detail=False, methods=['get', 'put', 'patch', 'delete'], url_path='rank-categories/(?P<category_id>[^/.]+)')
    def rank_category_detail(self, request, category_id=None):
        """নির্দিষ্ট র্যাঙ্ক ক্যাটাগরি দেখার, আপডেট এবং ডিলেট করার জন্য"""
        try:
            rank_category = RankCategory.objects.get(id=category_id)
        except RankCategory.DoesNotExist:
            return Response({
                'success': False,
                'error': 'Rank category not found'
            }, status=status.HTTP_404_NOT_FOUND)
        
        # GET - সবাই দেখতে পারে
        if request.method == 'GET':
            serializer = RankCategorySerializer(rank_category)
            return Response({
                'success': True,
                'data': serializer.data
            })
        
        # PUT, PATCH, DELETE - শুধু অ্যাডমিন
        if not (request.user.is_superuser or getattr(request.user, 'role', None) == 'admin'):
            return Response({
                'success': False,
                'message': 'Only admins can modify rank categories'
            }, status=status.HTTP_403_FORBIDDEN)
        
        # DELETE
        if request.method == 'DELETE':
            if ProfileRank.objects.filter(rank_category=rank_category).exists():
                return Response({
                    'success': False,
                    'message': 'Cannot delete category that has been claimed by users'
                }, status=status.HTTP_400_BAD_REQUEST)
            
            rank_category.delete()
            return Response({
                'success': True,
                'message': 'Rank category deleted successfully'
            }, status=status.HTTP_204_NO_CONTENT)
        
        # PUT (সম্পূর্ণ আপডেট) অথবা PATCH (আংশিক আপডেট)
        partial = request.method == 'PATCH'
        serializer = RankCategorySerializer(rank_category, data=request.data, partial=partial)
        
        if serializer.is_valid():
            serializer.save()
            return Response({
                'success': True,
                'message': 'Rank category updated successfully',
                'data': serializer.data
            }, status=status.HTTP_200_OK)
        
        return Response({
            'success': False,
            'errors': serializer.errors
        }, status=status.HTTP_400_BAD_REQUEST)
    
    @action(detail=False, methods=['get'], url_path='category-ranking', permission_classes=[IsAuthenticated])
    def category_ranking(self, request):
        """ক্যাটাগরি অনুযায়ী rank দেখুন (প্যাজিনেশন সহ)"""
        category_id = request.query_params.get('category_id')
        
        # Pagination parameters
        page = int(request.query_params.get('page', 1))
        page_size = int(request.query_params.get('page_size', 10))
        
        if category_id:
            try:
                category = RankCategory.objects.get(id=category_id)
            except RankCategory.DoesNotExist:
                return Response({
                    'success': False,
                    'error': 'Category not found'
                }, status=status.HTTP_404_NOT_FOUND)
            
            # Base queryset
            ranks_queryset = ProfileRank.objects.filter(
                rank_category=category,
                is_active=True
            ).select_related('profile__user').order_by('category_rank')
            
            # Get total count
            total_claimants = ranks_queryset.count()
            
            # Apply pagination
            paginator = Paginator(ranks_queryset, page_size)
            
            try:
                paginated_ranks = paginator.page(page)
            except PageNotAnInteger:
                paginated_ranks = paginator.page(1)
                page = 1
            except EmptyPage:
                paginated_ranks = paginator.page(paginator.num_pages)
                page = paginator.num_pages
            
            # Prepare ranking data
            ranking_data = []
            for rank in paginated_ranks:
                ranking_data.append({
                    'rank_position': rank.category_rank,
                    'user_name': rank.profile.user.name,
                    'user_email': rank.profile.user.email,
                    'claimed_at': rank.claimed_at
                })
            
            # Prepare pagination response
            return Response({
                'success': True,
                'category': category.name,
                'total_claimants': total_claimants,
                'ranking': ranking_data,
                'page': page,
                'page_size': page_size,
                'total_pages': paginator.num_pages,
                'has_next': paginated_ranks.has_next(),
                'has_previous': paginated_ranks.has_previous(),
                'next_page_number': paginated_ranks.next_page_number() if paginated_ranks.has_next() else None,
                'previous_page_number': paginated_ranks.previous_page_number() if paginated_ranks.has_previous() else None
            })
        
        else:
            # সব ক্যাটাগরি (টপ ১০)
            categories = RankCategory.objects.all().order_by('min_spending')
            result = []
            
            for category in categories:
                ranks = ProfileRank.objects.filter(
                    rank_category=category,
                    is_active=True
                ).select_related('profile__user').order_by('category_rank')[:10]  # Top 10
                
                ranking_data = []
                for rank in ranks:
                    ranking_data.append({
                        'rank_position': rank.category_rank,
                        'user_name': rank.profile.user.name,
                        'user_email': rank.profile.user.email,
                        'claimed_at': rank.claimed_at
                    })
                
                result.append({
                    'category_id': category.id,
                    'category_name': category.name,
                    'min_spending': str(category.min_spending),
                    'total_claimants': ProfileRank.objects.filter(
                        rank_category=category, 
                        is_active=True
                    ).count(),
                    'ranking': ranking_data
                })
            
            return Response({
                'success': True,
                'data': result
            })
    
    @action(detail=True, methods=['get'], permission_classes=[IsAuthenticated])
    def rank_progress(self, request, pk=None):
        """ইউজারের র্যাঙ্ক প্রোগ্রেস দেখার জন্য"""
        profile = self.get_object()
        total_spending = profile.get_total_package_price()
        
        all_ranks = RankCategory.objects.all().order_by('min_spending')
        
        # ইউজারের নেওয়া র্যাঙ্কগুলো
        claimed_ranks = ProfileRank.objects.filter(
            profile=profile
        ).values_list('rank_category_id', flat=True)
        
        progress_data = []
        for rank in all_ranks:
            is_claimed = rank.id in claimed_ranks
            progress = {
                'rank': RankCategorySerializer(rank).data,
                'is_claimed': is_claimed,
                'current_spending': float(total_spending),
                'meets_requirement': total_spending >= rank.min_spending,
                'can_claim': not is_claimed and total_spending >= rank.min_spending
            }
            
            if not is_claimed and total_spending < rank.min_spending:
                progress['needed'] = float(rank.min_spending - total_spending)
                progress['progress_percentage'] = (float(total_spending) / float(rank.min_spending)) * 100 if rank.min_spending > 0 else 0
            
            progress_data.append(progress)
        
        return Response({
            'success': True,
            'data': progress_data
        })