from rest_framework import viewsets, permissions
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework import status
from django.utils import timezone
from django.db.models import Sum
from .models import DistributorID
from .models import AutoIDAllocation, DistributorTransferAcceptance, DistributorTransferRequest, DistributorTransferTerms, DistributorTransferHistory, DistributorRewardOwnershipLedger
from .serializers import (
    AutoIDActivateSerializer,
    AutoIDAllocationSerializer,
    DistributorIDSerializer,
    DistributorTransferOldOwnerConsentSerializer,
    DistributorTransferRequestAdminActionSerializer,
    DistributorTransferRequestCreateSerializer,
    DistributorTransferRequestSerializer,
    DistributorTransferTermsSerializer,
    DistributorTransferRequestUpdateSerializer,
)
from .services.hybrid_service import (
    activate_auto_ids,
    approve_transfer_request,
    reject_transfer_request,
    submit_transfer_request,
)
from apps.business.products.models import Product
from apps.accounts.models import User


def _active_transfer_terms_queryset():
    now = timezone.now()
    return DistributorTransferTerms.objects.filter(
        is_active=True,
    ).filter(
        applies_from__isnull=True,
    ) | DistributorTransferTerms.objects.filter(
        is_active=True,
        applies_from__lte=now,
    )


def _active_transfer_terms():
    now = timezone.now()
    return DistributorTransferTerms.objects.filter(is_active=True).filter(
        applies_from__isnull=True,
    ).union(
        DistributorTransferTerms.objects.filter(is_active=True, applies_from__lte=now)
    )


def _current_applicable_terms():
    now = timezone.now()
    qs = DistributorTransferTerms.objects.filter(is_active=True).order_by('display_order', 'created_at')
    qs = qs.exclude(applies_from__isnull=False, applies_from__gt=now)
    qs = qs.exclude(applies_to__isnull=False, applies_to__lt=now)
    return qs


def _has_mandatory_acceptance(transfer_request: DistributorTransferRequest, accepted_by_user) -> bool:
    terms = _current_applicable_terms()
    mandatory_term_ids = set(str(term.id) for term in terms if term.mandatory)
    if not mandatory_term_ids:
        return True

    accepted_term_ids = set(
        str(acceptance.terms_id)
        for acceptance in DistributorTransferAcceptance.objects.filter(
            transfer_request=transfer_request,
            accepted_by=accepted_by_user,
            accepted=True,
        )
    )
    return mandatory_term_ids.issubset(accepted_term_ids)

class DistributorIDAdminViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = DistributorID.objects.all()
    serializer_class = DistributorIDSerializer
    permission_classes = [permissions.IsAdminUser]

class DistributorIDWebViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = DistributorIDSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        return DistributorID.objects.filter(user=user)

    def _build_incoming_transfer_info_map(self, distributor_ids):
        if not distributor_ids:
            return {}

        histories = list(
            DistributorTransferHistory.objects.filter(
                distributor_id__in=distributor_ids,
                new_owner_user=self.request.user,
            ).select_related('old_owner_user', 'new_owner_user').order_by('-approved_at')
        )

        latest_by_distributor = {}
        for history in histories:
            key = str(history.distributor_id)
            if key not in latest_by_distributor:
                latest_by_distributor[key] = history

        transfer_info = {}
        for distributor_id, history in latest_by_distributor.items():
            boundary = history.transfer_reward_boundary_timestamp or history.approved_at
            aggregate = DistributorRewardOwnershipLedger.objects.filter(
                distributor_id=history.distributor_id,
                beneficiary_user_id=history.old_owner_user_id,
                reward_timestamp__lte=boundary,
            ).aggregate(total=Sum('amount'))
            pre_transfer_total = aggregate.get('total')
            from_owner_name = f"{history.old_owner_user.first_name} {history.old_owner_user.last_name}".strip() or history.old_owner_user.email or history.old_owner_user.mobile
            to_owner_name = f"{history.new_owner_user.first_name} {history.new_owner_user.last_name}".strip() or history.new_owner_user.email or history.new_owner_user.mobile
            transfer_info[distributor_id] = {
                'is_transferred_in': True,
                'from_owner_name': from_owner_name,
                'to_owner_name': to_owner_name,
                'transferred_at': history.approved_at,
                'transfer_reward_boundary_timestamp': boundary,
                'pre_transfer_benefit_total_for_old_owner': str(pre_transfer_total or 0),
            }
        return transfer_info

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        distributor_ids = [str(row.id) for row in queryset]
        context = self.get_serializer_context()
        context['incoming_transfer_info_by_distributor'] = self._build_incoming_transfer_info_map(distributor_ids)
        serializer = self.get_serializer(queryset, many=True, context=context)
        return Response(serializer.data)

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        context = self.get_serializer_context()
        context['incoming_transfer_info_by_distributor'] = self._build_incoming_transfer_info_map([str(instance.id)])
        serializer = self.get_serializer(instance, context=context)
        return Response(serializer.data)


