"""
Модели для подписок и платежей.
"""
from django.db import models
from django.core.validators import MinValueValidator
from django.utils import timezone
from datetime import timedelta
from decimal import Decimal
import uuid


class BulkDiscount(models.Model):
    """
    Модель прогрессирующих скидок для подписки "за ученика".
    
    Пример:
    - 1-4 ученика: 100 руб за каждого
    - 5-9 учеников: 420 руб за 5 (84 руб за каждого)
    - 10+ учеников: 850 руб за 10 (85 руб за каждого)
    """
    
    plan = models.ForeignKey(
        'SubscriptionPlan',
        on_delete=models.CASCADE,
        related_name='bulk_discounts',
        verbose_name='Тарифный план'
    )
    
    min_students = models.IntegerField(
        validators=[MinValueValidator(1)],
        verbose_name='Минимальное количество учеников',
        help_text='От какого количества учеников действует эта цена'
    )
    
    max_students = models.IntegerField(
        null=True,
        blank=True,
        validators=[MinValueValidator(1)],
        verbose_name='Максимальное количество учеников',
        help_text='До какого количества учеников действует эта цена. Если не указано - без ограничений'
    )
    
    price_per_student = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Цена за ученика',
        help_text='Цена за одного ученика в этом диапазоне'
    )
    
    total_price = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Итоговая цена',
        help_text='Автоматически рассчитывается как: цена за ученика × минимальное количество учеников',
        editable=False
    )
    
    # Временные метки
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Дата создания'
    )
    
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name='Дата обновления'
    )
    
    class Meta:
        db_table = 'bulk_discounts'
        verbose_name = 'Прогрессирующая скидка'
        verbose_name_plural = 'Прогрессирующие скидки'
        ordering = ['plan', 'min_students']
        indexes = [
            models.Index(fields=['plan', 'min_students']),
        ]
        constraints = [
            models.CheckConstraint(
                check=models.Q(max_students__isnull=True) | models.Q(max_students__gte=models.F('min_students')),
                name='max_students_gte_min_students'
            )
        ]
    
    def __str__(self):
        if self.max_students:
            return f"{self.plan.name}: {self.min_students}-{self.max_students} учеников = {self.price_per_student} руб/ученик"
        else:
            return f"{self.plan.name}: {self.min_students}+ учеников = {self.price_per_student} руб/ученик"
    
    def save(self, *args, **kwargs):
        """Автоматически рассчитываем total_price из price_per_student × min_students."""
        if self.price_per_student and self.min_students:
            self.total_price = self.price_per_student * Decimal(str(self.min_students))
        super().save(*args, **kwargs)
    
    def matches(self, student_count):
        """
        Проверить, подходит ли эта скидка для указанного количества учеников.
        
        Args:
            student_count: количество учеников
        
        Returns:
            bool
        """
        if student_count < self.min_students:
            return False
        
        if self.max_students is None:
            return True
        
        return student_count <= self.max_students


class DurationDiscount(models.Model):
    """
    Модель скидок за длительность подписки.
    
    ВАЖНО: Периоды, для которых указаны скидки, автоматически становятся доступными для оплаты.
    Если скидок нет, доступны все стандартные периоды (30, 90, 180, 365 дней).
    
    Пример:
    - 3 месяца (90 дней): 7% скидка
    - 6 месяцев (180 дней): 12% скидка
    - 12 месяцев (365 дней): 18% скидка
    
    Применяется к ежемесячным тарифам (subscription_type='monthly').
    Для тарифов "За ученика" (per_student) скидки не применяются, но периоды определяют доступные варианты оплаты.
    """
    
    plan = models.ForeignKey(
        'SubscriptionPlan',
        on_delete=models.CASCADE,
        related_name='duration_discounts',
        verbose_name='Тарифный план'
    )
    
    duration_days = models.IntegerField(
        validators=[MinValueValidator(1)],
        verbose_name='Длительность в днях',
        help_text='Количество дней подписки (30, 90, 180, 365)'
    )
    
    discount_percent = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Процент скидки',
        help_text='Процент скидки за эту длительность (например, 7.00 для 7%)'
    )
    
    # Временные метки
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Дата создания'
    )
    
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name='Дата обновления'
    )
    
    class Meta:
        db_table = 'duration_discounts'
        verbose_name = 'Скидка за длительность'
        verbose_name_plural = 'Скидки за длительность'
        ordering = ['plan', 'duration_days']
        unique_together = [['plan', 'duration_days']]
        indexes = [
            models.Index(fields=['plan', 'duration_days']),
        ]
    
    def __str__(self):
        months = self.duration_days / 30
        return f"{self.plan.name}: {months:.0f} мес ({self.duration_days} дней) - {self.discount_percent}%"
    
    def matches(self, days):
        """Проверить, подходит ли эта скидка для указанной длительности."""
        return self.duration_days == days


