"""
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 import models
from django.utils import timezone
from django.core.cache import cache
from .models import SubscriptionPlan, Subscription, Payment, PaymentHistory
from .serializers import (
    SubscriptionPlanSerializer,
    SubscriptionSerializer,
    SubscriptionCreateSerializer,
    PaymentSerializer,
    PaymentCreateSerializer,
    PaymentHistorySerializer
)
from .permissions import IsSubscriptionOwner
from apps.users.utils import format_datetime_for_user
from .services import PaymentService, SubscriptionService, PromoCodeService


class SubscriptionPlanViewSet(viewsets.ReadOnlyModelViewSet):
    """
    ViewSet для тарифных планов.
    
    list: Список тарифов
    retrieve: Детали тарифа
    """
    
    permission_classes = [AllowAny]
    serializer_class = SubscriptionPlanSerializer
    queryset = SubscriptionPlan.objects.filter(is_active=True)
    lookup_field = 'slug'
    
    def get_queryset(self):
        """Получение тарифов с кэшированием и фильтрацией по роли."""
        # Получаем роль пользователя из запроса
        user = self.request.user if hasattr(self.request, 'user') else None
        user_role = user.role if user and hasattr(user, 'role') and not user.is_anonymous else None
        
        # Формируем ключ кеша с учетом роли
        cache_key = f'subscription_plans_active_{user_role or "anonymous"}'
        queryset = cache.get(cache_key)
        
        # Всегда пересчитываем queryset, чтобы учесть изменения в target_role
        # (кеш может быть устаревшим после изменения планов в админке)
        queryset = SubscriptionPlan.objects.filter(
            is_active=True
        )
        
        # Фильтруем по роли пользователя
        if user_role:
            # Показываем планы для всех или для конкретной роли
            queryset = queryset.filter(
                models.Q(target_role='all') | models.Q(target_role=user_role)
            )
        else:
            # Для неавторизованных пользователей показываем только планы для всех
            queryset = queryset.filter(target_role='all')
        
        queryset = queryset.order_by('sort_order', 'price').prefetch_related(
            'bulk_discounts',
            'duration_discounts'
        )
        
        # Оптимизация: для списка используем only() для ограничения полей
        if self.action == 'list':
            queryset = queryset.only(
                'id', 'name', 'slug', 'description', 'price', 'price_per_student',
                'currency', 'billing_period', 'subscription_type', 'trial_days',
                'max_clients', 'max_lessons_per_month', 'max_storage_mb',
                'max_video_minutes_per_month', 'target_role', 'is_active', 'is_featured', 'sort_order'
            )
        
        # Обновляем кеш (но не полагаемся на него полностью)
        cache.set(cache_key, queryset, 300)  # Кеш на 5 минут вместо 1 часа
        
        return queryset
    
    @action(detail=False, methods=['get'])
    def featured(self, request):
        """
        Рекомендуемые тарифы.
        
        GET /api/subscriptions/plans/featured/
        """
        plans = self.get_queryset().filter(is_featured=True)
        serializer = self.get_serializer(plans, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def compare(self, request):
        """
        Сравнение тарифов.
        
        GET /api/subscriptions/plans/compare/?plans=1,2,3
        """
        plan_ids = request.query_params.get('plans', '').split(',')
        plan_ids = [int(id.strip()) for id in plan_ids if id.strip().isdigit()]
        
        if not plan_ids:
            return Response(
                {'error': 'Укажите ID тарифов для сравнения (plans=1,2,3)'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        plans = self.get_queryset().filter(id__in=plan_ids)
        
        # Оптимизация: используем exists() вместо count() для проверки
        if plans.count() != len(plan_ids):
            return Response(
                {'error': 'Некоторые тарифы не найдены'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        # Формируем данные для сравнения
        comparison_data = []
        
        # Получаем все уникальные функции из всех тарифов
        all_features = set()
        for plan in plans:
            features = plan.get_features_list()
            all_features.update(features.keys())
        
        # Для каждого тарифа формируем данные
        for plan in plans:
            features = plan.get_features_list()
            plan_data = {
                'id': plan.id,
                'name': plan.name,
                'slug': plan.slug,
                'description': plan.description,
                'price': float(plan.price),
                'price_per_student': float(plan.price_per_student) if plan.price_per_student else None,
                'currency': plan.currency,
                'billing_period': plan.billing_period,
                'subscription_type': plan.subscription_type,
                'trial_days': plan.trial_days,
                'max_clients': plan.max_clients,
                'max_lessons_per_month': plan.max_lessons_per_month,
                'max_storage_mb': plan.max_storage_mb,
                'max_video_minutes_per_month': plan.max_video_minutes_per_month,
                'is_featured': plan.is_featured,
                'features': {}
            }
            
            # Добавляем все функции (если есть в тарифе - значение, если нет - None)
            for feature_key in all_features:
                plan_data['features'][feature_key] = features.get(feature_key, None)
            
            comparison_data.append(plan_data)
        
        return Response({
            'plans': comparison_data,
            'features_list': sorted(list(all_features))
        })
    
    @action(detail=False, methods=['get'])
    def by_type(self, request):
        """
        Тарифы по типу подписки.
        
        GET /api/subscriptions/plans/by_type/?type=per_student
        GET /api/subscriptions/plans/by_type/?type=monthly
        """
        subscription_type = request.query_params.get('type')
        
        if subscription_type not in ['per_student', 'monthly']:
            return Response(
                {'error': 'Неверный тип подписки. Используйте: per_student или monthly'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        plans = self.get_queryset().filter(subscription_type=subscription_type)
        serializer = self.get_serializer(plans, many=True)
        return Response(serializer.data)


class SubscriptionViewSet(viewsets.ModelViewSet):
    """
    ViewSet для подписок.
    
    list: Мои подписки
    create: Создать подписку
    retrieve: Детали подписки
    update: Обновить подписку
    cancel: Отменить подписку
    renew: Продлить подписку
    check_feature: Проверить доступ к функции
    check_limit: Проверить лимит
    """
    
    permission_classes = [IsAuthenticated, IsSubscriptionOwner]
    
    def get_queryset(self):
        """Получение подписок пользователя."""
        queryset = Subscription.objects.filter(
            user=self.request.user
        ).select_related('plan').order_by('-created_at')
        
        # Оптимизация: для списка используем only() для ограничения полей
        if self.action == 'list':
            queryset = queryset.only(
                'id', 'user_id', 'plan_id', 'status', 'start_date', 'end_date',
                'trial_end_date', 'student_count', 'duration_days',
                'original_amount', 'discount_amount', 'final_amount',
                'created_at', 'updated_at', 'cancelled_at'
            )
        
        return queryset
    
    def get_serializer_class(self):
        """Выбор сериализатора."""
        if self.action == 'create':
            return SubscriptionCreateSerializer
        return SubscriptionSerializer
    
    def create(self, request, *args, **kwargs):
        """Создание подписки."""
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        subscription = serializer.save()
        
        response_serializer = SubscriptionSerializer(subscription)
        return Response(
            response_serializer.data,
            status=status.HTTP_201_CREATED
        )
    
    @action(detail=False, methods=['get'])
    def active(self, request):
        """
        Активная подписка. При отсутствии — 200 и null (без 404), чтобы фронт не получал ошибку.
        
        GET /api/subscriptions/subscriptions/active/
        """
        subscription = Subscription.objects.filter(
            user=request.user,
            status__in=['trial', 'active']
        ).select_related('plan', 'user').order_by('-end_date').first()
        
        if not subscription or not subscription.is_active():
            return Response(None)
        
        serializer = SubscriptionSerializer(subscription, context={'request': request})
        return Response(serializer.data)
    
    @action(detail=True, methods=['post'])
    def cancel(self, request, pk=None):
        """
        Отменить подписку.
        
        POST /api/subscriptions/subscriptions/{id}/cancel/
        """
        subscription = self.get_object()
        
        if subscription.status in ['cancelled', 'expired']:
            return Response(
                {'error': 'Подписка уже отменена или истекла'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        subscription.cancel()
        
        serializer = SubscriptionSerializer(subscription, context={'request': request})
        return Response(serializer.data)
    
    @action(detail=True, methods=['post'])
    def renew(self, request, pk=None):
        """
        Продлить подписку.
        
        POST /api/subscriptions/subscriptions/{id}/renew/
        """
        subscription = self.get_object()
        
        if not subscription.is_active():
            return Response(
                {'error': 'Подписка неактивна'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        subscription.renew()
        
        serializer = SubscriptionSerializer(subscription, context={'request': request})
        return Response(serializer.data)
    
    @action(detail=False, methods=['post'])
    def activate_free(self, request):
        """
        Активировать бесплатный тариф (цена плана = 0). Платёжный запрос не создаётся.
        
        POST /api/subscriptions/subscriptions/activate_free/
        Body: {
            "plan_id": 1,
            "duration_days": 30,   // опционально
            "student_count": 1     // опционально, для тарифа "за ученика"
        }
        """
        from datetime import timedelta
        from decimal import Decimal
        
        plan_id = request.data.get('plan_id')
        if not plan_id:
            return Response(
                {'error': 'Требуется plan_id'},
                status=status.HTTP_400_BAD_REQUEST
            )
        try:
            plan = SubscriptionPlan.objects.get(id=plan_id, is_active=True)
        except SubscriptionPlan.DoesNotExist:
            return Response(
                {'error': 'Тариф не найден'},
                status=status.HTTP_404_NOT_FOUND
            )
        # Только для тарифов с нулевой ценой (monthly: price=0, per_student: price_per_student=0)
        base_price = Decimal(str(plan.price if plan.price is not None else 0))
        price_per_student = Decimal(str(plan.price_per_student if getattr(plan, 'price_per_student', None) is not None else 0))
        st = getattr(plan, 'subscription_type', None) or 'monthly'
        is_free = (st == 'per_student' and price_per_student == Decimal('0')) or (st != 'per_student' and base_price == Decimal('0'))
        if not is_free:
            return Response(
                {'error': 'Активация без оплаты доступна только для бесплатных тарифов (цена 0)'},
                status=status.HTTP_400_BAD_REQUEST
            )
        duration_days = int(request.data.get('duration_days', 30))
        student_count = int(request.data.get('student_count', 1)) if st == 'per_student' else 0
        if st == 'per_student' and student_count <= 0:
            student_count = 1
        available = plan.get_available_durations() if hasattr(plan, 'get_available_durations') else [30]
        if not available:
            available = [30]
        if duration_days not in available:
            duration_days = available[0]
        try:
            subscription = SubscriptionService.create_subscription(
                user=request.user,
                plan=plan,
                student_count=student_count,
                duration_days=duration_days,
                start_date=timezone.now(),
                promo_code=None
            )
        except Exception as e:
            import logging
            logging.getLogger(__name__).exception("activate_free create_subscription failed")
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )
        subscription.status = 'active'
        subscription.save(update_fields=['status'])
        serializer = SubscriptionSerializer(subscription, context={'request': request})
        return Response({
            'success': True,
            'message': 'Подписка активирована',
            'subscription': serializer.data,
        }, status=status.HTTP_201_CREATED)

    @action(detail=False, methods=['post'])
    def check_feature(self, request):
        """
        Проверить доступ к функции.
        
        POST /api/subscriptions/subscriptions/check_feature/
        Body: {
            "feature": "video_calls"
        }
        """
        feature = request.data.get('feature')
        
        if not feature:
            return Response(
                {'error': 'Укажите feature'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Получаем активную подписку с оптимизацией
        subscription = Subscription.objects.filter(
            user=request.user,
            status__in=['trial', 'active']
        ).select_related('plan').order_by('-end_date').first()
        
        if not subscription:
            return Response({
                'has_access': False,
                'reason': 'Нет активной подписки'
            })
        
        has_access = subscription.has_feature(feature)
        
        return Response({
            'has_access': has_access,
            'subscription_id': subscription.id,
            'plan': subscription.plan.name
        })
    
    @action(detail=False, methods=['post'])
    def check_limit(self, request):
        """
        Проверить лимит.
        
        POST /api/subscriptions/subscriptions/check_limit/
        Body: {
            "limit_type": "lessons"
        }
        """
        limit_type = request.data.get('limit_type')
        
        if not limit_type:
            return Response(
                {'error': 'Укажите limit_type'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Получаем активную подписку с оптимизацией
        subscription = Subscription.objects.filter(
            user=request.user,
            status__in=['trial', 'active']
        ).select_related('plan').order_by('-end_date').first()
        
        if not subscription:
            return Response({
                'within_limit': False,
                'reason': 'Нет активной подписки'
            })
        
        within_limit = subscription.check_limit(limit_type)
        
        return Response({
            'within_limit': within_limit,
            'subscription_id': subscription.id,
            'plan': subscription.plan.name
        })
    
    @action(detail=True, methods=['post'])
    def change_plan(self, request, pk=None):
        """
        Сменить тарифный план подписки.
        
        POST /api/subscriptions/subscriptions/{id}/change_plan/
        Body: {
            "plan_id": 1,
            "student_count": 5,  # опционально, для типа per_student
            "duration_days": 90,  # опционально
            "promo_code": "PROMO123"  # опционально
        }
        """
        subscription = self.get_object()
        
        plan_id = request.data.get('plan_id')
        if not plan_id:
            return Response(
                {'error': 'Требуется plan_id'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        try:
            new_plan = SubscriptionPlan.objects.get(id=plan_id, is_active=True)
        except SubscriptionPlan.DoesNotExist:
            return Response(
                {'error': 'Тарифный план не найден'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        student_count = request.data.get('student_count')
        duration_days = request.data.get('duration_days')
        promo_code_str = request.data.get('promo_code')
        
        # Валидация и применение промокода
        promo_code = None
        if promo_code_str:
            promo_result = PromoCodeService.validate_promo_code(promo_code_str, request.user)
            if not promo_result['valid']:
                return Response(
                    {'error': f'Промокод невалиден: {promo_result["error"]}'},
                    status=status.HTTP_400_BAD_REQUEST
                )
            promo_code = promo_result['promo_code']
        
        try:
            # Меняем тариф
            updated_subscription = SubscriptionService.change_plan(
                subscription=subscription,
                new_plan=new_plan,
                student_count=student_count,
                duration_days=duration_days,
                promo_code=promo_code
            )
            
            serializer = SubscriptionSerializer(updated_subscription)
            return Response(serializer.data)
        
        except ValueError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"Error changing plan: {e}")
            return Response(
                {'error': 'Ошибка при смене тарифа'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class PaymentViewSet(viewsets.ModelViewSet):
    """
    ViewSet для платежей.
    
    list: Мои платежи
    create: Создать платеж
    retrieve: Детали платежа
    history: История изменений
    """
    
    permission_classes = [IsAuthenticated]
    
    def get_queryset(self):
        """Получение платежей пользователя."""
        queryset = Payment.objects.filter(
            user=self.request.user
        ).select_related('subscription', 'subscription__plan').order_by('-created_at')
        
        # Оптимизация: для списка используем only() для ограничения полей
        if self.action == 'list':
            queryset = queryset.only(
                'id', 'user_id', 'subscription_id', 'amount', 'currency', 'status',
                'payment_method', 'description', 'external_id', 'created_at', 'paid_at'
            )
        
        return queryset
    
    def get_serializer_class(self):
        """Выбор сериализатора."""
        if self.action == 'create':
            return PaymentCreateSerializer
        return PaymentSerializer
    
    def create(self, request, *args, **kwargs):
        """Создание платежа."""
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        payment = serializer.save()
        
        # Инициализация платежа через платежную систему
        payment_service = PaymentService()
        payment_data = payment_service.create_payment(
            payment=payment,
            return_url=request.data.get('return_url')
        )
        
        response_serializer = PaymentSerializer(payment)
        data = response_serializer.data
        data['payment_url'] = payment_data.get('payment_url')
        
        return Response(data, status=status.HTTP_201_CREATED)
    
    @action(detail=True, methods=['get'])
    def changes(self, request, pk=None):
        """
        История изменений платежа.
        
        GET /api/subscriptions/payments/{id}/changes/
        """
        payment = self.get_object()
        history = payment.history.all()
        serializer = PaymentHistorySerializer(history, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def history(self, request):
        """
        История платежей пользователя.
        
        GET /api/subscriptions/payments/history/
        """
        # Оптимизация: используем select_related для subscription и plan
        payments = self.get_queryset().select_related('subscription', 'subscription__plan')
        
        return Response({
            'payments': [
                {
                    'id': p.id,
                    'amount': float(p.amount),
                    'currency': p.currency,
                    'status': p.status,
                    'payment_method': p.payment_method,
                    'description': p.description,
                    'plan_name': p.subscription.plan.name if p.subscription and p.subscription.plan else None,
                    'created_at': format_datetime_for_user(p.created_at, request.user.timezone) if p.created_at else None,
                    'paid_at': format_datetime_for_user(p.paid_at, request.user.timezone) if p.paid_at else None,
                }
                for p in payments
            ]
        })
    
    @action(detail=False, methods=['get'])
    def successful(self, request):
        """
        Успешные платежи.
        
        GET /api/subscriptions/payments/successful/
        """
        payments = self.get_queryset().filter(status='succeeded')
        serializer = PaymentSerializer(payments, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def pending(self, request):
        """
        Ожидающие платежи.
        
        GET /api/subscriptions/payments/pending/
        """
        payments = self.get_queryset().filter(status='pending')
        serializer = PaymentSerializer(payments, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['post'])
    def create_extra_payment(self, request):
        """
        Создать дополнительный платеж (например, за дополнительных учеников).
        
        POST /api/subscriptions/payments/create_extra_payment/
        Body: {
            "amount": 500.00,
            "extra_students": 2,
            "description": "Доплата за 2 дополнительных учеников"
        }
        """
        from .yookassa_service import yookassa_service
        from django.conf import settings
        from decimal import Decimal
        import logging
        import uuid
        
        logger = logging.getLogger(__name__)
        
        amount = request.data.get('amount')
        extra_students = request.data.get('extra_students', 0)
        description = request.data.get('description', 'Дополнительный платеж')
        return_url = request.data.get('return_url') or f"{settings.FRONTEND_URL}/profile/payments"
        
        if not amount:
            return Response(
                {'error': 'Требуется amount'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        try:
            amount = Decimal(str(amount))
            
            if amount <= 0:
                return Response(
                    {'error': 'Сумма должна быть больше 0'},
                    status=status.HTTP_400_BAD_REQUEST
                )
            
            # Создаем платеж в БД
            payment = Payment.objects.create(
                user=request.user,
                amount=amount,
                currency='RUB',
                status='pending',
                provider='yookassa',
                payment_type='extra',
                description=description,
                metadata={
                    'extra_students': extra_students,
                    'type': 'extra_payment'
                }
            )
            
            # Создаем платеж в YooKassa
            yookassa_payment = yookassa_service.create_payment(
                amount=float(amount),
                currency='RUB',
                description=description,
                return_url=return_url,
                metadata={
                    'payment_id': str(payment.id),
                    'user_id': str(request.user.id),
                    'extra_students': extra_students,
                    'type': 'extra_payment'
                }
            )
            
            if yookassa_payment:
                # Обновляем платеж
                payment.external_id = yookassa_payment.id
                payment.provider_response = {
                    'yookassa_payment': yookassa_payment.id,
                    'status': yookassa_payment.status,
                    'created_at': str(yookassa_payment.created_at)
                }
                payment.save()
                
                # Получаем URL для оплаты
                confirmation_url = yookassa_payment.confirmation.confirmation_url
                
                logger.info(f"Extra payment created: {payment.id}, YooKassa ID: {yookassa_payment.id}")
                
                return Response({
                    'success': True,
                    'payment_id': payment.id,
                    'confirmation_url': confirmation_url,
                    'amount': float(amount),
                    'currency': 'RUB'
                }, status=status.HTTP_201_CREATED)
            else:
                payment.status = 'failed'
                payment.save()
                return Response(
                    {'error': 'Не удалось создать платеж в YooKassa'},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )
        
        except Exception as e:
            logger.error(f"Error creating extra payment: {e}", exc_info=True)
            return Response(
                {'error': f'Ошибка при создании платежа: {str(e)}'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    @action(detail=False, methods=['post'])
    def create_payment(self, request):
        """
        Создать платеж для подписки через ЮKassa с поддержкой промокодов и бонусов.
        
        POST /api/subscriptions/payments/create_payment/
        Body: {
            "plan_id": 1,
            "student_count": 5,  # опционально, для тарифов "за ученика"
            "duration_days": 90,  # опционально, период оплаты (30, 90, 180, 365)
            "subscription_id": 1,  # опционально, для смены тарифа или продления
            "promo_code": "PROMO123",  # опционально
            "use_bonus": 50.00,  # опционально, сумма бонусов для использования
            "return_url": "http://localhost:3000/payment/success"
        }
        """
        from .yookassa_service import yookassa_service
        from django.conf import settings
        from decimal import Decimal
        import logging
        
        logger = logging.getLogger(__name__)
        
        plan_id = request.data.get('plan_id')
        student_count = request.data.get('student_count')
        duration_days = request.data.get('duration_days')
        subscription_id = request.data.get('subscription_id')
        promo_code = request.data.get('promo_code')
        use_bonus = request.data.get('use_bonus', 0)
        return_url = request.data.get('return_url') or f"{settings.FRONTEND_URL}/payment/success"
        
        if not plan_id:
            return Response(
                {'error': 'Требуется plan_id'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        try:
            # Получаем план подписки с оптимизацией
            plan = SubscriptionPlan.objects.select_related().get(id=plan_id, is_active=True)
            
            # Проверяем доступность тарифа (акция)
            can_use, error_message = plan.can_be_used(request.user)
            if not can_use:
                return Response(
                    {'error': error_message},
                    status=status.HTTP_400_BAD_REQUEST
                )
            
            # Определяем длительность
            if duration_days:
                duration_days = int(duration_days)
                # Проверяем доступность периода
                if not plan.is_duration_available(duration_days):
                    available = plan.get_available_durations()
                    return Response(
                        {'error': f'Период {duration_days} дней недоступен для этого тарифа. Доступные периоды: {", ".join(map(str, available))}'},
                        status=status.HTTP_400_BAD_REQUEST
                    )
            else:
                # По умолчанию используем первый доступный период или 30 дней
                available_durations = plan.get_available_durations()
                duration_days = available_durations[0] if available_durations else 30
            
            # Определяем количество учеников для тарифов "за ученика"
            if plan.subscription_type == 'per_student':
                if student_count is None or student_count <= 0:
                    return Response(
                        {'error': 'Для тарифа "За ученика" необходимо указать количество учеников'},
                        status=status.HTTP_400_BAD_REQUEST
                    )
                student_count = int(student_count)
            else:
                student_count = 0
            
            # Валидация промокода если указан
            promo_obj = None
            if promo_code:
                from apps.referrals.models import PromoCode, PromoCodeUsage
                
                try:
                    promo_obj = PromoCode.objects.get(code=promo_code.upper())
                    
                    # Проверяем валидность
                    is_valid, message = promo_obj.is_valid()
                    if not is_valid:
                        return Response(
                            {'error': f'Промокод невалиден: {message}'},
                            status=status.HTTP_400_BAD_REQUEST
                        )
                    
                    # Проверяем применимость
                    if not promo_obj.can_apply_to_plan(plan):
                        return Response(
                            {'error': 'Промокод не применим к этому плану'},
                            status=status.HTTP_400_BAD_REQUEST
                        )
                except PromoCode.DoesNotExist:
                    return Response(
                        {'error': 'Промокод не найден'},
                        status=status.HTTP_404_NOT_FOUND
                    )
            
            # Рассчитываем цену через сервис (с учетом промокода)
            price_data = SubscriptionService.calculate_subscription_price(
                plan=plan,
                student_count=student_count,
                duration_days=duration_days,
                promo_code=promo_obj
            )
            
            original_price = price_data['original_amount']
            final_price = price_data['final_amount']
            discount_info = {}
            
            # Добавляем информацию о скидке за длительность
            if 'duration_discount' in price_data:
                discount_info['duration'] = price_data['duration_discount']
            
            # Добавляем информацию о промокоде
            if promo_obj:
                discount_info['promo'] = {
                    'code': promo_obj.code,
                    'discount': float(price_data['discount_amount'])
                }
            
            # Применяем бонусы если указаны
            bonus_used = Decimal('0')
            bonus_account = None
            if use_bonus and Decimal(str(use_bonus)) > 0:
                from apps.referrals.models import BonusAccount, UserReferralProfile
                
                try:
                    bonus_account = request.user.bonus_account
                    referral_profile = request.user.referral_profile
                    
                    use_bonus = Decimal(str(use_bonus))
                    
                    # Проверяем баланс
                    if use_bonus > bonus_account.balance:
                        return Response(
                            {'error': 'Недостаточно бонусов на счете'},
                            status=status.HTTP_400_BAD_REQUEST
                        )
                    
                    # Проверяем лимит использования бонусов
                    max_bonus_percent = referral_profile.get_max_bonus_payment_percent()
                    max_bonus_amount = original_price * (Decimal(str(max_bonus_percent)) / 100)
                    
                    if use_bonus > max_bonus_amount:
                        return Response(
                            {'error': f'Максимум {max_bonus_percent}% ({float(max_bonus_amount)} ₽) можно оплатить бонусами'},
                            status=status.HTTP_400_BAD_REQUEST
                        )
                    
                    # Проверяем что не превышаем итоговую цену
                    if use_bonus > final_price:
                        use_bonus = final_price
                    
                    bonus_used = use_bonus
                    final_price -= bonus_used
                    
                    discount_info['bonus'] = {
                        'used': float(bonus_used),
                        'remaining': float(bonus_account.balance - bonus_used)
                    }
                    
                except (BonusAccount.DoesNotExist, UserReferralProfile.DoesNotExist):
                    return Response(
                        {'error': 'Бонусный счет не найден'},
                        status=status.HTTP_404_NOT_FOUND
                    )
            
            # Проверяем, нужно ли сменить тариф
            subscription = None
            if subscription_id:
                try:
                    subscription = Subscription.objects.get(id=subscription_id, user=request.user)
                    # Если план отличается, меняем тариф
                    if subscription.plan.id != plan.id:
                        subscription = SubscriptionService.change_plan(
                            subscription=subscription,
                            new_plan=plan,
                            student_count=student_count if plan.subscription_type == 'per_student' else None,
                            duration_days=duration_days,
                            promo_code=promo_obj if promo_obj else None
                        )
                        # После смены тарифа цена уже рассчитана, используем её
                        original_price = float(subscription.original_amount)
                        final_price = float(subscription.final_amount)
                except Subscription.DoesNotExist:
                    return Response(
                        {'error': 'Подписка не найдена'},
                        status=status.HTTP_404_NOT_FOUND
                    )
            
            # Если итоговая цена = 0, активируем подписку без оплаты
            if final_price <= 0:
                if not subscription:
                    subscription = SubscriptionService.create_subscription(
                    user=request.user,
                    plan=plan,
                        student_count=student_count,
                        duration_days=duration_days,
                        promo_code=promo_obj if promo_obj else None
                )
                
                # Списываем бонусы
                if bonus_used > 0 and bonus_account:
                    bonus_account.spend_bonus(
                        bonus_used,
                        reason=f'Оплата подписки {plan.name}'
                    )
                
                # Отмечаем использование промокода
                if promo_obj:
                    from apps.referrals.models import PromoCodeUsage
                    promo_obj.use()
                    PromoCodeUsage.objects.create(
                        user=request.user,
                        promo_code=promo_obj,
                        original_amount=original_price,
                        discount_amount=discount_info.get('promo', {}).get('discount', 0),
                        final_amount=0
                    )
                
                return Response({
                    'success': True,
                    'free_activation': True,
                    'message': 'Подписка активирована бесплатно',
                    'original_price': float(original_price),
                    'discount_info': discount_info,
                    'final_price': 0
                })
            
            # Создаем или обновляем подписку
            if not subscription:
                subscription = SubscriptionService.create_subscription(
                user=request.user,
                plan=plan,
                    student_count=student_count,
                    duration_days=duration_days,
                    promo_code=promo_obj if promo_obj else None
                )
            else:
                # Обновляем подписку с новой длительностью и количеством учеников
                subscription.duration_days = duration_days
                if plan.subscription_type == 'per_student':
                    subscription.student_count = student_count
                subscription.original_amount = original_price
                subscription.discount_amount = price_data.get('discount_amount', 0)
                subscription.final_amount = final_price
                subscription.save()
            
            # Создаем платеж в ЮKassa
            yookassa_payment = yookassa_service.create_payment(
                amount=final_price,
                description=f"Оплата подписки: {plan.name} ({duration_days} дней)" + (f", {student_count} учеников" if student_count > 0 else ""),
                return_url=return_url,
                metadata={
                    'subscription_id': subscription.id,
                    'user_id': request.user.id,
                    'plan_id': plan.id,
                    'duration_days': duration_days,
                    'student_count': student_count if plan.subscription_type == 'per_student' else None,
                    'promo_code': promo_code if promo_code else None,
                    'bonus_used': float(bonus_used) if bonus_used > 0 else None,
                }
            )
            
            # Сохраняем платеж в БД
            payment = Payment.objects.create(
                user=request.user,
                subscription=subscription,
                amount=final_price,
                currency='RUB',
                status='pending',
                payment_method='yookassa',
                external_id=yookassa_payment['id'],
                description=f"Оплата подписки: {plan.name}",
                provider_response={
                    'confirmation_url': yookassa_payment['confirmation_url'],
                    'yookassa_status': yookassa_payment['status'],
                    'metadata': yookassa_payment['metadata'],
                    'original_price': float(original_price),
                    'discount_info': discount_info,
                },
            )
            
            # Списываем бонусы (будут возвращены если платеж отменится)
            if bonus_used > 0:
                bonus_account.spend_bonus(
                    bonus_used,
                    reason=f'Оплата подписки {plan.name} (платеж #{payment.id})'
                )
            
            # Отмечаем использование промокода
            if promo_obj:
                from apps.referrals.models import PromoCodeUsage
                promo_obj.use()
                PromoCodeUsage.objects.create(
                    user=request.user,
                    promo_code=promo_obj,
                    payment=payment,
                    original_amount=original_price,
                    discount_amount=discount_info.get('promo', {}).get('discount', 0),
                    final_amount=final_price
                )
            
            logger.info(f"Payment created: {payment.id} for user {request.user.id}, price: {final_price}")
            
            return Response({
                'success': True,
                'payment_id': payment.id,
                'external_id': payment.external_id,
                'confirmation_url': yookassa_payment['confirmation_url'],
                'original_price': float(original_price),
                'discount_info': discount_info,
                'final_price': float(final_price),
            }, status=status.HTTP_201_CREATED)
        
        except SubscriptionPlan.DoesNotExist:
            return Response(
                {'error': 'План подписки не найден'},
                status=status.HTTP_404_NOT_FOUND
            )
        except Exception as e:
            logger.error(f"Error creating payment: {e}")
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    @action(detail=True, methods=['get'])
    def check_status(self, request, pk=None):
        """
        Проверить статус платежа в ЮKassa.
        
        GET /api/subscriptions/payments/{id}/check_status/
        """
        from .yookassa_service import yookassa_service
        import logging
        
        logger = logging.getLogger(__name__)
        
        try:
            # Оптимизация: используем select_related для subscription и plan
            payment = Payment.objects.select_related('subscription', 'subscription__plan').get(pk=pk)
        except Payment.DoesNotExist:
            return Response(
                {'error': 'Платеж не найден'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        try:
            # Получаем актуальный статус из ЮKassa
            yookassa_payment = yookassa_service.get_payment(payment.external_id)
            
            # Обновляем статус в БД
            old_status = payment.status
            yookassa_status = yookassa_payment['status']
            
            if yookassa_status == 'succeeded' and old_status != 'succeeded':
                payment.status = 'succeeded'
                payment.paid_at = timezone.now()
                payment.provider_response['payment_method'] = yookassa_payment.get('payment_method')
                payment.save()
                
                # Активируем подписку
                if payment.subscription:
                    payment.subscription.status = 'active'
                    payment.subscription.start_date = timezone.now()
                    payment.subscription.save()
            
            elif yookassa_status == 'canceled':
                payment.status = 'cancelled'
                payment.save()
            
            return Response({
                'id': payment.id,
                'status': payment.status,
                'external_id': payment.external_id,
                'amount': float(payment.amount),
                'created_at': format_datetime_for_user(payment.created_at, request.user.timezone) if payment.created_at else None,
                'paid_at': format_datetime_for_user(payment.paid_at, request.user.timezone) if payment.paid_at else None,
            })
        
        except Exception as e:
            logger.error(f"Error checking payment status: {e}")
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class WebhookViewSet(viewsets.ViewSet):
    """
    ViewSet для webhook от платежных систем.
    
    yookassa: Webhook от ЮKassa
    stripe: Webhook от Stripe
    """
    
    permission_classes = [AllowAny]
    
    @action(detail=False, methods=['post'])
    def yookassa(self, request):
        """
        Webhook от ЮKassa.
        
        POST /api/subscriptions/webhooks/yookassa/
        """
        import logging
        
        logger = logging.getLogger(__name__)
        
        try:
            # Получаем данные из запроса
            data = request.data
            event_type = data.get('event')
            payment_object = data.get('object', {})
            payment_id = payment_object.get('id')
            
            if not payment_id:
                return Response(
                    {'error': 'Payment ID not found'},
                    status=status.HTTP_400_BAD_REQUEST
                )
            
            # Находим платеж в БД с оптимизацией
            try:
                payment = Payment.objects.select_related('subscription', 'subscription__plan', 'user').get(external_id=payment_id)
            except Payment.DoesNotExist:
                logger.warning(f"Payment {payment_id} not found in database")
                return Response({'status': 'ok'}, status=status.HTTP_200_OK)
            
            # Обрабатываем событие
            if event_type == 'payment.succeeded':
                payment_method = payment_object.get('payment_method', {}).get('type')
                payment.status = 'succeeded'
                payment.paid_at = timezone.now()
                payment.provider_response['payment_method'] = payment_method
                payment.provider_response['webhook_data'] = data
                payment.save()
                
                # Активируем подписку
                if payment.subscription:
                    payment.subscription.status = 'active'
                    payment.subscription.start_date = timezone.now()
                    payment.subscription.save()
                
                logger.info(f"Payment {payment_id} succeeded")
            
            elif event_type == 'payment.canceled':
                cancellation_details = payment_object.get('cancellation_details', {})
                reason = cancellation_details.get('reason', '')
                payment.status = 'cancelled'
                payment.provider_response['cancellation_reason'] = reason
                payment.provider_response['webhook_data'] = data
                payment.save()
                logger.info(f"Payment {payment_id} canceled: {reason}")
            
            elif event_type == 'payment.waiting_for_capture':
                payment.status = 'processing'
                payment.save()
            
            return Response({'status': 'ok'}, status=status.HTTP_200_OK)
        
        except Exception as e:
            logger.error(f"Error processing webhook: {e}")
            return Response(
                {'error': str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
    
    @action(detail=False, methods=['post'])
    def stripe(self, request):
        """
        Webhook от Stripe.
        
        POST /api/subscriptions/webhooks/stripe/
        """
        payment_service = PaymentService()
        result = payment_service.process_stripe_webhook(request.data)
        
        if result['success']:
            return Response({'status': 'ok'})
        else:
            return Response(
                {'error': result.get('error')},
                status=status.HTTP_400_BAD_REQUEST
            )