class UserTransferredOutDistributorListView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        histories = DistributorTransferHistory.objects.filter(
            old_owner_user=request.user,
        ).select_related('distributor', 'old_owner_user', 'new_owner_user').order_by('-approved_at')

        data = []
        for history in histories:
            boundary = history.transfer_reward_boundary_timestamp or history.approved_at
            aggregate = DistributorRewardOwnershipLedger.objects.filter(
                distributor_id=history.distributor_id,
                beneficiary_user_id=history.old_owner_user_id,
                reward_timestamp__lte=boundary,
            ).aggregate(total=Sum('amount'))
            pre_transfer_total = aggregate.get('total')
            from_owner_name = f"{history.old_owner_user.first_name} {history.old_owner_user.last_name}".strip() or history.old_owner_user.email or history.old_owner_user.mobile
            to_owner_name = f"{history.new_owner_user.first_name} {history.new_owner_user.last_name}".strip() or history.new_owner_user.email or history.new_owner_user.mobile
            data.append({
                'transfer_history_id': str(history.id),
                'distributor_id': str(history.distributor_id),
                'distributor_code': history.distributor.distributor_code,
                'from_owner_name': from_owner_name,
                'to_owner_name': to_owner_name,
                'transferred_at': history.approved_at,
                'transfer_reward_boundary_timestamp': boundary,
                'pre_transfer_benefit_total_for_old_owner': str(pre_transfer_total or 0),
            })

        return Response({'success': True, 'data': data})


class DistributorTransferTermsAdminViewSet(viewsets.ModelViewSet):
    queryset = DistributorTransferTerms.objects.all().order_by('display_order', 'created_at')
    serializer_class = DistributorTransferTermsSerializer
    permission_classes = [permissions.IsAdminUser]


class UserTransferTermsListView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        terms = _current_applicable_terms()
        return Response({'success': True, 'data': DistributorTransferTermsSerializer(terms, many=True).data})