class SubscriptionPlan(models.Model):
    """
    Модель тарифного плана.
    """
    
    BILLING_PERIOD_CHOICES = [
        ('monthly', 'Ежемесячно'),
        ('quarterly', 'Ежеквартально'),
        ('yearly', 'Ежегодно'),
        ('lifetime', 'Навсегда'),
    ]
    
    SUBSCRIPTION_TYPE_CHOICES = [
        ('per_student', 'За ученика'),
        ('monthly', 'Ежемесячная'),
    ]
    
    # Основная информация
    name = models.CharField(
        max_length=100,
        unique=True,
        verbose_name='Название'
    )
    
    slug = models.SlugField(
        max_length=100,
        unique=True,
        verbose_name='Слаг'
    )
    
    description = models.TextField(
        blank=True,
        verbose_name='Описание'
    )
    
    # Стоимость
    price = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Цена'
    )
    
    currency = models.CharField(
        max_length=3,
        default='RUB',
        verbose_name='Валюта'
    )
    
    billing_period = models.CharField(
        max_length=20,
        choices=BILLING_PERIOD_CHOICES,
        default='monthly',
        verbose_name='Период оплаты'
    )
    
    subscription_type = models.CharField(
        max_length=20,
        choices=SUBSCRIPTION_TYPE_CHOICES,
        default='monthly',
        verbose_name='Тип подписки'
    )
    
    price_per_student = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        null=True,
        blank=True,
        validators=[MinValueValidator(0)],
        verbose_name='Цена за ученика',
        help_text='Используется для типа "За ученика"'
    )
    
    trial_days = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Пробный период (дней)'
    )
    
    # Лимиты
    max_clients = models.IntegerField(
        null=True,
        blank=True,
        validators=[MinValueValidator(1)],
        verbose_name='Максимум клиентов'
    )
    
    max_lessons_per_month = models.IntegerField(
        null=True,
        blank=True,
        validators=[MinValueValidator(1)],
        verbose_name='Максимум занятий в месяц'
    )
    
    max_storage_mb = models.IntegerField(
        default=1024,  # 1 GB
        validators=[MinValueValidator(1)],
        verbose_name='Максимум хранилища (МБ)'
    )
    
    max_video_minutes_per_month = models.IntegerField(
        null=True,
        blank=True,
        validators=[MinValueValidator(1)],
        verbose_name='Максимум минут видео в месяц'
    )
    
    # Функциональность
    allow_video_calls = models.BooleanField(
        default=True,
        verbose_name='Видеозвонки'
    )
    
    allow_screen_sharing = models.BooleanField(
        default=True,
        verbose_name='Демонстрация экрана'
    )
    
    allow_whiteboard = models.BooleanField(
        default=True,
        verbose_name='Интерактивная доска'
    )
    
    allow_homework = models.BooleanField(
        default=True,
        verbose_name='Домашние задания'
    )
    
    allow_materials = models.BooleanField(
        default=True,
        verbose_name='Материалы'
    )
    
    allow_analytics = models.BooleanField(
        default=True,
        verbose_name='Аналитика'
    )
    
    allow_telegram_bot = models.BooleanField(
        default=False,
        verbose_name='Telegram бот'
    )
    
    allow_api_access = models.BooleanField(
        default=False,
        verbose_name='API доступ'
    )
    
    # Тип акции
    PROMO_TYPE_CHOICES = [
        ('none', 'Без акции'),
        ('first_time', 'Только для новых пользователей'),
        ('limited_uses', 'Ограниченное количество использований'),
    ]
    
    promo_type = models.CharField(
        max_length=20,
        choices=PROMO_TYPE_CHOICES,
        default='none',
        verbose_name='Тип акции',
        help_text='Тип акции для данного тарифа'
    )
    
    max_uses = models.IntegerField(
        null=True,
        blank=True,
        validators=[MinValueValidator(1)],
        verbose_name='Максимальное количество использований',
        help_text='Максимальное количество раз, которое можно использовать этот тариф (для типа "Ограниченное количество использований")'
    )
    
    current_uses = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Текущее количество использований',
        help_text='Сколько раз уже использован этот тариф'
    )
    
    # Целевая аудитория
    TARGET_ROLE_CHOICES = [
        ('all', 'Для всех'),
        ('mentor', 'Для менторов'),
        ('client', 'Для студентов'),
        ('parent', 'Для родителей'),
    ]
    
    target_role = models.CharField(
        max_length=20,
        choices=TARGET_ROLE_CHOICES,
        default='all',
        verbose_name='Для кого предназначена подписка',
        help_text='Роль пользователя, для которой предназначена эта подписка'
    )
    
    # Настройки
    is_active = models.BooleanField(
        default=True,
        verbose_name='Активен',
        db_index=True
    )
    
    is_featured = models.BooleanField(
        default=False,
        verbose_name='Рекомендуемый'
    )
    
    sort_order = models.IntegerField(
        default=0,
        verbose_name='Порядок сортировки'
    )
    
    # Статистика
    subscribers_count = models.IntegerField(
        default=0,
        verbose_name='Количество подписчиков'
    )
    
    # Временные метки
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Дата создания'
    )
    
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name='Дата обновления'
    )
    
    class Meta:
        db_table = 'subscription_plans'
        verbose_name = 'Тарифный план'
        verbose_name_plural = 'Тарифные планы'
        ordering = ['sort_order', 'price']
        indexes = [
            models.Index(fields=['is_active', 'sort_order']),
            models.Index(fields=['slug']),
            models.Index(fields=['subscription_type', 'is_active']),
        ]
    
    def __str__(self):
        return f"{self.name} - {self.price} {self.currency}/{self.get_billing_period_display()}"
    
    def get_duration_days(self, custom_days=None):
        """
        Получить длительность подписки в днях.
        
        Args:
            custom_days: кастомная длительность в днях (30, 90, 180, 365)
        
        Returns:
            int: количество дней
        """
        if custom_days:
            return custom_days
        
        if self.billing_period == 'monthly':
            return 30
        elif self.billing_period == 'quarterly':
            return 90
        elif self.billing_period == 'yearly':
            return 365
        elif self.billing_period == 'lifetime':
            return 36500  # 100 лет
        return 30
    
    def get_available_durations(self):
        """
        Получить список доступных периодов оплаты из скидок за длительность.
        
        Returns:
            list: список доступных периодов в днях [30, 90, 180, 365]
        """
        # Получаем периоды из скидок за длительность
        durations = list(self.duration_discounts.values_list('duration_days', flat=True))
        
        # Если есть скидки, возвращаем их периоды
        if durations:
            return sorted(durations)
        
        # Если скидок нет, доступны все стандартные периоды
        return [30, 90, 180, 365]
    
    def is_duration_available(self, duration_days):
        """
        Проверить, доступен ли указанный период оплаты.
        
        Args:
            duration_days: период в днях (30, 90, 180, 365)
        
        Returns:
            bool
        """
        # Если есть скидки за длительность, проверяем по ним
        if self.duration_discounts.exists():
            return self.duration_discounts.filter(duration_days=duration_days).exists()
        
        # Если скидок нет, доступны все стандартные периоды
        return duration_days in [30, 90, 180, 365]
    
    def can_be_used(self, user=None):
        """
        Проверить, можно ли использовать этот тариф.
        
        Args:
            user: пользователь (для проверки типа акции "first_time")
        
        Returns:
            tuple: (can_use: bool, error_message: str)
        """
        if self.promo_type == 'limited_uses':
            if self.max_uses is not None and self.current_uses >= self.max_uses:
                return False, f'Тариф можно использовать только {self.max_uses} раз(а). Лимит исчерпан.'
        
        if self.promo_type == 'first_time' and user:
            # Проверяем, есть ли у пользователя уже подписки
            # Используем строку для избежания циклического импорта
            from django.apps import apps
            Subscription = apps.get_model('subscriptions', 'Subscription')
            has_subscriptions = Subscription.objects.filter(user=user).exists()
            if has_subscriptions:
                return False, 'Этот тариф доступен только для новых пользователей.'
        
        return True, ''
    
    def calculate_price(self, student_count=0, duration_days=None, promo_code=None, remaining_days=None, is_add_students=False, current_student_count=0):
        """
        Рассчитать цену подписки.
        
        Args:
            student_count: количество учеников (для типа per_student)
            duration_days: длительность в днях (30, 90, 180, 365)
            promo_code: промокод (опционально)
            remaining_days: оставшиеся дни текущей подписки (для продления)
            is_add_students: режим добавления учеников в текущий период
            current_student_count: текущее количество учеников в подписке (для расчета доплаты)
        
        Returns:
            dict: {
                'original_amount': исходная сумма,
                'discount_amount': сумма скидки,
                'final_amount': итоговая сумма,
                'end_date': дата окончания,
                'end_date_display': дата окончания для отображения,
                'extra_students_payment': доплата за новых учеников за оставшиеся дни
            }
        """
        from decimal import Decimal
        
        # Определяем базовую цену
        if self.subscription_type == 'per_student':
            # Проверяем прогрессирующие скидки
            bulk_discount = None
            if student_count > 0:
                # Ищем подходящую прогрессирующую скидку
                bulk_discounts = self.bulk_discounts.all().order_by('-min_students')
                for discount in bulk_discounts:
                    if discount.matches(student_count):
                        bulk_discount = discount
                        break
            
            if bulk_discount:
                # Используем прогрессирующую скидку
                # price_per_student - это цена за одного ученика в этом диапазоне
                # Применяем цену за единицу к количеству учеников
                monthly_price = bulk_discount.price_per_student * Decimal(str(student_count))
            else:
                # Обычная цена за ученика
                price_per_student = self.price_per_student or self.price
                monthly_price = price_per_student * Decimal(str(student_count))
            
            # Учитываем длительность подписки
            if duration_days:
                days_in_month = 30
                months = Decimal(str(duration_days)) / Decimal(str(days_in_month))
                original_amount = monthly_price * months
            else:
                # По умолчанию 1 месяц
                original_amount = monthly_price
        else:
            # Ежемесячная подписка
            # Если указана кастомная длительность, пересчитываем цену
            if duration_days:
                days_in_month = 30
                months = Decimal(str(duration_days)) / Decimal(str(days_in_month))
                original_amount = self.price * months
            else:
                original_amount = self.price
        
        # Применяем скидку за длительность для ежемесячных тарифов
        duration_discount_amount = Decimal('0')
        if self.subscription_type == 'monthly' and duration_days:
            # Ищем скидку за длительность
            duration_discount = self.duration_discounts.filter(duration_days=duration_days).first()
            if duration_discount:
                # Применяем процентную скидку
                discount_percent = duration_discount.discount_percent / Decimal('100')
                duration_discount_amount = original_amount * discount_percent
        
        # Применяем промокод если есть (скидка от суммы после скидки за длительность)
        promo_discount_amount = Decimal('0')
        price_after_duration_discount = original_amount - duration_discount_amount
        if promo_code:
            is_valid, error = promo_code.is_valid()
            if is_valid:
                promo_discount_amount = promo_code.calculate_discount(price_after_duration_discount)
        
        # Итоговая сумма скидки
        discount_amount = duration_discount_amount + promo_discount_amount
        final_amount = max(Decimal('0'), original_amount - discount_amount)
        
        # Если это продление с добавлением учеников, рассчитываем доплату за новых учеников за оставшиеся дни
        extra_students_payment = Decimal('0')
        if (remaining_days and remaining_days > 0 and 
            self.subscription_type == 'per_student' and 
            student_count > current_student_count and current_student_count > 0):
            # Количество новых учеников
            new_students = student_count - current_student_count
            
            # Рассчитываем цену за одного нового ученика в месяц
            # Используем ту же логику прогрессивных скидок, но для нового количества
            new_bulk_discount = None
            if student_count > 0:
                bulk_discounts = self.bulk_discounts.all().order_by('-min_students')
                for discount in bulk_discounts:
                    if discount.matches(student_count):
                        new_bulk_discount = discount
                        break
            
            if new_bulk_discount:
                price_per_new_student_per_month = new_bulk_discount.price_per_student
            else:
                price_per_new_student_per_month = self.price_per_student or self.price
            
            # Рассчитываем доплату за новых учеников за оставшиеся дни
            days_in_month = 30
            months_remaining = Decimal(str(remaining_days)) / Decimal(str(days_in_month))
            extra_students_payment = price_per_new_student_per_month * Decimal(str(new_students)) * months_remaining
            
            # Добавляем доплату к итоговой сумме
            final_amount = final_amount + extra_students_payment
        
        result = {
            'original_amount': original_amount,
            'discount_amount': discount_amount,
            'final_amount': final_amount
        }
        
        # Добавляем информацию о доплате за новых учеников
        if extra_students_payment > 0:
            result['extra_students_payment'] = float(extra_students_payment)
            result['extra_students_count'] = student_count - current_student_count
            result['remaining_days_payment'] = remaining_days
        
        # Добавляем информацию о скидке за длительность
        if duration_discount_amount > 0:
            result['duration_discount'] = {
                'amount': float(duration_discount_amount),
                'percent': float(duration_discount.discount_percent) if duration_discount else 0,
                'duration_days': duration_days
            }
        
        # Добавляем дату окончания подписки
        if duration_days:
            # Если есть оставшиеся дни (продление), добавляем к текущей дате окончания
            if remaining_days and remaining_days > 0:
                # Для продления: берем текущую дату + оставшиеся дни + новые дни
                base_date = timezone.now() + timedelta(days=remaining_days)
                end_date = base_date + timedelta(days=duration_days)
            else:
                # Новая подписка: от текущей даты
                end_date = timezone.now() + timedelta(days=duration_days)
            
            result['end_date'] = end_date.strftime('%Y-%m-%d')
            result['end_date_display'] = end_date.strftime('%d.%m.%Y')
            result['total_duration_days'] = (remaining_days or 0) + duration_days
        else:
            # Если длительность не указана, используем по умолчанию
            if remaining_days and remaining_days > 0:
                base_date = timezone.now() + timedelta(days=remaining_days)
                end_date = base_date + timedelta(days=30)
            else:
                end_date = timezone.now() + timedelta(days=30)
            result['end_date'] = end_date.strftime('%Y-%m-%d')
            result['end_date_display'] = end_date.strftime('%d.%m.%Y')
            result['total_duration_days'] = (remaining_days or 0) + 30
        
        return result


