from django.db import models
from django.db.models import Q, Sum
from uuid import uuid4
from django.db.models.signals import post_save
from django.dispatch import receiver
from decimal import Decimal
from django.db import transaction
from apps.level.models import Level
from django.utils import timezone 

class Profile(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
    user = models.OneToOneField('users.User', on_delete=models.CASCADE, related_name='profile')
    balance = models.DecimalField(max_digits=12, decimal_places=4, default=0.0000)
    
    packages = models.ManyToManyField(
        'package.Package', 
        through='ProfilePackage', 
        related_name='profiles', 
        blank=True
    )
    
    super_refer_code = models.ForeignKey(
        'users.User', 
        on_delete=models.SET_NULL, 
        null=True, blank=True, 
        related_name='referred_profiles_set' 
    )
    is_active = models.BooleanField(default=True)
    is_referred = models.BooleanField(default=False) 
    
    def __str__(self):
        return f"{self.user.email}'s Profile"

    def get_total_package_price(self):
        """Calculate total spending on packages"""
        total = self.profilepackage_set.aggregate(
            total=Sum('package__price')
        )['total'] or Decimal('0.00')
        return total

    def distribute_commission(self, deposit_amount):         
        current_profile = self
        amount = Decimal(str(deposit_amount))
        print(f"DEBUG: Starting commission for {self.user.email}, Amount: {amount}")

        with transaction.atomic():
            for level_num in range(1, 16):
                upline_user = current_profile.super_refer_code
                
                if not upline_user:
                    print(f"DEBUG: No more upline found at level {level_num}. Breaking.")
                    break 

                try:
                    # select_for_update ব্যবহার করা হয়েছে ডাটা সেফটির জন্য
                    upline_profile = Profile.objects.select_for_update().get(user=upline_user)
                    
                    # ডিরেক্ট রেফারেল সংখ্যা চেক
                    direct_refer_count = Profile.objects.filter(super_refer_code=upline_user).count()
                    
                    # প্যাকেজ চেক
                    has_package = upline_profile.packages.exists()

                    print(f"DEBUG: Level {level_num} - Upline: {upline_user.email}, Refers: {direct_refer_count}, HasPackage: {has_package}")

                    # শর্ত চেক (আপনার শর্ত অনুযায়ী)
                    if has_package and direct_refer_count >= level_num:
                        try:
                            level_config = Level.objects.get(level_number=level_num)
                            percentage = Decimal(str(level_config.percentage))
                            
                            if percentage > 0:
                                commission_value = (amount * percentage) / Decimal('100.00')
                                upline_profile.balance += commission_value
                                upline_profile.save()
                                print(f"DEBUG: Bonus Added! {commission_value} to {upline_user.email}")
                        except Level.DoesNotExist:
                            print(f"DEBUG: Level {level_num} config not found in Database.")
                    else:
                        print(f"DEBUG: Conditions not met for {upline_user.email}. Bonus skipped.")

                    # পরবর্তী লেভেলের জন্য
                    current_profile = upline_profile

                except Profile.DoesNotExist:
                    print(f"DEBUG: Profile for {upline_user.email} not found.")
                    break

    def _format_user_data(self, profile, level):
        return {
            'level': level,
            'user_id': str(profile.user.id),
            'user_name': profile.user.name,
            'user_status': profile.user.is_active,
            'user_refer_code': profile.user.refer_code,
            'balance': float(profile.balance) if profile.balance else 0.0,
        }

    def get_downline(self, max_level=15):
        downline = []
        current_level_user_ids = [self.user.id]
        for level in range(1, max_level + 1):
            profiles = Profile.objects.filter(super_refer_code_id__in=current_level_user_ids).select_related('user')
            if not profiles.exists(): break
            level_data = [self._format_user_data(p, level) for p in profiles]
            downline.extend(level_data)
            current_level_user_ids = [p.user_id for p in profiles]
        return downline

    def get_level_downline(self, target_level, search_term=None):
        if target_level < 1: return []
        current_level_user_ids = [self.user.id]
        for level in range(1, target_level + 1):
            profiles = Profile.objects.filter(super_refer_code_id__in=current_level_user_ids).select_related('user')
            if not profiles.exists(): return []
            if level == target_level:
                if search_term:
                    profiles = profiles.filter(Q(user__name__icontains=search_term) | Q(user__email__icontains=search_term))
                return [self._format_user_data(p, level) for p in profiles]
            current_level_user_ids = [p.user_id for p in profiles]
        return []

    def get_downline_stats(self, max_level=15):
        stats = []
        current_level_user_ids = [self.user.id]
        visited_user_ids = {self.user.id} 
        for level in range(1, max_level + 1):
            level_profiles = Profile.objects.filter(super_refer_code_id__in=current_level_user_ids).exclude(user_id__in=visited_user_ids) 
            count = level_profiles.count()
            stats.append({"level": f"LVL-{level}", "count": count})
            if count == 0: break
            new_ids = list(level_profiles.values_list('user_id', flat=True))
            current_level_user_ids = new_ids
            visited_user_ids.update(new_ids) 
        return stats

    def get_downline_earning_stats(self, max_level=15):
        earning_stats = []
        current_level_user_ids = [self.user.id]
        total_downline_earnings = Decimal('0.00')
        for level_num in range(1, max_level + 1):
            profiles = Profile.objects.filter(super_refer_code_id__in=current_level_user_ids)
            if not profiles.exists(): break
            income_res = profiles.aggregate(total=Sum('balance'))['total']
            level_members_income = Decimal(str(income_res)) if income_res else Decimal('0.00')
            try:
                level_config = Level.objects.get(level_number=level_num)
                commission_pct = Decimal(str(level_config.percentage))
            except Level.DoesNotExist:
                commission_pct = Decimal('0.00')
            your_earning_from_level = (level_members_income * commission_pct) / Decimal('100.00')
            total_downline_earnings += your_earning_from_level
            earning_stats.append({
                "level": f"LVL-{level_num}",
                "member_count": profiles.count(),
                "members_total_income": float(level_members_income),
                "your_percentage": float(commission_pct),
                "your_earning": float(your_earning_from_level)
            })
            current_level_user_ids = list(profiles.values_list('user_id', flat=True))
        return {"total_earning": float(total_downline_earnings), "breakdown": earning_stats}


# --- Signals ---
@receiver(post_save, sender='users.User')
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        try:
            Profile.objects.get_or_create(user=instance)
        except Exception as e:
            print(f"Error creating profile for {instance.email}: {e}")


class ProfilePackage(models.Model):
    profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
    package = models.ForeignKey('package.Package', on_delete=models.CASCADE)
    subscribed_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField()

    class Meta:
        unique_together = ('profile', 'package')

    def is_expired(self):
        return timezone.now() > self.expires_at


class RankCategory(models.Model):
    """Fixed ranks like Diamond, Silver, Gold for individual ranking"""
    CATEGORY_TYPES = [
        ('diamond', 'Diamond'),
        ('platinum', 'Platinum'),
        ('gold', 'Gold'),
        ('silver', 'Silver'),
        ('bronze', 'Bronze'),
    ]
    
    name = models.CharField(max_length=50, choices=CATEGORY_TYPES, unique=True)
    min_spending = models.DecimalField(max_digits=12, decimal_places=2, default=0.00)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        ordering = ['min_spending']  # min_spending অনুযায়ী সাজানো
        verbose_name_plural = "Rank Categories"
    
    def __str__(self):
        return self.name


class ProfileRank(models.Model):
    profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='profile_ranks')
    rank_category = models.ForeignKey(RankCategory, on_delete=models.CASCADE, related_name='profile_ranks')
    claimed_at = models.DateTimeField(auto_now_add=True)
    is_active = models.BooleanField(default=True)
    category_rank = models.IntegerField(null=True, blank=True) 
    
    class Meta:
        ordering = ['-claimed_at']
        unique_together = ['profile', 'rank_category']  
        verbose_name_plural = "Profile Ranks"
    
    def __str__(self):
        return f"{self.profile.user.email} - {self.rank_category.name}"
    

class GlobalRank(models.Model):
    profile = models.OneToOneField(Profile, on_delete=models.CASCADE, related_name='global_rank')
    total_spending = models.DecimalField(max_digits=15, decimal_places=2, default=0)
    global_rank = models.IntegerField(null=True, blank=True)
    
    class Meta:
        ordering = ['global_rank']
        verbose_name_plural = "Global Spending Ranks"
    
    def __str__(self):
        return f"{self.profile.user.email} - Rank: {self.global_rank}"


class SubscriptionHistory(models.Model):
    """Track user package purchases"""
    user = models.ForeignKey('users.User', on_delete=models.CASCADE, related_name='subscription_history')
    package = models.ForeignKey('package.Package', on_delete=models.SET_NULL, null=True)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    purchased_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField(null=True, blank=True)
    
    class Meta:
        ordering = ['-purchased_at']
        verbose_name_plural = "Subscription Histories"
    
    def __str__(self):
        return f"{self.user.email} - {self.package.name if self.package else 'Unknown'} - ${self.amount}"