class UserTransferRequestListCreateView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        qs = DistributorTransferRequest.objects.filter(
            old_owner_user=request.user
        ).select_related('target_distributor', 'old_owner_user', 'requested_new_owner_user').order_by('-created_at')
        return Response({'success': True, 'data': DistributorTransferRequestSerializer(qs, many=True).data})

    def post(self, request):
        serializer = DistributorTransferRequestCreateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        referral_code = str(serializer.validated_data.get('referral_code', '')).strip().upper()
        own_referral_code = str(getattr(request.user, 'referral_code', '') or '').strip().upper()
        if own_referral_code and referral_code == own_referral_code:
            return Response({'success': False, 'error': 'Your own referral code is not allowed for transfer request.'}, status=status.HTTP_400_BAD_REQUEST)

        distributor = DistributorID.objects.filter(id=serializer.validated_data['distributor_id'], is_active=True).first()
        if not distributor:
            return Response({'success': False, 'error': 'Distributor not found.'}, status=status.HTTP_404_NOT_FOUND)

        if str(distributor.user_id) != str(request.user.id):
            return Response({'success': False, 'error': 'Only the current owner can request transfer for this Distributor ID.'}, status=status.HTTP_403_FORBIDDEN)

        new_owner_user = User.objects.filter(referral_code=referral_code, is_active=True).first()
        if not new_owner_user:
            return Response({'success': False, 'error': 'Invalid referral code for new owner.'}, status=status.HTTP_400_BAD_REQUEST)

        existing_unapproved_same_distributor = DistributorTransferRequest.objects.filter(
            old_owner_user=request.user,
            target_distributor=distributor,
        ).exclude(
            status__in=[
                DistributorTransferRequest.Status.APPROVED,
                DistributorTransferRequest.Status.CANCELLED,
            ]
        ).order_by('-created_at').first()

        if existing_unapproved_same_distributor:
            return Response(
                {
                    'success': False,
                    'error': 'An unapproved request already exists for this Distributor ID. Please edit or delete that request first.',
                    'data': DistributorTransferRequestSerializer(existing_unapproved_same_distributor).data,
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        transfer_request = submit_transfer_request(
            requested_by=request.user,
            target_distributor=distributor,
            reason=serializer.validated_data.get('reason', ''),
            referral_code=referral_code,
            requested_new_owner_user=new_owner_user,
            accepted_term_ids=serializer.validated_data.get('accepted_term_ids', []),
        )
        return Response({'success': True, 'data': DistributorTransferRequestSerializer(transfer_request).data}, status=status.HTTP_201_CREATED)


class UserIncomingTransferRequestListView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        qs = DistributorTransferRequest.objects.filter(
            requested_new_owner_user=request.user,
        ).exclude(
            status=DistributorTransferRequest.Status.CANCELLED,
        ).select_related('target_distributor', 'old_owner_user', 'requested_new_owner_user').order_by('-created_at')
        return Response({'success': True, 'data': DistributorTransferRequestSerializer(qs, many=True).data})


class UserTransferRequestDetailView(APIView):
    permission_classes = [IsAuthenticated]

    @staticmethod
    def _get_transfer_for_user(user, transfer_id):
        return DistributorTransferRequest.objects.filter(
            id=transfer_id,
            old_owner_user=user,
        ).select_related('target_distributor', 'old_owner_user', 'requested_new_owner_user').first()

    def patch(self, request, transfer_id):
        transfer = self._get_transfer_for_user(request.user, transfer_id)
        if not transfer:
            return Response({'success': False, 'error': 'Transfer request not found.'}, status=status.HTTP_404_NOT_FOUND)

        if transfer.status not in [DistributorTransferRequest.Status.SUBMITTED, DistributorTransferRequest.Status.OLD_OWNER_CONFIRMED]:
            return Response({'success': False, 'error': 'Only active pending transfer requests can be edited.'}, status=status.HTTP_400_BAD_REQUEST)

        serializer = DistributorTransferRequestUpdateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        validated_data = serializer.validated_data

        if 'reason' in validated_data:
            transfer.reason = validated_data.get('reason', '')
        if 'referral_code' in validated_data:
            referral_code = str(validated_data.get('referral_code', '')).strip().upper()
            own_referral_code = str(getattr(request.user, 'referral_code', '') or '').strip().upper()
            if own_referral_code and referral_code == own_referral_code:
                return Response({'success': False, 'error': 'Your own referral code is not allowed for transfer request.'}, status=status.HTTP_400_BAD_REQUEST)
            transfer.referral_code = referral_code

        transfer.save(update_fields=['reason', 'referral_code', 'updated_at'])
        return Response({'success': True, 'data': DistributorTransferRequestSerializer(transfer).data})

    def delete(self, request, transfer_id):
        transfer = self._get_transfer_for_user(request.user, transfer_id)
        if not transfer:
            return Response({'success': False, 'error': 'Transfer request not found.'}, status=status.HTTP_404_NOT_FOUND)

        if transfer.status not in [DistributorTransferRequest.Status.SUBMITTED, DistributorTransferRequest.Status.OLD_OWNER_CONFIRMED]:
            return Response({'success': False, 'error': 'Only active pending transfer requests can be deleted.'}, status=status.HTTP_400_BAD_REQUEST)

        transfer.status = DistributorTransferRequest.Status.CANCELLED
        transfer.save(update_fields=['status', 'updated_at'])
        return Response({'success': True, 'data': {'transfer_id': str(transfer.id), 'status': transfer.status}})


class UserTransferRequestReopenView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request, transfer_id):
        transfer = DistributorTransferRequest.objects.filter(
            id=transfer_id,
        ).select_related('target_distributor', 'old_owner_user', 'requested_new_owner_user').first()
        if not transfer:
            return Response({'success': False, 'error': 'Transfer request not found.'}, status=status.HTTP_404_NOT_FOUND)

        is_participant = str(request.user.id) in [str(transfer.old_owner_user_id), str(transfer.requested_new_owner_user_id)]
        if not is_participant:
            return Response({'success': False, 'error': 'Only transfer participants can reopen this request.'}, status=status.HTTP_403_FORBIDDEN)

        if transfer.status not in [DistributorTransferRequest.Status.REJECTED, DistributorTransferRequest.Status.CANCELLED]:
            return Response({'success': False, 'error': 'Only closed requests can be reopened.'}, status=status.HTTP_400_BAD_REQUEST)

        transfer.status = DistributorTransferRequest.Status.SUBMITTED
        transfer.reviewed_by = None
        transfer.reviewed_at = None
        transfer.save(update_fields=['status', 'reviewed_by', 'reviewed_at', 'updated_at'])
        return Response({'success': True, 'data': DistributorTransferRequestSerializer(transfer).data})


class UserTransferOldOwnerConsentView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request, transfer_id):
        transfer = DistributorTransferRequest.objects.filter(
            id=transfer_id,
            requested_new_owner_user=request.user,
        ).select_related('target_distributor', 'old_owner_user', 'requested_new_owner_user').first()
        if not transfer:
            return Response({'success': False, 'error': 'Transfer request not found.'}, status=status.HTTP_404_NOT_FOUND)

        if transfer.status in [DistributorTransferRequest.Status.APPROVED, DistributorTransferRequest.Status.REJECTED, DistributorTransferRequest.Status.CANCELLED]:
            return Response({'success': False, 'error': 'This transfer request can no longer be confirmed.'}, status=status.HTTP_400_BAD_REQUEST)

        serializer = DistributorTransferOldOwnerConsentSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        accepted_term_ids = set(str(term_id) for term_id in serializer.validated_data.get('accepted_term_ids', []))
        applicable_terms = list(_current_applicable_terms())

        missing_mandatory_terms = [
            term.title
            for term in applicable_terms
            if term.mandatory and str(term.id) not in accepted_term_ids
        ]
        if missing_mandatory_terms:
            return Response({'success': False, 'error': 'All mandatory transfer terms must be accepted.'}, status=status.HTTP_400_BAD_REQUEST)

        for term in applicable_terms:
            accepted = str(term.id) in accepted_term_ids
            DistributorTransferAcceptance.objects.update_or_create(
                transfer_request=transfer,
                terms=term,
                accepted_by=request.user,
                defaults={
                    'accepted': accepted,
                    'accepted_at': timezone.now() if accepted else None,
                },
            )

        transfer.status = DistributorTransferRequest.Status.OLD_OWNER_CONFIRMED
        transfer.save(update_fields=['status', 'updated_at'])
        return Response({'success': True, 'data': DistributorTransferRequestSerializer(transfer).data})


