from decimal import Decimal
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.db.models import Sum, Q
from rest_framework import generics, permissions, status
from rest_framework.views import APIView
from rest_framework.response import Response

from .models import (
    Restaurant, LoyaltyProgram, LoyaltyRule,
    CustomerWallet, CoinTransaction, Booking,
    LoyaltySubscription, WalletTopup,
    KhataAccount, KhataTransaction, LoyaltyGroup, LoyaltyGroupMember,
    DeadHourDrop,
)
from .loyalty_serializers import (
    LoyaltyProgramSerializer, LoyaltyProgramSetupSerializer,
    LoyaltyRuleSerializer, CustomerWalletSerializer,
    CoinTransactionSerializer, RedemptionRequestSerializer,
    RedemptionResponseSerializer, ReferralApplySerializer,
    LoyaltyAnalyticsSerializer,
    # Phase 2
    LoyaltySubscriptionSerializer, SubscriptionCreateSerializer,
    WalletTopupSerializer, TopupRequestSerializer,
    # Phase 3
    KhataAccountSerializer, KhataUseLifelineSerializer, KhataClearSerializer,
    LoyaltyGroupSerializer, GroupCreateSerializer, JoinGroupSerializer, VaultRedeemSerializer,
    # Phase 4
    DeadHourDropSerializer, DeadHourDropCreateSerializer, FullAnalyticsSerializer,
)
from .loyalty_services import (
    get_or_create_wallet, WalletService,
    IronLawsEnforcer, IronLawsError,
    SubscriptionService, WalletTopupService,
    KhataService, VaultService,
)
from .permissions import IsRestaurantOwner, IsRestaurantOwnerOrAdmin
from django.http import Http404
from .models import AaharConfig


def _get_owner_restaurant(restaurant_id, user):
    """
    Shared helper: fetch a restaurant that the user is authorised to manage.
    Access is granted if:
      1. The user is staff/admin, OR
      2. The user is the direct ``owner`` of the restaurant, OR
      3. The user is a RESTAURANT_OWNER / HOTEL_OWNER and the restaurant
         is linked via AaharConfig (covers Aahar-synced restaurants whose
         owner field was never set).

    When case 3 matches and the restaurant has no owner yet, we auto-assign
    the current user so future checks are fast.
    """
    restaurant = get_object_or_404(Restaurant, pk=restaurant_id)

    # Staff / admin — always allowed
    if user.is_staff:
        return restaurant

    # Direct owner match
    if restaurant.owner == user:
        return restaurant

    # Aahar-linked ownership check: if the user is a restaurant owner and
    # this restaurant has an AaharConfig, grant access (the restaurant was
    # synced from Aahar but owner wasn't assigned correctly).
    if hasattr(user, 'role') and user.role in ('RESTAURANT_OWNER', 'HOTEL_OWNER'):
        user_owns_any = Restaurant.objects.filter(owner=user).exists()
        has_aahar_link = AaharConfig.objects.filter(
            restaurant_id=restaurant_id,
        ).exists()

        if has_aahar_link and (user_owns_any or restaurant.owner is None):
            # Auto-assign ownership so this works seamlessly next time.
            if restaurant.owner is None:
                restaurant.owner = user
                restaurant.save(update_fields=['owner'])
            return restaurant

    # Fallback: deny access with a 404 (same behaviour as before).
    raise Http404


# ──────────────────────────────────────────────
# Owner Views
# ──────────────────────────────────────────────

class LoyaltyProgramSetupView(APIView):
    """
    GET  /restaurant/<id>/loyalty/  — fetch program config
    POST /restaurant/<id>/loyalty/  — create program
    PUT  /restaurant/<id>/loyalty/  — update program
    """
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def get(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)
        try:
            program = restaurant.loyalty_program
        except LoyaltyProgram.DoesNotExist:
            return Response({'detail': 'Loyalty program not set up yet.'}, status=404)
        return Response(LoyaltyProgramSerializer(program).data)

    def post(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)
        if hasattr(restaurant, 'loyalty_program'):
            return Response(
                {'detail': 'Program already exists. Use PUT to update.'},
                status=status.HTTP_400_BAD_REQUEST,
            )
        ser = LoyaltyProgramSetupSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        program = ser.save(restaurant=restaurant)
        return Response(LoyaltyProgramSerializer(program).data, status=status.HTTP_201_CREATED)

    def put(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)
        program = get_object_or_404(LoyaltyProgram, restaurant=restaurant)
        ser = LoyaltyProgramSetupSerializer(program, data=request.data, partial=True)
        ser.is_valid(raise_exception=True)
        ser.save()
        return Response(LoyaltyProgramSerializer(program).data)


