"""
Admin views for distributor position management.

GET /api/v1/admin/distributor-positions/
  - Paginated list of ALL DistributorID records across all users
  - Filter by: opportunity_bundle, search (user name/email/mobile/distributor_code), is_active
  - Returns per-position: user info, level reached, withdrawal status, power stream status

GET /api/v1/admin/distributor-positions/user/<user_id>/
  - All positions owned by a specific user (for the popup detail view)
"""

from __future__ import annotations

import bisect
from collections import defaultdict

from django.db.models import Count, Q
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView

from apps.business.distributor.models import DistributorID
from apps.business.rewards.models import DistributorLevelProgress
from apps.business.schemes.models import OpportunityBundle, OpportunityBundleLevel


# ── helpers ──────────────────────────────────────────────────────────────────

def _chunked(lst, size=500):
    for i in range(0, len(lst), size):
        yield lst[i: i + size]


def _build_direct_referral_count_map(distributor_ids: list) -> dict:
    """
    Returns {distributor_id: direct_referral_count} for the given DistributorID list.
    """
    id_values = [d.id for d in distributor_ids]
    counts: dict = {d.id: 0 for d in distributor_ids}
    for chunk in _chunked(id_values):
        rows = (
            DistributorID.objects.filter(sponsor_distributor_id__in=chunk)
            .values("sponsor_distributor_id")
            .annotate(cnt=Count("id"))
        )
        for row in rows:
            counts[row["sponsor_distributor_id"]] = row["cnt"]
    return counts


def _build_global_position_count_map(distributor_ids: list) -> dict:
    """
    For each distributor, count how many positions in the same bundle have
    global_position > this distributor's global_position.
    """
    result: dict = {}
    bundle_to_dists: dict = defaultdict(list)

    for dist in distributor_ids:
        bundle_id = getattr(getattr(dist, "product", None), "opportunity_bundle_id", None)
        if bundle_id and dist.global_position:
            bundle_to_dists[bundle_id].append(dist)
        else:
            result[dist.id] = 0

    for bundle_id, dists in bundle_to_dists.items():
        all_positions = sorted(
            DistributorID.objects.filter(
                product__opportunity_bundle_id=bundle_id
            ).values_list("global_position", flat=True)
        )
        total = len(all_positions)
        for dist in dists:
            idx = bisect.bisect_right(all_positions, dist.global_position)
            result[dist.id] = total - idx

    return result


def _build_cumulative_levels(bundle_id) -> list:
    levels = OpportunityBundleLevel.objects.filter(
        opportunity_bundle_id=bundle_id
    ).order_by("level_number")
    cumulative = 0
    entries = []
    for lvl in levels:
        cumulative += int(getattr(lvl, "required_positions", 0) or 0)
        entries.append(
            {
                "level_number": lvl.level_number,
                "cumulative_required_positions": cumulative,
            }
        )
    return entries


def _resolve_level(total_positions: int, cumulative_levels: list) -> dict:
    highest_level = 0
    next_level = None
    next_required = None
    current_threshold = 0

    for level in cumulative_levels:
        threshold = level["cumulative_required_positions"]
        if total_positions >= threshold:
            highest_level = level["level_number"]
            current_threshold = threshold
        else:
            next_level = level["level_number"]
            next_required = threshold
            break

    positions_to_next = max(0, next_required - total_positions) if next_required is not None else 0
    return {
        "level_reached": highest_level,
        "next_level": next_level,
        "positions_to_next": positions_to_next,
    }


def _build_level_progress_map(distributor_ids: list) -> dict:
    """
    Fetch persisted level progress records from DB.
    Falls back to 0 if not yet computed.
    """
    id_values = [d.id for d in distributor_ids]
    result = {d.id: {"last_completed_level": 0, "current_level": 0} for d in distributor_ids}
    for chunk in _chunked(id_values):
        rows = DistributorLevelProgress.objects.filter(
            distributor_id__in=chunk
        ).values("distributor_id", "current_level", "last_completed_level")
        for row in rows:
            result[row["distributor_id"]] = {
                "last_completed_level": row.get("last_completed_level", 0),
                "current_level": row.get("current_level", 0),
            }
    return result