class AdminTransferRequestListView(APIView):
    permission_classes = [IsAdminUser]

    def get(self, request):
        qs = DistributorTransferRequest.objects.exclude(
            status=DistributorTransferRequest.Status.CANCELLED,
        ).select_related(
            'target_distributor', 'old_owner_user', 'requested_new_owner_user', 'reviewed_by'
        ).order_by('-created_at')
        return Response({'success': True, 'data': DistributorTransferRequestSerializer(qs, many=True).data})


class AdminTransferRequestActionView(APIView):
    permission_classes = [IsAdminUser]

    def post(self, request, transfer_id):
        transfer = DistributorTransferRequest.objects.filter(id=transfer_id).select_related('target_distributor').first()
        if not transfer:
            return Response({'success': False, 'error': 'Transfer request not found.'}, status=status.HTTP_404_NOT_FOUND)

        serializer = DistributorTransferRequestAdminActionSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        action = serializer.validated_data['action']
        notes = serializer.validated_data.get('admin_notes', '')

        if transfer.status in [DistributorTransferRequest.Status.CANCELLED, DistributorTransferRequest.Status.APPROVED]:
            return Response({'success': False, 'error': 'This request is closed and cannot be actioned.'}, status=status.HTTP_400_BAD_REQUEST)

        if action == 'approve':
            if transfer.status != DistributorTransferRequest.Status.OLD_OWNER_CONFIRMED:
                return Response({'success': False, 'error': 'Old owner must confirm transfer terms before approval.'}, status=status.HTTP_400_BAD_REQUEST)

            if not _has_mandatory_acceptance(transfer, transfer.requested_new_owner_user):
                return Response({'success': False, 'error': 'New owner has not accepted all mandatory terms.'}, status=status.HTTP_400_BAD_REQUEST)

            if not _has_mandatory_acceptance(transfer, transfer.old_owner_user):
                return Response({'success': False, 'error': 'Old owner has not accepted all mandatory terms.'}, status=status.HTTP_400_BAD_REQUEST)

            history = approve_transfer_request(transfer_request=transfer, approved_by=request.user, notes=notes)
            return Response({'success': True, 'data': {'transfer_id': str(transfer.id), 'history_id': str(history.id), 'status': transfer.status}})

        if not str(notes or '').strip():
            return Response({'success': False, 'error': 'Admin rejection comment is required.'}, status=status.HTTP_400_BAD_REQUEST)

        reject_transfer_request(transfer_request=transfer, reviewed_by=request.user, notes=notes)
        return Response({'success': True, 'data': {'transfer_id': str(transfer.id), 'status': transfer.status}})