class LoyaltyRuleListCreateView(APIView):
    """
    GET  /restaurant/<id>/loyalty/rules/  — list all rules
    POST /restaurant/<id>/loyalty/rules/  — add a rule
    """
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def _get_program(self, restaurant_id, user):
        restaurant = _get_owner_restaurant(restaurant_id, user)
        return get_object_or_404(LoyaltyProgram, restaurant=restaurant)

    def get(self, request, restaurant_id):
        program = self._get_program(restaurant_id, request.user)
        return Response(LoyaltyRuleSerializer(program.rules.all(), many=True).data)

    def post(self, request, restaurant_id):
        program = self._get_program(restaurant_id, request.user)
        ser = LoyaltyRuleSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        rule = ser.save(program=program)
        return Response(LoyaltyRuleSerializer(rule).data, status=status.HTTP_201_CREATED)


class LoyaltyRuleDetailView(APIView):
    """
    GET/PUT/DELETE /restaurant/<id>/loyalty/rules/<rule_id>/
    """
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def _get_rule(self, restaurant_id, rule_id, user):
        restaurant = _get_owner_restaurant(restaurant_id, user)
        program = get_object_or_404(LoyaltyProgram, restaurant=restaurant)
        return get_object_or_404(LoyaltyRule, pk=rule_id, program=program)

    def get(self, request, restaurant_id, rule_id):
        rule = self._get_rule(restaurant_id, rule_id, request.user)
        return Response(LoyaltyRuleSerializer(rule).data)

    def put(self, request, restaurant_id, rule_id):
        rule = self._get_rule(restaurant_id, rule_id, request.user)
        ser = LoyaltyRuleSerializer(rule, data=request.data, partial=True)
        ser.is_valid(raise_exception=True)
        ser.save()
        return Response(LoyaltyRuleSerializer(rule).data)

    def delete(self, request, restaurant_id, rule_id):
        rule = self._get_rule(restaurant_id, rule_id, request.user)
        rule.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


class LoyaltyAnalyticsView(APIView):
    """GET /restaurant/<id>/loyalty/analytics/"""
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def get(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)

        wallets = CustomerWallet.objects.filter(restaurant=restaurant)
        since_30d = timezone.now() - timezone.timedelta(days=30)

        earned = CoinTransaction.objects.filter(
            wallet__restaurant=restaurant,
            coins__gt=0,
        ).aggregate(total=Sum('coins'))['total'] or Decimal('0')

        redeemed = CoinTransaction.objects.filter(
            wallet__restaurant=restaurant,
            transaction_type='REDEEMED',
        ).aggregate(total=Sum('coins'))['total'] or Decimal('0')

        redemption_rate = 0.0
        if earned > 0:
            redemption_rate = round(float(abs(redeemed) / earned * 100), 1)

        cutoff_48h = timezone.now() + timezone.timedelta(hours=48)
        expiring_48h = CoinTransaction.objects.filter(
            wallet__restaurant=restaurant,
            is_expired=False,
            expires_at__isnull=False,
            expires_at__lte=cutoff_48h,
            coins__gt=0,
        ).aggregate(total=Sum('coins'))['total'] or Decimal('0')

        data = {
            'total_wallets': wallets.count(),
            'active_wallets_30d': wallets.filter(updated_at__gte=since_30d).count(),
            'coins_issued_total': earned,
            'coins_redeemed_total': abs(redeemed),
            'redemption_rate_percent': redemption_rate,
            'summit_vips': wallets.filter(is_summit_vip=True).count(),
            'coins_expiring_48h': expiring_48h,
        }
        return Response(LoyaltyAnalyticsSerializer(data).data)


# ──────────────────────────────────────────────
# Customer Views
# ──────────────────────────────────────────────

class CustomerLoyaltyProgramView(APIView):
    """GET /loyalty/<restaurant_id>/info/"""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        try:
            program = restaurant.loyalty_program
        except LoyaltyProgram.DoesNotExist:
            return Response({'detail': 'Loyalty program not set up yet.'}, status=404)
        return Response(LoyaltyProgramSerializer(program).data)


