from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from django.core.exceptions import ValidationError
from django.db.models import Count, Q
import uuid
from apps.accounts.models import User
from apps.business.distributor.models import DistributorID
from apps.business.distributor.serializers import DistributorIDSerializer
from apps.business.products.models import Product
from apps.core.utils import error_response


def _display_name(user):
    first_name = (getattr(user, 'first_name', '') or '').strip()
    last_name = (getattr(user, 'last_name', '') or '').strip()
    full_name = f"{first_name} {last_name}".strip()
    return full_name or getattr(user, 'mobile', None) or getattr(user, 'email', None) or 'User'


def _filter_for_product_opportunity_bundle(queryset, product):
    scoped = queryset.select_related('product__opportunity_bundle', 'user').order_by('created_at', 'id')
    if getattr(product, 'opportunity_bundle_id', None):
        return scoped.filter(product__opportunity_bundle_id=product.opportunity_bundle_id)
    return scoped.filter(product_id=product.id)


def _company_fallback_distributors(product):
    """Return company root distributor(s) for the same product/bundle; fallback to very first ID if root missing."""
    company_root_qs = _filter_for_product_opportunity_bundle(
        DistributorID.objects.filter(is_active=True, is_company_root=True),
        product,
    )
    if company_root_qs.exists():
        return company_root_qs.order_by('global_position', 'created_at', 'id')

    scoped_all = _filter_for_product_opportunity_bundle(
        DistributorID.objects.filter(is_active=True),
        product,
    ).order_by('global_position', 'created_at', 'id')
    first_distributor = scoped_all.first()
    if not first_distributor:
        return scoped_all.none()
    return scoped_all.filter(id=first_distributor.id)