class Subscription(models.Model):
    """
    Модель подписки пользователя.
    """
    
    STATUS_CHOICES = [
        ('trial', 'Пробная'),
        ('active', 'Активна'),
        ('past_due', 'Просрочена'),
        ('cancelled', 'Отменена'),
        ('expired', 'Истекла'),
    ]
    
    # Основная информация
    user = models.ForeignKey(
        'users.User',
        on_delete=models.CASCADE,
        related_name='subscriptions',
        verbose_name='Пользователь'
    )
    
    plan = models.ForeignKey(
        SubscriptionPlan,
        on_delete=models.PROTECT,
        related_name='subscriptions',
        verbose_name='Тарифный план'
    )
    
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='trial',
        verbose_name='Статус',
        db_index=True
    )
    
    # Промокод и скидка
    promo_code = models.ForeignKey(
        'PromoCode',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='subscriptions',
        verbose_name='Промокод'
    )
    
    discount_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Сумма скидки'
    )
    
    # Для подписки за ученика
    student_count = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Количество учеников',
        help_text='Используется для типа подписки "За ученика"'
    )
    
    # Отслеживание неоплаченных учеников
    unpaid_students_count = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Количество неоплаченных учеников',
        help_text='Количество учеников, добавленных сверх оплаченного количества'
    )
    
    pending_payment_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Сумма ожидающей доплаты',
        help_text='Сумма доплаты за неоплаченных учеников'
    )
    
    # Даты (можно указывать вручную)
    start_date = models.DateTimeField(
        verbose_name='Дата начала'
    )
    
    end_date = models.DateTimeField(
        verbose_name='Дата окончания'
    )
    
    # Длительность в днях (30, 90, 180, 365)
    duration_days = models.IntegerField(
        default=30,
        validators=[MinValueValidator(1)],
        verbose_name='Длительность в днях',
        help_text='30, 90, 180, 365 дней'
    )
    
    # Сумма с учетом скидки
    original_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Исходная сумма'
    )
    
    discount_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Сумма скидки'
    )
    
    final_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Итоговая сумма'
    )
    
    trial_end_date = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='Дата окончания пробного периода'
    )
    
    cancelled_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='Дата отмены'
    )
    
    # Автопродление
    auto_renew = models.BooleanField(
        default=True,
        verbose_name='Автопродление'
    )
    
    # Статистика использования
    lessons_used = models.IntegerField(
        default=0,
        verbose_name='Использовано занятий'
    )
    
    storage_used_mb = models.IntegerField(
        default=0,
        verbose_name='Использовано хранилища (МБ)'
    )
    
    video_minutes_used = models.IntegerField(
        default=0,
        verbose_name='Использовано минут видео'
    )
    
    # Временные метки
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Дата создания'
    )
    
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name='Дата обновления'
    )
    
    class Meta:
        db_table = 'subscriptions'
        verbose_name = 'Подписка'
        verbose_name_plural = 'Подписки'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['user', 'status']),
            models.Index(fields=['end_date']),
            models.Index(fields=['status']),
            models.Index(fields=['user', 'end_date']),
            models.Index(fields=['plan', 'status']),
            models.Index(fields=['start_date', 'end_date']),
        ]
    
    def __str__(self):
        return f"{self.user.email} - {self.plan.name} ({self.get_status_display()})"
    
    def is_active(self):
        """Проверка активности подписки."""
        now = timezone.now()
        return (
            self.status in ['trial', 'active'] and
            self.start_date <= now <= self.end_date
        )
    
    def is_trial(self):
        """Проверка пробного периода."""
        if not self.trial_end_date:
            return False
        return timezone.now() <= self.trial_end_date and self.status == 'trial'
    
    def days_until_expiration(self):
        """Дни до истечения."""
        if not self.is_active():
            return 0
        delta = self.end_date - timezone.now()
        return max(0, delta.days)
    
    def renew(self, duration_days=None):
        """
        Продление подписки.
        
        Args:
            duration_days: длительность в днях (если не указана, используется из плана)
        """
        if duration_days:
            days = duration_days
        elif self.custom_duration_days:
            days = self.custom_duration_days
        else:
            days = self.plan.get_duration_days()
        
        self.end_date = self.end_date + timedelta(days=days)
        self.status = 'active'
        self.save()
    
    def apply_extra_payment(self, new_student_count):
        """
        Применить доплату за дополнительных учеников.
        
        Обновляет student_count и сбрасывает счетчики неоплаченных учеников.
        
        Args:
            new_student_count: новое общее количество учеников
        """
        if self.plan.subscription_type != 'per_student':
            return
        
        # Обновляем количество оплаченных учеников
        self.student_count = new_student_count
        
        # Сбрасываем счетчики неоплаченных
        self.unpaid_students_count = 0
        self.pending_payment_amount = Decimal('0')
        
        self.save(update_fields=['student_count', 'unpaid_students_count', 'pending_payment_amount'])
    
    def cancel(self):
        """Отмена подписки."""
        self.status = 'cancelled'
        self.cancelled_at = timezone.now()
        self.auto_renew = False
        self.save()
    
    def check_expiration(self):
        """Проверка истечения срока."""
        now = timezone.now()
        
        # Проверка окончания пробного периода
        if self.is_trial() and self.trial_end_date and now > self.trial_end_date:
            self.status = 'active' if self.auto_renew else 'expired'
            self.save()
        
        # Проверка истечения подписки
        if now > self.end_date:
            if self.auto_renew and self.status == 'active':
                self.status = 'past_due'  # Ожидание оплаты
            else:
                self.status = 'expired'
            self.save()
    
    def has_feature(self, feature_name):
        """Проверка доступности функции."""
        if not self.is_active():
            return False
        
        feature_map = {
            'video_calls': self.plan.allow_video_calls,
            'screen_sharing': self.plan.allow_screen_sharing,
            'whiteboard': self.plan.allow_whiteboard,
            'homework': self.plan.allow_homework,
            'materials': self.plan.allow_materials,
            'analytics': self.plan.allow_analytics,
            'telegram_bot': self.plan.allow_telegram_bot,
            'api_access': self.plan.allow_api_access,
        }
        
        return feature_map.get(feature_name, False)
    
    def check_limit(self, limit_type):
        """Проверка лимитов."""
        if not self.is_active():
            return False
        
        if limit_type == 'lessons':
            max_lessons = self.plan.max_lessons_per_month
            if max_lessons is None:
                return True
            return self.lessons_used < max_lessons
        
        elif limit_type == 'storage':
            return self.storage_used_mb < self.plan.max_storage_mb
        
        elif limit_type == 'video_minutes':
            max_minutes = self.plan.max_video_minutes_per_month
            if max_minutes is None:
                return True
            return self.video_minutes_used < max_minutes
        
        elif limit_type == 'clients':
            # Для типа "За ученика" - лимит не применяется, но нужно платить за каждого
            if self.plan.subscription_type == 'per_student':
                return True  # Можно добавлять учеников, но нужно будет доплатить
            # Для ежемесячной подписки - проверяем лимит
            max_clients = self.plan.max_clients
            if max_clients is None:
                return True
            from apps.users.models import Client
            current_clients_count = Client.objects.filter(mentors=self.user).count()
            return current_clients_count < max_clients
        
        return True
    
    def calculate_monthly_fee(self):
        """Рассчитать ежемесячную плату."""
        if self.plan.subscription_type == 'per_student':
            if self.plan.price_per_student:
                from apps.users.models import Client
                current_clients_count = Client.objects.filter(mentors=self.user).count()
                return current_clients_count * self.plan.price_per_student
            return 0
        else:
            return self.plan.price
    
    def update_monthly_fee(self):
        """Обновить ежемесячную плату на основе текущего количества учеников."""
        if self.plan.subscription_type == 'per_student':
            from apps.users.models import Client
            self.monthly_fee = self.calculate_monthly_fee()
            self.students_count = Client.objects.filter(mentor=self.user).count()
            self.save(update_fields=['monthly_fee', 'students_count'])
    
    def reset_monthly_usage(self):
        """Сброс месячного использования."""
        self.lessons_used = 0
        self.video_minutes_used = 0
        self.save()