class MyWalletView(APIView):
    """GET /loyalty/<restaurant_id>/wallet/"""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        wallet = get_or_create_wallet(request.user, restaurant)
        WalletService.expire_stale_coins(wallet)
        wallet.refresh_from_db()
        return Response(CustomerWalletSerializer(wallet).data)


class MyAllWalletsView(generics.ListAPIView):
    """GET /loyalty/my-wallets/"""
    serializer_class = CustomerWalletSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        wallets = CustomerWallet.objects.filter(customer=self.request.user)
        for wallet in wallets:
            WalletService.expire_stale_coins(wallet)
        return wallets.select_related('restaurant').order_by('-coin_balance')


class CoinTransactionHistoryView(generics.ListAPIView):
    """GET /loyalty/<restaurant_id>/transactions/"""
    serializer_class = CoinTransactionSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        restaurant = get_object_or_404(
            Restaurant, pk=self.kwargs['restaurant_id'], is_active=True
        )
        wallet = get_or_create_wallet(self.request.user, restaurant)
        return wallet.transactions.order_by('-created_at')


class RedeemCoinsView(APIView):
    """POST /loyalty/<restaurant_id>/redeem/"""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        ser = RedemptionRequestSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        booking = get_object_or_404(
            Booking,
            pk=ser.validated_data['booking_id'],
            customer=request.user,
            restaurant=restaurant,
        )
        if booking.status not in ('PENDING', 'CONFIRMED'):
            return Response(
                {'detail': 'Cannot redeem coins for this booking status.'},
                status=status.HTTP_400_BAD_REQUEST,
            )

        wallet = get_or_create_wallet(request.user, restaurant)

        try:
            result = IronLawsEnforcer.validate_redemption(
                wallet=wallet,
                bill_amount=booking.total_amount,
                coins_requested=ser.validated_data['coins_to_redeem'],
            )
        except IronLawsError as e:
            return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)

        IronLawsEnforcer.apply_redemption(
            wallet=wallet,
            booking=booking,
            coins_to_redeem=result['coins_approved'],
        )

        # Reduce booking total
        booking.total_amount -= result['discount_amount']
        booking.save(update_fields=['total_amount'])

        wallet.refresh_from_db()
        return Response(RedemptionResponseSerializer({
            'coins_redeemed': result['coins_approved'],
            'discount_amount': result['discount_amount'],
            'new_balance': wallet.coin_balance,
            'message': (
                f"₹{result['discount_amount']:.2f} discount applied! "
                f"Remaining balance: {wallet.coin_balance} coins."
            ),
        }).data)


class ReferralCodeView(APIView):
    """GET /loyalty/referral-code/?restaurant_id=X"""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        restaurant_id = request.query_params.get('restaurant_id')
        if not restaurant_id:
            return Response(
                {'detail': 'restaurant_id query param required.'},
                status=status.HTTP_400_BAD_REQUEST,
            )
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        wallet = get_or_create_wallet(request.user, restaurant)
        return Response({
            'referral_code': wallet.referral_code,
            'restaurant': restaurant.name,
            'share_message': (
                f"Join {restaurant.name}'s loyalty program with my code "
                f"{wallet.referral_code} and earn bonus coins on your first order!"
            ),
        })


class ApplyReferralView(APIView):
    """POST /loyalty/referral/apply/"""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        ser = ReferralApplySerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        restaurant = ser.validated_data['restaurant']
        referrer_wallet = ser.validated_data['referrer_wallet']

        # Can't refer yourself
        if referrer_wallet.customer == request.user:
            return Response(
                {'detail': 'You cannot use your own referral code.'},
                status=status.HTTP_400_BAD_REQUEST,
            )

        wallet = get_or_create_wallet(request.user, restaurant)

        if wallet.referred_by:
            return Response(
                {'detail': 'You have already applied a referral code.'},
                status=status.HTTP_400_BAD_REQUEST,
            )
        if wallet.total_orders > 0:
            return Response(
                {'detail': 'Referral code must be applied before your first order.'},
                status=status.HTTP_400_BAD_REQUEST,
            )

        wallet.referred_by = referrer_wallet.customer
        wallet.save(update_fields=['referred_by'])

        return Response({
            'detail': (
                f"Referral code applied! "
                f"{referrer_wallet.customer.first_name or referrer_wallet.customer.username} "
                f"will earn bonus coins when you place your first order."
            )
        })


