from django.utils import timezone
from django.utils.dateparse import parse_datetime
from rest_framework import decorators, generics, permissions, status, viewsets
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView

from .models import LuckyDip, LuckyDipEntry, LuckyDipPrize
from .serializers import LuckyDipAdminSerializer, LuckyDipEntrySerializer, LuckyDipPrizeSerializer, LuckyDipSerializer
from .services.lucky_dip_service import join_lucky_dip, run_lucky_dip_draw
from apps.business.products.models import Product


class LuckyDipAdminViewSet(viewsets.ModelViewSet):
    queryset = LuckyDip.objects.all()
    serializer_class = LuckyDipAdminSerializer
    permission_classes = [IsAdminUser]

    @decorators.action(detail=True, methods=["post"])
    def activate(self, request, pk=None):
        dip = self.get_object()
        dip.is_active = True
        dip.save(update_fields=["is_active"])
        return Response({"success": True, "is_active": dip.is_active})

    @decorators.action(detail=True, methods=["post"])
    def inactivate(self, request, pk=None):
        dip = self.get_object()
        dip.is_active = False
        dip.save(update_fields=["is_active"])
        return Response({"success": True, "is_active": dip.is_active})

    @decorators.action(detail=True, methods=["post"])
    def ready_to_draw(self, request, pk=None):
        dip = self.get_object()
        draw_date_raw = request.data.get("draw_date")
        announcement_title = request.data.get("announcement_title", "")
        announcement_message = request.data.get("announcement_message", "")
        # Channels the admin selected to notify on (list of strings)
        notify_channels_raw = request.data.get("notify_channels", [])
        notify_channels = [str(c).strip() for c in (notify_channels_raw if isinstance(notify_channels_raw, list) else []) if str(c).strip()]

        VALID_CHANNELS = {"in_app", "push", "email", "whatsapp", "sms"}
        notify_channels = [c for c in notify_channels if c in VALID_CHANNELS]

        parsed_draw_date = parse_datetime(draw_date_raw) if draw_date_raw else None
        if draw_date_raw and parsed_draw_date is None:
            return Response({"success": False, "error": "Invalid draw date format."}, status=status.HTTP_400_BAD_REQUEST)

        if parsed_draw_date and timezone.is_naive(parsed_draw_date):
            parsed_draw_date = timezone.make_aware(parsed_draw_date, timezone.get_current_timezone())

        dip.draw_date = parsed_draw_date or timezone.now()
        dip.announcement_title = str(announcement_title or "").strip()
        dip.announcement_message = str(announcement_message or "").strip()
        dip.save(update_fields=["draw_date", "announcement_title", "announcement_message"])

        # Dispatch notifications if any channels were selected
        dispatch_result: dict = {}
        if notify_channels:
            try:
                from .tasks import dispatch_lucky_dip_announcement_task
                dispatch_lucky_dip_announcement_task.delay(dip.id, notify_channels)
                dispatch_result = {"queued": True, "channels": notify_channels}
            except Exception:
                # Celery not running — fall back to synchronous dispatch
                try:
                    from .services.announcement_service import dispatch_lucky_dip_announcement
                    dispatch_result = dispatch_lucky_dip_announcement(dip, channels=notify_channels)
                except Exception as exc:
                    dispatch_result = {"error": str(exc)}

        return Response(
            {
                "success": True,
                "draw_date": dip.draw_date,
                "announcement_title": dip.announcement_title,
                "announcement_message": dip.announcement_message,
                "notifications": dispatch_result,
            }
        )

    @decorators.action(detail=True, methods=["post"])
    def undo_ready_to_draw(self, request, pk=None):
        dip = self.get_object()
        if dip.is_drawn:
            return Response(
                {"success": False, "error": "Cannot undo ready-to-draw after draw is completed."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        dip.draw_date = None
        dip.announcement_title = ""
        dip.announcement_message = ""
        dip.save(update_fields=["draw_date", "announcement_title", "announcement_message"])
        return Response({"success": True, "draw_date": dip.draw_date, "announcement_title": dip.announcement_title, "announcement_message": dip.announcement_message})

    @decorators.action(detail=True, methods=["post"])
    def draw(self, request, pk=None):
        dip = self.get_object()
        if not dip.draw_date:
            return Response({"success": False, "error": "Mark this Lucky Dip as ready to draw first."}, status=status.HTTP_400_BAD_REQUEST)
        if not dip.prizes.exists():
            return Response({"success": False, "error": "Configure prizes before running draw."}, status=status.HTTP_400_BAD_REQUEST)
        if not dip.entries.exists():
            return Response({"success": False, "error": "No entries to draw from."}, status=status.HTTP_400_BAD_REQUEST)

        try:
            winners = run_lucky_dip_draw(dip.id)
            return Response({"success": True, "winners": winners})
        except Exception as exc:
            return Response({"success": False, "error": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

    @decorators.action(detail=True, methods=["get", "post"])
    def prizes(self, request, pk=None):
        dip = self.get_object()
        if request.method.lower() == "get":
            prizes = LuckyDipPrize.objects.filter(lucky_dip=dip)
            return Response(LuckyDipPrizeSerializer(prizes, many=True).data)

        prizes_payload = request.data.get("prizes", request.data)
        if not isinstance(prizes_payload, list):
            return Response({"success": False, "error": "Payload must be a list or include a 'prizes' list."}, status=status.HTTP_400_BAD_REQUEST)

        if dip.is_drawn:
            return Response({"success": False, "error": "Cannot modify prizes after draw is completed."}, status=status.HTTP_400_BAD_REQUEST)

        normalized = []
        seen_positions = set()
        for idx, item in enumerate(prizes_payload, start=1):
            if not isinstance(item, dict):
                return Response({"success": False, "error": f"Prize row {idx} is invalid."}, status=status.HTTP_400_BAD_REQUEST)

            position = item.get("position")
            product_id = item.get("product")
            quantity = item.get("quantity")

            try:
                position = int(position)
                quantity = int(quantity)
            except (TypeError, ValueError):
                return Response({"success": False, "error": f"Prize row {idx}: position and quantity must be numbers."}, status=status.HTTP_400_BAD_REQUEST)

            if position <= 0:
                return Response({"success": False, "error": f"Prize row {idx}: position must be greater than zero."}, status=status.HTTP_400_BAD_REQUEST)
            if quantity <= 0:
                return Response({"success": False, "error": f"Prize row {idx}: quantity must be greater than zero."}, status=status.HTTP_400_BAD_REQUEST)
            if position in seen_positions:
                return Response({"success": False, "error": f"Duplicate prize position: {position}."}, status=status.HTTP_400_BAD_REQUEST)

            product = Product.objects.filter(id=product_id, is_active=True).first()
            if not product:
                return Response({"success": False, "error": f"Prize row {idx}: product {product_id} not found or inactive."}, status=status.HTTP_400_BAD_REQUEST)

            seen_positions.add(position)
            normalized.append({"position": position, "product": product, "quantity": quantity})

        from django.db import transaction
        with transaction.atomic():
            LuckyDipPrize.objects.filter(lucky_dip=dip).delete()
            LuckyDipPrize.objects.bulk_create([
                LuckyDipPrize(
                    lucky_dip=dip,
                    position=row["position"],
                    product=row["product"],
                    quantity=row["quantity"],
                )
                for row in sorted(normalized, key=lambda r: r["position"])
            ])

        prizes = LuckyDipPrize.objects.filter(lucky_dip=dip)
        return Response({"success": True, "prizes": LuckyDipPrizeSerializer(prizes, many=True).data})

    @decorators.action(detail=True, methods=["get"])
    def entries(self, request, pk=None):
        dip = self.get_object()
        entries = dip.entries.all()
        return Response(LuckyDipEntrySerializer(entries, many=True).data)


class LuckyDipListView(generics.ListAPIView):
    serializer_class = LuckyDipSerializer
    queryset = LuckyDip.objects.filter(is_active=True)
    permission_classes = [permissions.AllowAny]


class LuckyDipJoinView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        user = request.user
        lucky_dip_id = request.data.get("lucky_dip_id")
        try:
            entry_number = join_lucky_dip(user, lucky_dip_id)
            # Return updated entry count for the dip
            from .models import LuckyDipEntry, LuckyDip
            current_entries = LuckyDipEntry.objects.filter(lucky_dip_id=lucky_dip_id, user=user).count()
            try:
                dip = LuckyDip.objects.get(id=lucky_dip_id)
                max_slots = dip.max_slots_per_user
            except LuckyDip.DoesNotExist:
                max_slots = 5
            return Response(
                {"success": True, "entry_number": entry_number, "user_entries": current_entries, "max_slots_per_user": max_slots},
                status=status.HTTP_201_CREATED,
            )
        except Exception as exc:
            error_msg = str(exc)
            if error_msg.startswith('MAX_SLOTS_REACHED:'):
                parts = error_msg.split(':')
                return Response(
                    {
                        "success": False,
                        "error": "max_slots_reached",
                        "current_entries": int(parts[1]),
                        "max_slots": int(parts[2]),
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )
            return Response({"success": False, "error": error_msg}, status=status.HTTP_400_BAD_REQUEST)