class Payment(models.Model):
    """
    Модель платежа.
    """
    
    STATUS_CHOICES = [
        ('pending', 'Ожидает'),
        ('processing', 'Обрабатывается'),
        ('succeeded', 'Успешно'),
        ('failed', 'Ошибка'),
        ('cancelled', 'Отменен'),
        ('refunded', 'Возврат'),
    ]
    
    PAYMENT_METHOD_CHOICES = [
        ('card', 'Карта'),
        ('yookassa', 'ЮKassa'),
        ('stripe', 'Stripe'),
        ('paypal', 'PayPal'),
        ('other', 'Другое'),
    ]
    
    # Основная информация
    uuid = models.UUIDField(
        default=uuid.uuid4,
        unique=True,
        editable=False,
        verbose_name='UUID'
    )
    
    user = models.ForeignKey(
        'users.User',
        on_delete=models.CASCADE,
        related_name='payments',
        verbose_name='Пользователь'
    )
    
    subscription = models.ForeignKey(
        Subscription,
        on_delete=models.CASCADE,
        related_name='payments',
        verbose_name='Подписка'
    )
    
    # Сумма
    amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Сумма'
    )
    
    currency = models.CharField(
        max_length=3,
        default='RUB',
        verbose_name='Валюта'
    )
    
    # Статус
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='pending',
        verbose_name='Статус',
        db_index=True
    )
    
    payment_method = models.CharField(
        max_length=20,
        choices=PAYMENT_METHOD_CHOICES,
        verbose_name='Метод оплаты'
    )
    
    # Внешние данные
    external_id = models.CharField(
        max_length=255,
        blank=True,
        verbose_name='Внешний ID',
        db_index=True
    )
    
    provider_response = models.JSONField(
        default=dict,
        blank=True,
        verbose_name='Ответ провайдера'
    )
    
    # Описание
    description = models.TextField(
        blank=True,
        verbose_name='Описание'
    )
    
    # Временные метки
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Дата создания',
        db_index=True
    )
    
    paid_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='Дата оплаты'
    )
    
    failed_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='Дата ошибки'
    )
    
    refunded_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='Дата возврата'
    )
    
    class Meta:
        db_table = 'payments'
        verbose_name = 'Платеж'
        verbose_name_plural = 'Платежи'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['user', 'created_at']),
            models.Index(fields=['subscription']),
            models.Index(fields=['status']),
            models.Index(fields=['external_id']),
        ]
    
    def __str__(self):
        return f"{self.user.email} - {self.amount} {self.currency} ({self.get_status_display()})"
    
    def mark_as_succeeded(self):
        """Отметить платеж как успешный."""
        self.status = 'succeeded'
        self.paid_at = timezone.now()
        self.save()
        
        # Обновляем подписку
        if self.subscription.status in ['trial', 'past_due']:
            self.subscription.status = 'active'
            self.subscription.save()
    
    def mark_as_failed(self, reason=''):
        """Отметить платеж как неудачный."""
        self.status = 'failed'
        self.failed_at = timezone.now()
        if reason:
            self.description = reason
        self.save()
    
    def refund(self):
        """Возврат платежа."""
        self.status = 'refunded'
        self.refunded_at = timezone.now()
        self.save()