class SummitProgressView(APIView):
    """GET /loyalty/<restaurant_id>/summit/"""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        wallet = get_or_create_wallet(request.user, restaurant)

        target = Decimal('7000')
        max_vips = 100
        try:
            rule = restaurant.loyalty_program.rules.get(rule_type='SUMMIT', is_active=True)
            target = Decimal(str(rule.trigger_config.get('target_spend', 7000)))
            max_vips = rule.trigger_config.get('max_vips', 100)
        except (LoyaltyProgram.DoesNotExist, LoyaltyRule.DoesNotExist):
            pass

        current_vips = CustomerWallet.objects.filter(
            restaurant=restaurant, is_summit_vip=True
        ).count()
        progress_pct = min(100, float(wallet.lifetime_cash_spent / target * 100))

        return Response({
            'lifetime_cash_spent': str(wallet.lifetime_cash_spent),
            'target_spend': str(target),
            'progress_percent': round(progress_pct, 1),
            'is_summit_vip': wallet.is_summit_vip,
            'summit_vip_at': wallet.summit_vip_at,
            'current_vip_count': current_vips,
            'max_vips': max_vips,
            'spots_remaining': max(0, max_vips - current_vips),
        })


# ══════════════════════════════════════════════════════════════════
# Phase 2 — Subscription Views
# ══════════════════════════════════════════════════════════════════

class SubscribeView(APIView):
    """POST /loyalty/<restaurant_id>/subscribe/"""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        ser = SubscriptionCreateSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        rule = ser.validated_data['rule_id']
        wallet = get_or_create_wallet(request.user, restaurant)

        existing = SubscriptionService.get_active_subscription(wallet)
        if existing:
            return Response(
                {'detail': 'You already have an active subscription.'},
                status=status.HTTP_400_BAD_REQUEST,
            )

        sub = SubscriptionService.create_subscription(
            wallet, rule, ser.validated_data.get('payment_reference', '')
        )
        return Response(LoyaltySubscriptionSerializer(sub).data, status=status.HTTP_201_CREATED)


class MySubscriptionView(APIView):
    """GET /loyalty/<restaurant_id>/subscription/"""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        wallet = get_or_create_wallet(request.user, restaurant)
        sub = SubscriptionService.get_active_subscription(wallet)
        if not sub:
            return Response({'detail': 'No active subscription.', 'active': False})
        return Response({**LoyaltySubscriptionSerializer(sub).data, 'active': True})


class WalletTopupView(APIView):
    """POST /loyalty/<restaurant_id>/topup/"""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        ser = TopupRequestSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        wallet = get_or_create_wallet(request.user, restaurant)
        try:
            topup = WalletTopupService.process_topup(
                wallet=wallet,
                rule=ser.validated_data['rule'],
                cash_paid=ser.validated_data['cash_amount'],
                payment_ref=ser.validated_data.get('payment_reference', ''),
            )
        except ValueError as e:
            return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)

        wallet.refresh_from_db()
        return Response({
            **WalletTopupSerializer(topup).data,
            'new_balance': str(wallet.coin_balance),
        }, status=status.HTTP_201_CREATED)


class ActiveSubscriptionsView(APIView):
    """GET /restaurant/<id>/loyalty/subscriptions/ (owner)"""
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def get(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)
        today = timezone.now().date()
        subs = LoyaltySubscription.objects.filter(
            wallet__restaurant=restaurant,
            status='ACTIVE',
            valid_until__gte=today,
        ).select_related('wallet__customer')
        return Response(LoyaltySubscriptionSerializer(subs, many=True).data)


class TopupHistoryView(generics.ListAPIView):
    """GET /restaurant/<id>/loyalty/topups/ (owner)"""
    serializer_class = WalletTopupSerializer
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def get_queryset(self):
        restaurant = _get_owner_restaurant(self.kwargs['restaurant_id'], self.request.user)
        return WalletTopup.objects.filter(wallet__restaurant=restaurant).order_by('-created_at')


# ══════════════════════════════════════════════════════════════════
# Phase 3 — Khata Views
# ══════════════════════════════════════════════════════════════════

class KhataStatusView(APIView):
    """GET /loyalty/<restaurant_id>/khata/"""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        wallet = get_or_create_wallet(request.user, restaurant)
        khata = KhataService.get_or_create_khata(wallet)
        KhataService.check_overdue(wallet)
        khata.refresh_from_db()
        return Response(KhataAccountSerializer(khata).data)


