from django.db import transaction
from ..models import LuckyDip, LuckyDipEntry
from apps.business.wallet.services.wallet_service import WalletService
from django.utils import timezone
import random

def join_lucky_dip(user, lucky_dip_id):
    with transaction.atomic():
        lucky_dip = LuckyDip.objects.select_for_update().get(id=lucky_dip_id)
        if not lucky_dip.is_active or lucky_dip.current_members >= lucky_dip.max_members:
            raise Exception('Lucky Dip is not available for entry.')
        # Check per-user slot limit
        user_entry_count = LuckyDipEntry.objects.filter(lucky_dip=lucky_dip, user=user).count()
        max_slots = lucky_dip.max_slots_per_user
        if user_entry_count >= max_slots:
            raise Exception(f'MAX_SLOTS_REACHED:{user_entry_count}:{max_slots}')
        # Use slot-indexed reference_id so each purchase gets a unique wallet ledger entry
        next_slot_number = user_entry_count + 1
        reference_id = f'LUCKY_DIP_{lucky_dip_id}_{user.id}_{next_slot_number}'
        from apps.business.wallet.models import WalletLedger
        # Try repurchase wallet first
        from django.core.exceptions import ValidationError
        from apps.masterdata.platform_setting import PlatformSetting
        try:
            WalletService.debit_repurchase_wallet(user, lucky_dip.entry_amount, source_type='LUCKY_DIP', reference_id=reference_id)
        except ValidationError as e:
            # If insufficient repurchase balance, try main wallet if global setting allows
            allow_main_wallet = PlatformSetting.get_bool("lucky_dip_allow_main_wallet", True)
            if allow_main_wallet:
                try:
                    WalletService.debit_main_wallet(user, lucky_dip.entry_amount, source_type='LUCKY_DIP', reference_id=reference_id)
                except ValidationError as e2:
                    raise Exception('Insufficient balance in both repurchase and main wallet.')
            else:
                raise Exception('Insufficient repurchase wallet balance and main wallet usage is disabled by admin.')
        # Entry number concurrency
        last_entry = LuckyDipEntry.objects.select_for_update().filter(lucky_dip=lucky_dip).order_by('-entry_number').first()
        next_entry_number = 1 if not last_entry else last_entry.entry_number + 1
        # Create entry
        LuckyDipEntry.objects.create(
            lucky_dip=lucky_dip,
            user=user,
            entry_number=next_entry_number
        )
        # Increment current_members
        lucky_dip.current_members += 1
        lucky_dip.save(update_fields=['current_members'])
        return next_entry_number

def run_lucky_dip_draw(lucky_dip_id):
    with transaction.atomic():
        lucky_dip = LuckyDip.objects.select_for_update().get(id=lucky_dip_id)
        if lucky_dip.is_drawn:
            raise Exception('Lucky Dip draw already executed.')
        if not lucky_dip.is_active or (lucky_dip.current_members < lucky_dip.max_members and (not lucky_dip.draw_date or timezone.now() < lucky_dip.draw_date)):
            raise Exception('Lucky Dip draw cannot be run yet.')
        entries = list(LuckyDipEntry.objects.select_for_update().filter(lucky_dip=lucky_dip))
        if not entries:
            raise Exception('No entries to draw from.')
        prizes = list(lucky_dip.prizes.all().order_by('position'))
        # Multi-prize allocation: no duplicate winners, correct number per prize
        winners = []
        available_entries = entries.copy()
        import random
        for prize in prizes:
            if len(available_entries) < prize.quantity:
                raise Exception('Not enough entries for all prizes.')
            selected = random.sample(available_entries, prize.quantity)
            for entry in selected:
                winners.append({'entry_id': entry.id, 'user_id': entry.user_id, 'prize_id': prize.id})
                available_entries.remove(entry)
        # Mark dip as drawn and inactive after draw
        lucky_dip.is_drawn = True
        lucky_dip.is_active = False
        lucky_dip.save(update_fields=['is_drawn', 'is_active'])
        return winners
