from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.business.distributor.models import DistributorID
from apps.business.rewards.services.level_service import calculate_level_progress_snapshots
from apps.business.schema_compat import defer_missing_product_part_payment_fields
from apps.business.schemes.models import OpportunityBundleLevel
from apps.business.schemes.rank import Rank


class RankProgressView(APIView):
    """
    GET /api/v1/web/rank/progress/

    Returns rank progress data for every distributor ID the authenticated user owns,
    grouped by opportunity_bundle so the frontend can render opportunity_bundle tabs. Includes the full level
    list per opportunity_bundle (pulled from SchemeLevel + Rank) so the frontend doesn't need a
    second request.
    """

    permission_classes = [IsAuthenticated]

    def get(self, request):
        user = request.user

        # ── 1. All distributor IDs for this user ─────────────────────────────
        dist_ids = list(
            defer_missing_product_part_payment_fields(
                DistributorID.objects.filter(user=user)
                .select_related("product__opportunity_bundle", "level_progress")
                .order_by("created_at"),  # chronological -> first ID is oldest
                relation_prefix='product',
            )
        )

        if not dist_ids:
            return Response({"success": True, "data": {"schemes": [], "first_distributor_id": None}})

        # ── 2. Live progress snapshot per distributor ID ─────────────────────
        progress_snapshots = calculate_level_progress_snapshots(dist_ids, persist=True)

        # ── 3. Collect all opportunity bundles referenced by the user's IDs ─────────────
        opportunity_bundle_ids = list({
            d.product.opportunity_bundle_id
            for d in dist_ids
            if d.product and d.product.opportunity_bundle_id
        })

        # ── 4. Fetch OpportunityBundleLevel rows for all relevant bundles ───────────────
        scheme_levels_qs = (
            OpportunityBundleLevel.objects.filter(opportunity_bundle_id__in=opportunity_bundle_ids)
            .select_related("opportunity_bundle")
            .order_by("opportunity_bundle_id", "level_number")
        )

        # ── 5. Fetch Rank names (global + bundle-specific, prefer bundle) ────
        ranks_qs = Rank.objects.filter(
            opportunity_bundle_id__in=opportunity_bundle_ids + [None]
        ).order_by("level_number", "opportunity_bundle_id")

        # Build lookup: {(opportunity_bundle_id, level_number): rank meta}
        rank_meta_map: dict[tuple, dict] = {}
        request = self.request

        def _badge_image_url(rank_obj):
            if not getattr(rank_obj, "badge_image", None):
                return None
            if request:
                return request.build_absolute_uri(rank_obj.badge_image.url)
            return rank_obj.badge_image.url

        for r in ranks_qs:
            key = (r.opportunity_bundle_id, r.level_number)
            rank_meta_map[key] = {
                "name": r.name,
                "badge_image_url": _badge_image_url(r),
                "badge_icon": r.badge_icon,
            }
        # Global ranks (bundle=None) are fallback
        global_rank_map = {
            r.level_number: {
                "name": r.name,
                "badge_image_url": _badge_image_url(r),
                "badge_icon": r.badge_icon,
            }
            for r in ranks_qs
            if r.opportunity_bundle_id is None
        }

        def get_rank_meta(opportunity_bundle_id, level_number):
            rank_meta = rank_meta_map.get((opportunity_bundle_id, level_number)) or global_rank_map.get(level_number)
            if rank_meta:
                return rank_meta
            return {
                "name": f"Level {level_number}",
                "badge_image_url": None,
                "badge_icon": None,
            }

        # ── 6. Build per-opportunity-bundle level list ───────────────────────────────────
        scheme_level_map: dict = {}  # opportunity_bundle_id → list of level dicts
        for sl in scheme_levels_qs:
            rank_meta = get_rank_meta(sl.opportunity_bundle_id, sl.level_number)
            scheme_level_map.setdefault(sl.opportunity_bundle_id, []).append(
                {
                    "level_number": sl.level_number,
                    "rank_name": rank_meta["name"],
                    "rank_badge_image_url": rank_meta["badge_image_url"],
                    "rank_badge_icon": rank_meta["badge_icon"],
                    "required_positions": sl.required_positions,
                    "reward_amount": str(sl.reward_amount),
                    "has_reward": sl.has_reward,
                    "has_service": sl.has_service,
                    "has_gift": sl.has_gift,
                    "auto_id_enabled": sl.auto_id_enabled,
                    "auto_id_count": sl.auto_id_count,
                    "gift_external": sl.gift_external or "",
                }
            )

        # ── 7. Group distributor IDs by opportunity bundle ───────────────────────────────
        scheme_groups: dict = {}  # opportunity_bundle_id → {meta + list of ids}
        for d in dist_ids:
            if not d.product or not d.product.opportunity_bundle_id:
                continue
            opportunity_bundle_id = str(d.product.opportunity_bundle_id)
            if opportunity_bundle_id not in scheme_groups:
                scheme_groups[opportunity_bundle_id] = {
                    "opportunity_bundle_id": opportunity_bundle_id,
                    "opportunity_bundle_name": d.product.opportunity_bundle.name,
                    "opportunity_bundle_id": opportunity_bundle_id,
                    "opportunity_bundle_name": d.product.opportunity_bundle.name,
                    "distributor_ids": [],
                    "levels": scheme_level_map.get(d.product.opportunity_bundle_id, []),
                }

            progress_snapshot = progress_snapshots.get(d.id, {})
            current_level = progress_snapshot.get("current_level", 0)
            total_positions = progress_snapshot.get("total_positions_after", 0)
            # last_completed_level = levels whose rewards have been processed/paid out.
            # current_level = highest level unlocked by position count (may be ahead of rewards).
            last_completed_level = progress_snapshot.get("last_completed_level", 0)

            # Work out next level threshold from SchemeLevel
            levels_for_scheme = scheme_level_map.get(d.product.opportunity_bundle_id, [])
            next_level_data = next(
                (lv for lv in levels_for_scheme if lv["level_number"] == current_level + 1),
                None,
            )
            # If at/past the last level, use the current level threshold
            current_level_data = next(
                (lv for lv in levels_for_scheme if lv["level_number"] == current_level),
                None,
            )

            next_required = progress_snapshot.get("next_level_required")
            progress_pct = progress_snapshot.get("progress_percent", 0.0)
            positions_to_next = progress_snapshot.get("positions_to_next", 0)

            scheme_groups[opportunity_bundle_id]["distributor_ids"].append(
                {
                    "id": str(d.id),
                    "code": d.distributor_code,
                    "is_active": d.is_active,
                    "is_auto_generated": d.is_auto_generated,
                    "product_name": d.product.name if d.product else None,
                    "completed_levels": last_completed_level,
                    "current_level": current_level,
                    "current_rank_name": get_rank_meta(d.product.opportunity_bundle_id, current_level)["name"] if current_level else "Not started",
                    "current_rank_badge_image_url": (
                        get_rank_meta(d.product.opportunity_bundle_id, current_level)["badge_image_url"]
                        if current_level else None
                    ),
                    "current_rank_badge_icon": (
                        get_rank_meta(d.product.opportunity_bundle_id, current_level)["badge_icon"]
                        if current_level else None
                    ),
                    "next_level": current_level + 1 if next_level_data else None,
                    "next_rank_name": next_level_data["rank_name"] if next_level_data else None,
                    "next_rank_badge_image_url": next_level_data["rank_badge_image_url"] if next_level_data else None,
                    "next_rank_badge_icon": next_level_data["rank_badge_icon"] if next_level_data else None,
                    "current_level_required_positions": progress_snapshot.get("current_level_required_positions", 0),
                    "total_positions": total_positions,
                    "next_level_required": next_required,
                    "positions_to_next": positions_to_next,
                    "progress_percent": progress_pct,
                    "last_calculated_at": (
                        progress_snapshot["last_calculated_at"].isoformat() if progress_snapshot.get("last_calculated_at") else None
                    ),
                }
            )

        # First distributor ID overall (chronologically oldest)
        first_dist_id = str(dist_ids[0].id) if dist_ids else None

        return Response(
            {
                "success": True,
                "data": {
                    "opportunity_bundles": list(scheme_groups.values()),
                    "schemes": list(scheme_groups.values()),
                    "first_distributor_id": first_dist_id,
                },
            }
        )