class UseKhataLifelineView(APIView):
    """POST /loyalty/<restaurant_id>/khata/use/"""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        ser = KhataUseLifelineSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        booking = get_object_or_404(
            Booking,
            pk=ser.validated_data['booking_id'],
            customer=request.user,
            restaurant=restaurant,
        )
        wallet = get_or_create_wallet(request.user, restaurant)
        try:
            tx = KhataService.use_lifeline(wallet, booking)
        except ValueError as e:
            return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)

        khata = KhataService.get_or_create_khata(wallet)
        return Response({
            'detail': 'Lifeline used successfully.',
            'lifelines_remaining': khata.lifelines_remaining,
            'current_tab_amount': str(khata.current_tab_amount),
            'tab_due_date': str(khata.tab_due_date),
        })


class ClearKhataTabView(APIView):
    """POST /loyalty/<restaurant_id>/khata/clear/"""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        ser = KhataClearSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        wallet = get_or_create_wallet(request.user, restaurant)
        KhataService.clear_tab(wallet, ser.validated_data.get('payment_reference', ''))
        return Response({'detail': 'Tab cleared. Your Khata account is active again.'})


class KhataManagementView(APIView):
    """GET /restaurant/<id>/loyalty/khata/ — owner lists all khata accounts"""
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def get(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)
        accounts = KhataAccount.objects.filter(
            wallet__restaurant=restaurant
        ).select_related('wallet__customer').order_by('-current_tab_amount')
        return Response(KhataAccountSerializer(accounts, many=True).data)


# ══════════════════════════════════════════════════════════════════
# Phase 3 — Society Vault / Group Views
# ══════════════════════════════════════════════════════════════════

class GroupManagementView(APIView):
    """GET /restaurant/<id>/loyalty/groups/ — owner view, POST to create"""
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def get(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)
        groups = LoyaltyGroup.objects.filter(restaurant=restaurant)
        return Response(LoyaltyGroupSerializer(groups, many=True).data)

    def post(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)
        ser = GroupCreateSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        group = ser.save(restaurant=restaurant, admin=request.user)
        return Response(LoyaltyGroupSerializer(group).data, status=status.HTTP_201_CREATED)


class JoinGroupView(APIView):
    """POST /loyalty/group/join/"""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        ser = JoinGroupSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        group = get_object_or_404(LoyaltyGroup, pk=ser.validated_data['group_id'], is_active=True)
        restaurant = get_object_or_404(Restaurant, pk=ser.validated_data['restaurant_id'])
        wallet = get_or_create_wallet(request.user, restaurant)

        _, created = LoyaltyGroupMember.objects.get_or_create(group=group, wallet=wallet)
        if not created:
            return Response({'detail': 'Already a member of this group.'}, status=status.HTTP_400_BAD_REQUEST)
        return Response({'detail': f'Joined group "{group.group_name}" successfully.'})


class MyGroupView(APIView):
    """GET /loyalty/<restaurant_id>/group/"""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, restaurant_id):
        restaurant = get_object_or_404(Restaurant, pk=restaurant_id, is_active=True)
        wallet = get_or_create_wallet(request.user, restaurant)
        memberships = LoyaltyGroupMember.objects.filter(
            wallet=wallet, group__restaurant=restaurant
        ).select_related('group')
        groups = [m.group for m in memberships]
        return Response(LoyaltyGroupSerializer(groups, many=True).data)


class VaultRedeemView(APIView):
    """POST /restaurant/<id>/loyalty/groups/<group_id>/redeem/"""
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def post(self, request, restaurant_id, group_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)
        group = get_object_or_404(LoyaltyGroup, pk=group_id, restaurant=restaurant)
        ser = VaultRedeemSerializer(data=request.data)
        ser.is_valid(raise_exception=True)
        try:
            VaultService.redeem_vault(group, ser.validated_data['amount'], ser.validated_data.get('notes', ''))
        except ValueError as e:
            return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
        group.refresh_from_db()
        return Response({'detail': 'Vault redemption successful.', 'vault_balance': str(group.vault_balance)})


class SummitVIPListView(APIView):
    """GET /restaurant/<id>/loyalty/summit/vips/ — owner"""
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def get(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)
        vips = CustomerWallet.objects.filter(
            restaurant=restaurant, is_summit_vip=True
        ).select_related('customer').order_by('summit_vip_at')
        data = [
            {
                'username': w.customer.username,
                'name': w.customer.get_full_name() or w.customer.username,
                'lifetime_cash_spent': str(w.lifetime_cash_spent),
                'summit_vip_at': w.summit_vip_at,
            }
            for w in vips
        ]
        return Response(data)


