"""
API views для реферальной системы и промокодов.
"""
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from django.db.models import Sum, Q, F
from django.utils import timezone
from decimal import Decimal

from .models import (
    ReferralLevel,
    ReferralSettings,
    UserReferralProfile,
    BonusAccount,
    ReferralEarning,
    PointsTransaction,
    BonusTransaction,
    PromoCode,
    PromoCodeUsage,
    ReferralInvitedEmail,
    PendingReferralBonus,
)
from .serializers import (
    ReferralLevelSerializer,
    UserReferralProfileSerializer,
    BonusAccountSerializer,
    ReferralEarningSerializer,
    PointsTransactionSerializer,
    BonusTransactionSerializer,
    PromoCodeSerializer,
    PromoCodeUsageSerializer,
    SetReferrerSerializer,
    PromoCodeValidationSerializer,
    ReferralSettingsSerializer
)


class ReferralViewSet(viewsets.ViewSet):
    """
    ViewSet для реферальной системы.
    """
    permission_classes = [IsAuthenticated]
    
    @action(detail=False, methods=['get'])
    def my_profile(self, request):
        """
        Получить свой реферальный профиль.
        
        GET /api/referrals/my_profile/
        """
        profile, created = UserReferralProfile.objects.get_or_create(
            user=request.user
        )
        serializer = UserReferralProfileSerializer(
            profile,
            context={'request': request}
        )
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def levels(self, request):
        """
        Получить все уровни.
        
        GET /api/referrals/levels/
        """
        levels = ReferralLevel.objects.all().order_by('level')
        serializer = ReferralLevelSerializer(levels, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def referral_settings(self, request):
        """
        Получить настройки реферальной программы.
        
        GET /api/referrals/referral_settings/
        """
        settings_obj = ReferralSettings.get_settings()
        serializer = ReferralSettingsSerializer(settings_obj)
        return Response(serializer.data)
    
    @action(detail=False, methods=['post'])
    def set_referrer(self, request):
        """
        Установить реферера по коду.
        
        POST /api/referrals/set_referrer/
        Body: {
            "referral_code": "ABC12345"
        }
        """
        serializer = SetReferrerSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        
        referral_code = serializer.validated_data['referral_code']
        
        try:
            profile = request.user.referral_profile
        except UserReferralProfile.DoesNotExist:
            return Response(
                {'error': 'Реферальный профиль не найден'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        # Проверяем что реферер еще не установлен
        if profile.referred_by:
            return Response(
                {'error': 'Реферер уже установлен'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Находим реферера
        try:
            referrer_profile = UserReferralProfile.objects.get(
                referral_code=referral_code
            )
        except UserReferralProfile.DoesNotExist:
            return Response(
                {'error': 'Неверный реферальный код'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        # Проверяем что не ссылаемся на себя
        if referrer_profile.user == request.user:
            return Response(
                {'error': 'Нельзя быть рефералом самого себя'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Защита от накрутки: email уже был приглашён ранее (бэклог)
        email_lower = request.user.email.lower().strip()
        if ReferralInvitedEmail.objects.filter(email=email_lower).exists():
            return Response(
                {'error': 'Этот email уже был приглашён ранее'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Устанавливаем реферера
        profile.referred_by = referrer_profile.user
        profile.save()

        # Добавляем email в бэклог приглашённых
        ReferralInvitedEmail.objects.create(
            email=email_lower,
            referrer=referrer_profile.user,
            referred_user=request.user,
        )

        # Обновляем счётчик рефералов (без начисления очков — очки начисляются после проверки активности)
        settings_obj = ReferralSettings.get_settings()
        referrer_profile.direct_referrals_count += 1
        referrer_profile.save(update_fields=['direct_referrals_count'])
        
        # Очки начисляются отложенно: через 30 дней при 20+ днях активности реферала или при 21 дне активности
        now = timezone.now()
        PendingReferralBonus.objects.create(
            referrer=referrer_profile.user,
            referred_user=request.user,
            referred_at=now,
            points=settings_obj.points_direct_referral,
            level=1,
            reason=f'Регистрация реферала {request.user.email}',
            status=PendingReferralBonus.STATUS_PENDING,
        )
        if referrer_profile.referred_by:
            try:
                level2_profile = referrer_profile.referred_by.referral_profile
                level2_profile.indirect_referrals_count += 1
                level2_profile.save(update_fields=['indirect_referrals_count'])
                PendingReferralBonus.objects.create(
                    referrer=referrer_profile.referred_by,
                    referred_user=request.user,
                    referred_at=now,
                    points=settings_obj.points_indirect_referral,
                    level=2,
                    reason=f'Регистрация непрямого реферала {request.user.email}',
                    status=PendingReferralBonus.STATUS_PENDING,
                )
            except (UserReferralProfile.DoesNotExist, AttributeError):
                pass

        # Уведомление рефереру: по вашей ссылке зарегистрировался новый пользователь
        referrer_user = referrer_profile.user
        new_user_name = request.user.get_full_name() or request.user.email or 'Новый пользователь'
        try:
            from apps.notifications.services import NotificationService
            NotificationService.create_notification_with_telegram(
                recipient=referrer_user,
                notification_type='system',
                title='🎉 Новый реферал',
                message=f'По вашей реферальной ссылке зарегистрировался {new_user_name}',
                priority='normal',
                action_url='/referrals',
            )
        except Exception:
            pass

        return Response({
            'success': True,
            'message': f'Реферер установлен: {referrer_profile.user.email}'
        })
    
    @action(detail=False, methods=['get'])
    def my_referrals(self, request):
        """
        Получить своих рефералов.
        
        GET /api/referrals/my_referrals/
        """
        # Прямые рефералы
        direct_referrals = UserReferralProfile.objects.filter(
            referred_by=request.user
        ).select_related('user', 'current_level', 'referred_by').only(
            'id', 'user_id', 'current_level_id', 'referred_by_id', 'total_points', 'created_at'
        )
        
        # Непрямые рефералы (рефералы рефералов)
        indirect_referrals = UserReferralProfile.objects.filter(
            referred_by__referral_profile__referred_by=request.user
        ).select_related('user', 'current_level', 'referred_by', 'referred_by__referral_profile').only(
            'id', 'user_id', 'current_level_id', 'referred_by_id', 'total_points', 'created_at'
        )
        
        return Response({
            'direct': [
                {
                    'email': r.user.email,
                    'level': r.current_level.name if r.current_level else 'Новичок',
                    'total_points': r.total_points,
                    'created_at': r.created_at
                }
                for r in direct_referrals
            ],
            'indirect': [
                {
                    'email': r.user.email,
                    'level': r.current_level.name if r.current_level else 'Новичок',
                    'total_points': r.total_points,
                    'created_at': r.created_at
                }
                for r in indirect_referrals
            ]
        })
    
    @action(detail=False, methods=['get'])
    def stats(self, request):
        """
        Получить статистику.
        
        GET /api/referrals/stats/
        """
        try:
            profile = request.user.referral_profile
            bonus_account = request.user.bonus_account
        except (UserReferralProfile.DoesNotExist, BonusAccount.DoesNotExist):
            return Response(
                {'error': 'Профиль не найден'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        # Оптимизация: один запрос для заработков по уровням
        earnings_stats = ReferralEarning.objects.filter(
            referrer=request.user
        ).values('level').annotate(
            total=Sum('earned_amount')
        )
        earnings_by_level = {item['level']: item['total'] for item in earnings_stats}
        level1_earnings = earnings_by_level.get(1, 0)
        level2_earnings = earnings_by_level.get(2, 0)
        
        return Response({
            'referral_code': profile.referral_code,
            'total_points': profile.total_points,
            'current_level': {
                'level': profile.current_level.level if profile.current_level else 1,
                'name': profile.current_level.name if profile.current_level else 'Новичок',
                'bonus_payment_percent': profile.get_max_bonus_payment_percent()
            },
            'referrals': {
                'direct': profile.direct_referrals_count,
                'indirect': profile.indirect_referrals_count,
                'total': profile.direct_referrals_count + profile.indirect_referrals_count
            },
            'earnings': {
                'level1': float(level1_earnings),
                'level2': float(level2_earnings),
                'total': float(profile.total_earned)
            },
            'bonus_account': {
                'balance': float(bonus_account.balance),
                'total_earned': float(bonus_account.total_earned),
                'total_spent': float(bonus_account.total_spent)
            }
        })


class BonusAccountViewSet(viewsets.ViewSet):
    """
    ViewSet для бонусного счета.
    """
    permission_classes = [IsAuthenticated]
    
    @action(detail=False, methods=['get'])
    def balance(self, request):
        """
        Получить баланс бонусного счета.
        
        GET /api/bonus/balance/
        """
        account, created = BonusAccount.objects.get_or_create(user=request.user)
        serializer = BonusAccountSerializer(account)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def transactions(self, request):
        """
        Получить историю транзакций.
        
        GET /api/bonus/transactions/
        """
        transactions = BonusTransaction.objects.filter(
            user=request.user
        ).select_related('user').only(
            'id', 'user_id', 'amount', 'transaction_type', 'description', 'created_at'
        ).order_by('-created_at')[:50]
        
        serializer = BonusTransactionSerializer(transactions, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def earnings(self, request):
        """
        Получить историю заработков с рефералов.
        
        GET /api/bonus/earnings/
        """
        earnings = ReferralEarning.objects.filter(
            referrer=request.user
        ).select_related('referrer', 'referred').only(
            'id', 'referrer_id', 'referred_id', 'level', 'earned_amount', 'created_at'
        ).order_by('-created_at')[:50]
        
        serializer = ReferralEarningSerializer(earnings, many=True)
        return Response(serializer.data)


class PointsViewSet(viewsets.ViewSet):
    """
    ViewSet для очков.
    """
    permission_classes = [IsAuthenticated]
    
    @action(detail=False, methods=['get'])
    def transactions(self, request):
        """
        Получить историю начисления очков.
        
        GET /api/points/transactions/
        """
        transactions = PointsTransaction.objects.filter(
            user=request.user
        ).select_related('user').only(
            'id', 'user_id', 'points', 'transaction_type', 'description', 'created_at'
        ).order_by('-created_at')[:50]
        
        serializer = PointsTransactionSerializer(transactions, many=True)
        return Response(serializer.data)


class PromoCodeViewSet(viewsets.ViewSet):
    """
    ViewSet для промокодов.
    """
    permission_classes = [IsAuthenticated]
    
    @action(detail=False, methods=['post'])
    def validate(self, request):
        """
        Проверить валидность промокода для плана.
        
        POST /api/promo/validate/
        Body: {
            "code": "PROMO123",
            "plan_id": 1
        }
        """
        serializer = PromoCodeValidationSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        
        code = serializer.validated_data['code']
        plan_id = serializer.validated_data['plan_id']
        
        try:
            promo = PromoCode.objects.get(code=code.upper())
        except PromoCode.DoesNotExist:
            return Response(
                {'error': 'Промокод не найден'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        # Проверяем валидность
        is_valid, message = promo.is_valid()
        if not is_valid:
            return Response(
                {'error': message},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Проверяем применимость к плану
        from apps.subscriptions.models import SubscriptionPlan
        try:
            plan = SubscriptionPlan.objects.get(id=plan_id)
        except SubscriptionPlan.DoesNotExist:
            return Response(
                {'error': 'План не найден'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        if not promo.can_apply_to_plan(plan):
            return Response(
                {'error': 'Промокод не применим к этому плану'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Рассчитываем скидку
        discount = promo.calculate_discount(plan.price)
        final_price = plan.price - discount
        
        return Response({
            'valid': True,
            'promo_code': {
                'code': promo.code,
                'name': promo.name,
                'description': promo.description,
                'discount_type': promo.discount_type,
                'discount_value': float(promo.discount_value)
            },
            'original_price': float(plan.price),
            'discount': float(discount),
            'final_price': float(final_price)
        })
    
    @action(detail=False, methods=['get'])
    def my_usage(self, request):
        """
        Получить историю использования промокодов.
        
        GET /api/promo/my_usage/
        """
        usages = PromoCodeUsage.objects.filter(
            user=request.user
        ).select_related('promo_code', 'user').only(
            'id', 'promo_code_id', 'user_id', 'discount_amount', 'created_at'
        ).order_by('-created_at')[:20]
        
        serializer = PromoCodeUsageSerializer(usages, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'], permission_classes=[AllowAny])
    def public(self, request):
        """
        Получить активные публичные промокоды.
        
        GET /api/promo/public/
        """
        from django.utils import timezone
        
        promos = PromoCode.objects.filter(
            is_active=True
        ).filter(
            Q(valid_until__isnull=True) | Q(valid_until__gt=timezone.now())
        ).filter(
            Q(max_uses__isnull=True) | Q(current_uses__lt=F('max_uses'))
        ).only(
            'id', 'code', 'name', 'description', 'discount_type', 'discount_value',
            'valid_from', 'valid_until', 'max_uses', 'current_uses', 'is_active'
        )[:10]
        
        serializer = PromoCodeSerializer(promos, many=True)
        return Response(serializer.data)