class PaymentHistory(models.Model):
    """
    История изменений платежа.
    """
    
    payment = models.ForeignKey(
        Payment,
        on_delete=models.CASCADE,
        related_name='history',
        verbose_name='Платеж'
    )
    
    status = models.CharField(
        max_length=20,
        verbose_name='Статус'
    )
    
    message = models.TextField(
        blank=True,
        verbose_name='Сообщение'
    )
    
    data = models.JSONField(
        default=dict,
        blank=True,
        verbose_name='Данные'
    )
    
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Дата',
        db_index=True
    )
    
    class Meta:
        db_table = 'payment_history'
        verbose_name = 'История платежа'
        verbose_name_plural = 'История платежей'
        ordering = ['-created_at']
    
    def __str__(self):
        return f"{self.payment.uuid} - {self.status}"


class SubscriptionUsageLog(models.Model):
    """
    Лог использования подписки.
    """
    
    USAGE_TYPE_CHOICES = [
        ('lesson', 'Занятие'),
        ('storage', 'Хранилище'),
        ('video_minutes', 'Минуты видео'),
    ]
    
    subscription = models.ForeignKey(
        Subscription,
        on_delete=models.CASCADE,
        related_name='usage_logs',
        verbose_name='Подписка'
    )
    
    usage_type = models.CharField(
        max_length=20,
        choices=USAGE_TYPE_CHOICES,
        verbose_name='Тип использования'
    )
    
    amount = models.IntegerField(
        verbose_name='Количество'
    )
    
    description = models.CharField(
        max_length=255,
        blank=True,
        verbose_name='Описание'
    )
    
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Дата',
        db_index=True
    )
    
    class Meta:
        db_table = 'subscription_usage_logs'
        verbose_name = 'Лог использования'
        verbose_name_plural = 'Логи использования'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['subscription', 'created_at']),
            models.Index(fields=['usage_type']),
        ]
    
    def __str__(self):
        return f"{self.subscription.user.email} - {self.get_usage_type_display()} ({self.amount})"