# ══════════════════════════════════════════════════════════════════
# Phase 4 — Dead-Hour Drops + Full Analytics
# ══════════════════════════════════════════════════════════════════

class DeadHourDropCreateView(APIView):
    """POST /restaurant/<id>/loyalty/drop/"""
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def post(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)
        ser = DeadHourDropCreateSerializer(data=request.data)
        ser.is_valid(raise_exception=True)

        valid_until = timezone.now() + timezone.timedelta(hours=ser.validated_data['valid_hours'])
        drop = DeadHourDrop.objects.create(
            restaurant=restaurant,
            message=ser.validated_data['message'],
            discount_code=ser.validated_data.get('discount_code', ''),
            valid_until=valid_until,
            created_by=request.user,
        )

        # Queue async WhatsApp blast
        from .tasks import process_dead_hour_drop
        process_dead_hour_drop.delay(drop.id)

        return Response(DeadHourDropSerializer(drop).data, status=status.HTTP_201_CREATED)


class DeadHourDropHistoryView(generics.ListAPIView):
    """GET /restaurant/<id>/loyalty/drops/"""
    serializer_class = DeadHourDropSerializer
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwner]

    def get_queryset(self):
        restaurant = _get_owner_restaurant(self.kwargs['restaurant_id'], self.request.user)
        return DeadHourDrop.objects.filter(restaurant=restaurant)


class LoyaltyFullAnalyticsView(APIView):
    """GET /restaurant/<id>/loyalty/analytics/full/"""
    permission_classes = [permissions.IsAuthenticated, IsRestaurantOwnerOrAdmin]

    def get(self, request, restaurant_id):
        restaurant = _get_owner_restaurant(restaurant_id, request.user)

        wallets = CustomerWallet.objects.filter(restaurant=restaurant)
        since_30d = timezone.now() - timezone.timedelta(days=30)
        cutoff_24h = timezone.now() + timezone.timedelta(hours=24)

        earned = CoinTransaction.objects.filter(
            wallet__restaurant=restaurant, coins__gt=0
        ).aggregate(total=Sum('coins'))['total'] or Decimal('0')

        redeemed = CoinTransaction.objects.filter(
            wallet__restaurant=restaurant, transaction_type='REDEEMED'
        ).aggregate(total=Sum('coins'))['total'] or Decimal('0')

        redemption_rate = round(float(abs(redeemed) / earned * 100), 1) if earned > 0 else 0.0

        wallet_count = wallets.count()
        avg_coins = round(float(earned) / wallet_count, 1) if wallet_count > 0 else 0.0

        expiring_24h = CoinTransaction.objects.filter(
            wallet__restaurant=restaurant,
            is_expired=False,
            expires_at__isnull=False,
            expires_at__lte=cutoff_24h,
            coins__gt=0,
        ).aggregate(total=Sum('coins'))['total'] or Decimal('0')

        # Revenue influenced = sum of booking amounts where coins were redeemed
        from django.db.models import OuterRef, Exists
        redeemed_bookings = Booking.objects.filter(
            restaurant=restaurant,
            coin_transactions__transaction_type='REDEEMED',
        ).distinct()
        revenue_influenced = redeemed_bookings.aggregate(
            total=Sum('total_amount')
        )['total'] or Decimal('0')

        top_wallets = wallets.order_by('-coin_balance')[:10]
        top_10 = [
            {
                'username': w.customer.username,
                'coins': str(w.coin_balance),
                'lifetime_cash_spent': str(w.lifetime_cash_spent),
            }
            for w in top_wallets
        ]

        data = {
            'total_wallets': wallet_count,
            'active_wallets_30d': wallets.filter(updated_at__gte=since_30d).count(),
            'coins_issued_total': earned,
            'coins_redeemed_total': abs(redeemed),
            'redemption_rate_percent': redemption_rate,
            'avg_coins_per_customer': avg_coins,
            'summit_vips': wallets.filter(is_summit_vip=True).count(),
            'coins_expiring_24h': expiring_24h,
            'revenue_influenced': revenue_influenced,
            'top_10_customers': top_10,
        }
        return Response(FullAnalyticsSerializer(data).data)