class UserAutoIDAllocationListView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        qs = AutoIDAllocation.objects.filter(owner_user=request.user).select_related(
            'source_distributor', 'eligible_product'
        ).order_by('-created_at')
        return Response({'success': True, 'data': AutoIDAllocationSerializer(qs, many=True).data})


class UserAutoIDActivateView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        serializer = AutoIDActivateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        allocation = AutoIDAllocation.objects.filter(
            id=serializer.validated_data['allocation_id'],
            owner_user=request.user,
            is_active=True,
        ).select_related('source_distributor', 'eligible_product').first()
        if not allocation:
            return Response({'success': False, 'error': 'Allocation not found.'}, status=status.HTTP_404_NOT_FOUND)

        product = None
        product_id = serializer.validated_data.get('product_id')
        if product_id:
            product = Product.objects.filter(id=product_id, is_active=True).first()
            if not product:
                return Response({'success': False, 'error': 'Product not found.'}, status=status.HTTP_404_NOT_FOUND)

        try:
            result = activate_auto_ids(
                user=request.user,
                allocation=allocation,
                count=serializer.validated_data['count'],
                product=product,
            )
        except Exception as exc:
            return Response({'success': False, 'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)

        return Response({'success': True, 'data': result}, status=status.HTTP_201_CREATED)