class PromoCode(models.Model):
    """
    Модель промокода для скидок на подписки.
    """
    
    DISCOUNT_TYPE_CHOICES = [
        ('percentage', 'Процент'),
        ('fixed', 'Фиксированная сумма'),
    ]
    
    # Основная информация
    code = models.CharField(
        max_length=50,
        unique=True,
        db_index=True,
        verbose_name='Код',
        help_text='Уникальный код промокода'
    )
    
    description = models.TextField(
        blank=True,
        verbose_name='Описание'
    )
    
    # Тип скидки
    discount_type = models.CharField(
        max_length=20,
        choices=DISCOUNT_TYPE_CHOICES,
        default='percentage',
        verbose_name='Тип скидки'
    )
    
    # Размер скидки
    discount_value = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Значение скидки',
        help_text='Процент или фиксированная сумма'
    )
    
    # Ограничения
    max_discount_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        null=True,
        blank=True,
        validators=[MinValueValidator(0)],
        verbose_name='Максимальная сумма скидки',
        help_text='Для процентных скидок - ограничение максимальной суммы'
    )
    
    # Срок действия
    valid_from = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='Действителен с',
        help_text='Если не указано, действует с момента создания'
    )
    
    valid_until = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='Действителен до',
        help_text='Если не указано, действует бессрочно'
    )
    
    # Лимиты использования
    max_uses_total = models.IntegerField(
        null=True,
        blank=True,
        validators=[MinValueValidator(1)],
        verbose_name='Максимум использований (общий)',
        help_text='Общее количество использований промокода'
    )
    
    max_uses_per_user = models.IntegerField(
        default=1,
        validators=[MinValueValidator(1)],
        verbose_name='Максимум использований на пользователя',
        help_text='Сколько раз один пользователь может использовать промокод'
    )
    
    # Статистика
    uses_count = models.IntegerField(
        default=0,
        verbose_name='Количество использований'
    )
    
    # Ограничения
    is_active = models.BooleanField(
        default=True,
        verbose_name='Активен',
        db_index=True
    )
    
    # Применяется только к определенным планам (без промежуточной модели)
    applicable_plans = models.ManyToManyField(
        SubscriptionPlan,
        blank=True,
        related_name='promo_codes',
        verbose_name='Применимые тарифы',
        help_text='Если не выбрано, применяется ко всем тарифам'
    )
    
    # Временные метки
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Дата создания'
    )
    
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name='Дата обновления'
    )
    
    class Meta:
        db_table = 'promo_codes'
        verbose_name = 'Промокод'
        verbose_name_plural = 'Промокоды'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['code', 'is_active']),
            models.Index(fields=['valid_until']),
        ]
    
    def __str__(self):
        return f"{self.code} - {self.get_discount_display()}"
    
    def get_discount_display(self):
        """Отображение скидки."""
        if self.discount_type == 'percentage':
            return f"{self.discount_value}%"
        return f"{self.discount_value} {self.currency if hasattr(self, 'currency') else 'RUB'}"
    
    def is_valid(self, user=None, plan=None):
        """
        Проверка валидности промокода.
        
        Args:
            user: Пользователь (для проверки лимитов на пользователя)
            plan: Тарифный план (для проверки применимости)
        
        Returns:
            tuple: (is_valid: bool, error_message: str)
        """
        # Проверка активности
        if not self.is_active:
            return False, "Промокод неактивен"
        
        # Проверка срока действия
        now = timezone.now()
        if self.valid_from and now < self.valid_from:
            return False, "Промокод еще не действует"
        
        if self.valid_until and now > self.valid_until:
            return False, "Промокод истек"
        
        # Проверка общего лимита использований
        if self.max_uses_total and self.uses_count >= self.max_uses_total:
            return False, "Промокод исчерпан"
        
        # Проверка лимита на пользователя
        if user:
            # Используем строку для избежания циклического импорта
            from django.apps import apps
            Subscription = apps.get_model('subscriptions', 'Subscription')
            user_uses = Subscription.objects.filter(
                user=user,
                promo_code=self
            ).count()
            
            if user_uses >= self.max_uses_per_user:
                return False, f"Вы уже использовали этот промокод максимальное количество раз ({self.max_uses_per_user})"
        
        # Проверка применимости к тарифу
        if plan and self.applicable_plans.exists():
            if plan not in self.applicable_plans.all():
                return False, "Промокод не применим к выбранному тарифу"
        
        return True, ""
    
    def calculate_discount(self, amount):
        """
        Рассчитать сумму скидки.
        
        Args:
            amount: Исходная сумма
        
        Returns:
            tuple: (discount_amount: Decimal, final_amount: Decimal)
        """
        from decimal import Decimal
        
        if self.discount_type == 'percentage':
            discount_amount = (amount * self.discount_value) / Decimal('100')
            
            # Применяем максимальную скидку, если указана
            if self.max_discount_amount:
                discount_amount = min(discount_amount, self.max_discount_amount)
        else:
            discount_amount = min(self.discount_value, amount)
        
        final_amount = max(Decimal('0'), amount - discount_amount)
        
        return discount_amount, final_amount
    
    def apply(self, user, subscription):
        """
        Применить промокод к подписке.
        
        Args:
            user: Пользователь
            subscription: Подписка
        
        Returns:
            PromoCodeUsage: Объект использования промокода
        """
        # Создаем запись об использовании
        from django.apps import apps
        PromoCodeUsage = apps.get_model('subscriptions', 'PromoCodeUsage')
        usage = PromoCodeUsage.objects.create(
            promo_code=self,
            user=user,
            subscription=subscription,
            discount_amount=subscription.discount_amount or 0,
            original_amount=subscription.original_amount or 0,
            final_amount=subscription.final_amount or 0
        )
        
        # Увеличиваем счетчик использований
        self.uses_count += 1
        self.save(update_fields=['uses_count'])
        
        return usage


class PromoCodeUsage(models.Model):
    """
    История использования промокодов.
    """
    
    promo_code = models.ForeignKey(
        PromoCode,
        on_delete=models.CASCADE,
        related_name='usages',
        verbose_name='Промокод'
    )
    
    user = models.ForeignKey(
        'users.User',
        on_delete=models.CASCADE,
        related_name='promo_code_usages',
        verbose_name='Пользователь'
    )
    
    subscription = models.ForeignKey(
        Subscription,
        on_delete=models.CASCADE,
        related_name='promo_code_usages',
        verbose_name='Подписка'
    )
    
    discount_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        verbose_name='Сумма скидки'
    )
    
    original_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        verbose_name='Исходная сумма'
    )
    
    final_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        verbose_name='Итоговая сумма'
    )
    
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Дата использования',
        db_index=True
    )
    
    class Meta:
        db_table = 'promo_code_usages'
        verbose_name = 'Использование промокода'
        verbose_name_plural = 'Использования промокодов'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['user', 'created_at']),
            models.Index(fields=['promo_code', 'user']),
        ]
    
    def __str__(self):
        return f"{self.user.email} - {self.promo_code.code} ({self.discount_amount})"