def _resolve_referral_user(referral_id, fallback_user=None):
    referral_id = (referral_id or '').strip()
    if not referral_id:
        return fallback_user

    if fallback_user and getattr(fallback_user, 'referral_code', '').lower() == referral_id.lower():
        return fallback_user

    user_qs = User.objects.filter(
        Q(referral_code__iexact=referral_id) |
        Q(mobile__iexact=referral_id) |
        Q(email__iexact=referral_id)
    )

    # Also resolve by distributor code (TW...) for compatibility.
    distributor_owner_ids = DistributorID.objects.filter(distributor_code__iexact=referral_id).values_list('user_id', flat=True)
    if distributor_owner_ids:
        user_qs = user_qs | User.objects.filter(id__in=distributor_owner_ids)

    try:
        parsed_uuid = uuid.UUID(referral_id)
        user_qs = user_qs | User.objects.filter(id=parsed_uuid)
    except (ValueError, TypeError, AttributeError):
        pass

    if referral_id.isdigit():
        user_qs = user_qs | User.objects.filter(id=referral_id)

    return user_qs.first() or fallback_user

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_user_distributor_ids(request):
    """
    Get distributor IDs for a product by selection type.
    
    Query params:
    - product_id: Required, product ID
    - type: 'own' (user's own IDs) or 'referrer' (user.refferred_by's IDs)
    - referral_id: Optional, resolve referrer user by referral code/mobile/user id
    """
    user = request.user
    product_id = request.query_params.get('product_id')
    id_type = request.query_params.get('type', 'own')  # 'own' or 'referrer'
    referral_id = (request.query_params.get('referral_id') or '').strip()
    
    if not product_id:
        return error_response(
            message="product_id is required",
            code="PRODUCT_ID_REQUIRED",
            status_code=status.HTTP_400_BAD_REQUEST
        )
    
    if id_type not in ['own', 'referrer']:
        return error_response(
            message="type must be 'own' or 'referrer'",
            code="INVALID_TYPE",
            status_code=status.HTTP_400_BAD_REQUEST
        )

    try:
        product = Product.objects.select_related('opportunity_bundle').get(id=product_id, is_active=True)
    except ValidationError:
        return error_response(
            message="Invalid product_id format",
            code="INVALID_PRODUCT_ID",
            status_code=status.HTTP_400_BAD_REQUEST
        )
    except Product.DoesNotExist:
        return error_response(
            message="Product not found",
            code="PRODUCT_NOT_FOUND",
            status_code=status.HTTP_404_NOT_FOUND
        )
    
    try:
        if id_type == 'own':
            source_user = user
            dist_ids = _filter_for_product_opportunity_bundle(
                DistributorID.objects.filter(user=user),
                product,
            ).annotate(
                positions_filled=Count('direct_referrals', filter=Q(direct_referrals__is_active=True), distinct=True)
            )
        else:  # referrer
            linked_referrer = getattr(user, 'referred_by', None)
            source_user = _resolve_referral_user(referral_id, fallback_user=linked_referrer) if referral_id else linked_referrer
            if not source_user:
                return Response({
                    'success': True,
                    'data': [],
                    'type': id_type,
                    'count': 0,
                    'opportunity_bundle_id': str(product.opportunity_bundle_id) if product.opportunity_bundle_id else None,
                    'opportunity_bundle_name': product.opportunity_bundle.name if product.opportunity_bundle else None,
                    'source_user': None,
                    'lookup_error': 'Referral ID not found.' if referral_id else None,
                }, status=status.HTTP_200_OK)

            if str(source_user.id) == str(user.id):
                return Response({
                    'success': True,
                    'data': [],
                    'type': id_type,
                    'count': 0,
                    'opportunity_bundle_id': str(product.opportunity_bundle_id) if product.opportunity_bundle_id else None,
                    'opportunity_bundle_name': product.opportunity_bundle.name if product.opportunity_bundle else None,
                    'source_user': {
                        'id': str(source_user.id),
                        'name': _display_name(source_user),
                        'mobile': getattr(source_user, 'mobile', '') or '',
                    },
                    'lookup_error': 'Own referral ID is not allowed in Referrer tab. Please use the Own ID tab.',
                    'used_company_fallback': False,
                }, status=status.HTTP_200_OK)

            dist_ids = _filter_for_product_opportunity_bundle(
                DistributorID.objects.filter(user=source_user),
                product,
            ).annotate(
                positions_filled=Count('direct_referrals', filter=Q(direct_referrals__is_active=True), distinct=True)
            )

            if not dist_ids.exists():
                total_source_ids = DistributorID.objects.filter(user=source_user).count()
                if total_source_ids == 0:
                    lookup_error = 'This referrer has no distributor IDs yet.'
                else:
                    lookup_error = 'This referrer has distributor IDs, but none in the selected opportunity bundle.'

                fallback_ids = _company_fallback_distributors(product).annotate(
                    positions_filled=Count('direct_referrals', filter=Q(direct_referrals__is_active=True), distinct=True)
                )

                if fallback_ids.exists():
                    fallback_serializer = DistributorIDSerializer(fallback_ids, many=True)
                    fallback_data = fallback_serializer.data
                    fallback_map = {
                        str(dist.id): int(getattr(dist, 'positions_filled', 0) or 0)
                        for dist in fallback_ids
                    }
                    for item in fallback_data:
                        item['positions_filled'] = fallback_map.get(str(item.get('id')), 0)

                    fallback_owner = fallback_ids.first().user
                    return Response({
                        'success': True,
                        'data': fallback_data,
                        'type': id_type,
                        'count': len(fallback_data),
                        'opportunity_bundle_id': str(product.opportunity_bundle_id) if product.opportunity_bundle_id else None,
                        'opportunity_bundle_name': product.opportunity_bundle.name if product.opportunity_bundle else None,
                        'source_user': {
                            'id': str(fallback_owner.id),
                            'name': _display_name(fallback_owner),
                            'mobile': getattr(fallback_owner, 'mobile', '') or '',
                        },
                        'lookup_error': f'{lookup_error} Using company referral fallback (first eligible distributor ID).',
                        'used_company_fallback': True,
                    }, status=status.HTTP_200_OK)

                return Response({
                    'success': True,
                    'data': [],
                    'type': id_type,
                    'count': 0,
                    'opportunity_bundle_id': str(product.opportunity_bundle_id) if product.opportunity_bundle_id else None,
                    'opportunity_bundle_name': product.opportunity_bundle.name if product.opportunity_bundle else None,
                    'source_user': {
                        'id': str(source_user.id),
                        'name': _display_name(source_user),
                        'mobile': getattr(source_user, 'mobile', '') or '',
                    },
                    'lookup_error': lookup_error,
                    'used_company_fallback': False,
                }, status=status.HTTP_200_OK)
        
        serializer = DistributorIDSerializer(dist_ids, many=True)
        serialized_data = serializer.data
        filled_map = {str(dist.id): int(getattr(dist, 'positions_filled', 0) or 0) for dist in dist_ids}
        for item in serialized_data:
            item['positions_filled'] = filled_map.get(str(item.get('id')), 0)

        return Response({
            'success': True,
            'data': serialized_data,
            'type': id_type,
            'count': dist_ids.count(),
            'opportunity_bundle_id': str(product.opportunity_bundle_id) if product.opportunity_bundle_id else None,
            'opportunity_bundle_name': product.opportunity_bundle.name if product.opportunity_bundle else None,
            'source_user': {
                'id': str(source_user.id),
                'name': _display_name(source_user),
                'mobile': getattr(source_user, 'mobile', '') or '',
            } if source_user else None,
            'lookup_error': None,
            'used_company_fallback': False,
        }, status=status.HTTP_200_OK)
    
    except Exception as e:
        return error_response(
            message=str(e),
            code="LOOKUP_FAILED",
            status_code=status.HTTP_400_BAD_REQUEST
        )