def _serialize_position(dist, direct_count: int, positions_after: int, level_info: dict) -> dict:
    user = dist.user
    bundle_id = getattr(getattr(dist, "product", None), "opportunity_bundle_id", None)

    withdrawal_eligible = (direct_count >= 2)
    power_stream_active = (direct_count >= 3)

    product_obj = getattr(dist, "product", None)
    bundle_obj = getattr(product_obj, "opportunity_bundle", None)

    return {
        "id": str(dist.id),
        "distributor_code": dist.distributor_code,
        "global_position": dist.global_position,
        "is_active": dist.is_active,
        "created_at": dist.created_at.isoformat() if dist.created_at else None,
        # product / bundle
        "product_name": getattr(product_obj, "name", None),
        "opportunity_bundle_id": str(bundle_id) if bundle_id else None,
        "opportunity_bundle_name": getattr(bundle_obj, "name", None),
        # user
        "user_id": str(user.id),
        "user_name": f"{getattr(user, 'first_name', '') or ''} {getattr(user, 'last_name', '') or ''}".strip() or str(user),
        "user_email": getattr(user, "email", "") or "",
        "user_mobile": getattr(user, "mobile", "") or getattr(user, "phone", "") or "",
        # eligibility
        "direct_referrals": direct_count,
        "positions_after": positions_after,
        "withdrawal_eligible": withdrawal_eligible,
        "power_stream_active": power_stream_active,
        # level
        "level_reached": level_info.get("current_level", 0),
        "last_completed_level": level_info.get("last_completed_level", 0),
    }


# ── views ─────────────────────────────────────────────────────────────────────

class AdminDistributorPositionListView(APIView):
    """
    GET /api/v1/admin/distributor-positions/

    Query params:
      opportunity_bundle  - UUID to filter by bundle
      search              - matches user name / email / mobile / distributor_code
      is_active           - 'true' | 'false'
      page                - 1-based (default 1)
      page_size           - default 50, max 200
    """

    permission_classes = [permissions.IsAdminUser]

    def get(self, request):
        qs = (
            DistributorID.objects.select_related(
                "user", "product__opportunity_bundle"
            ).order_by("global_position")
        )

        # ── Filters ───────────────────────────────────────────────────────────
        bundle_id = request.query_params.get("opportunity_bundle", "").strip()
        if bundle_id:
            qs = qs.filter(product__opportunity_bundle_id=bundle_id)

        search = request.query_params.get("search", "").strip()
        if search:
            qs = qs.filter(
                Q(distributor_code__icontains=search)
                | Q(user__first_name__icontains=search)
                | Q(user__last_name__icontains=search)
                | Q(user__email__icontains=search)
                | Q(user__mobile__icontains=search)
            )

        is_active_param = request.query_params.get("is_active", "").strip().lower()
        if is_active_param == "true":
            qs = qs.filter(is_active=True)
        elif is_active_param == "false":
            qs = qs.filter(is_active=False)

        # ── Pagination ────────────────────────────────────────────────────────
        try:
            page = max(1, int(request.query_params.get("page", 1)))
        except (ValueError, TypeError):
            page = 1
        try:
            page_size = min(200, max(1, int(request.query_params.get("page_size", 50))))
        except (ValueError, TypeError):
            page_size = 50

        total = qs.count()
        offset = (page - 1) * page_size
        distributor_ids = list(qs[offset: offset + page_size])

        # ── Compute metrics ───────────────────────────────────────────────────
        direct_count_map = _build_direct_referral_count_map(distributor_ids)
        positions_after_map = _build_global_position_count_map(distributor_ids)
        level_progress_map = _build_level_progress_map(distributor_ids)

        results = [
            _serialize_position(
                dist,
                direct_count=direct_count_map.get(dist.id, 0),
                positions_after=positions_after_map.get(dist.id, 0),
                level_info=level_progress_map.get(dist.id, {}),
            )
            for dist in distributor_ids
        ]

        # ── Bundle list for filter dropdown ──────────────────────────────────
        bundles = list(
            OpportunityBundle.objects.filter(is_active=True)
            .values("id", "name")
            .order_by("name")
        )

        return Response(
            {
                "count": total,
                "page": page,
                "page_size": page_size,
                "total_pages": max(1, -(-total // page_size)),  # ceiling division
                "results": results,
                "bundles": [{"id": str(b["id"]), "name": b["name"]} for b in bundles],
            },
            status=status.HTTP_200_OK,
        )


class AdminDistributorPositionUserDetailView(APIView):
    """
    GET /api/v1/admin/distributor-positions/user/<user_id>/

    Returns all positions owned by this user with full metrics.
    """

    permission_classes = [permissions.IsAdminUser]

    def get(self, request, user_id):
        qs = (
            DistributorID.objects.filter(user_id=user_id)
            .select_related("user", "product__opportunity_bundle")
            .order_by("global_position")
        )
        distributor_ids = list(qs)
        if not distributor_ids:
            return Response({"results": [], "count": 0}, status=status.HTTP_200_OK)

        direct_count_map = _build_direct_referral_count_map(distributor_ids)
        positions_after_map = _build_global_position_count_map(distributor_ids)
        level_progress_map = _build_level_progress_map(distributor_ids)

        results = [
            _serialize_position(
                dist,
                direct_count=direct_count_map.get(dist.id, 0),
                positions_after=positions_after_map.get(dist.id, 0),
                level_info=level_progress_map.get(dist.id, {}),
            )
            for dist in distributor_ids
        ]

        return Response({"results": results, "count": len(results)}, status=status.HTTP_200_OK)
