"""
Модели реферальной системы и промокодов.
"""
import random
import string
from decimal import Decimal
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.utils import timezone
from django.conf import settings


def generate_referral_code():
    """Генерация уникального реферального кода."""
    return ''.join(random.choices(string.ascii_uppercase + string.digits, k=8))


class ReferralSettings(models.Model):
    """
    Настройки реферальной программы (singleton).
    """
    
    # Проценты комиссии
    level1_commission = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=10.00,
        validators=[MinValueValidator(0), MaxValueValidator(100)],
        verbose_name='Комиссия 1 уровня (%)',
        help_text='Процент от оплаты прямого реферала'
    )
    
    level2_commission = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=5.00,
        validators=[MinValueValidator(0), MaxValueValidator(100)],
        verbose_name='Комиссия 2 уровня (%)',
        help_text='Процент от оплаты реферала реферала'
    )
    
    # Очки за рефералов
    points_direct_referral = models.IntegerField(
        default=5,
        validators=[MinValueValidator(0)],
        verbose_name='Очков за прямого реферала'
    )
    
    points_indirect_referral = models.IntegerField(
        default=2,
        validators=[MinValueValidator(0)],
        verbose_name='Очков за реферала реферала'
    )
    
    # Обновление
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name='Обновлено'
    )
    
    class Meta:
        verbose_name = 'Настройки реферальной программы'
        verbose_name_plural = 'Настройки реферальной программы'
    
    def __str__(self):
        return f"Настройки реферальной программы (обновлено: {self.updated_at})"
    
    def save(self, *args, **kwargs):
        """Гарантируем что существует только одна запись."""
        self.pk = 1
        super().save(*args, **kwargs)
    
    @classmethod
    def get_settings(cls):
        """Получить настройки (создать если не существует)."""
        obj, created = cls.objects.get_or_create(pk=1)
        return obj


class ReferralLevel(models.Model):
    """
    Уровни реферальной программы.
    """
    
    level = models.IntegerField(
        unique=True,
        verbose_name='Уровень'
    )
    
    name = models.CharField(
        max_length=100,
        verbose_name='Название'
    )
    
    points_required = models.IntegerField(
        validators=[MinValueValidator(0)],
        verbose_name='Требуется очков',
        help_text='Минимальное количество очков для достижения уровня'
    )
    
    bonus_payment_percent = models.IntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(100)],
        verbose_name='% оплаты бонусами',
        help_text='Максимальный процент подписки, который можно оплатить бонусами'
    )
    
    icon = models.CharField(
        max_length=50,
        blank=True,
        verbose_name='Иконка'
    )
    
    class Meta:
        verbose_name = 'Уровень реферальной программы'
        verbose_name_plural = 'Уровни реферальной программы'
        ordering = ['level']
    
    def __str__(self):
        return f"Уровень {self.level}: {self.name} ({self.points_required} очков)"


class UserReferralProfile(models.Model):
    """
    Реферальный профиль пользователя.
    """
    
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='referral_profile',
        verbose_name='Пользователь'
    )
    
    referral_code = models.CharField(
        max_length=20,
        unique=True,
        db_index=True,
        verbose_name='Реферальный код'
    )
    
    referred_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='referrals',
        verbose_name='Пригласил'
    )
    
    # Статистика
    total_points = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Всего очков'
    )
    
    current_level = models.ForeignKey(
        ReferralLevel,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name='Текущий уровень'
    )
    
    direct_referrals_count = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Прямых рефералов'
    )
    
    indirect_referrals_count = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Непрямых рефералов'
    )
    
    total_earned = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Всего заработано (₽)'
    )
    
    # Даты
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Создан'
    )
    
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name='Обновлен'
    )
    
    class Meta:
        verbose_name = 'Реферальный профиль'
        verbose_name_plural = 'Реферальные профили'
    
    def __str__(self):
        return f"{self.user.email} - {self.referral_code}"
    
    def get_referral_link(self, base_url=''):
        """Получить реферальную ссылку."""
        if not base_url:
            base_url = settings.FRONTEND_URL
        return f"{base_url}/register?ref={self.referral_code}"
    
    def update_level(self):
        """Обновить уровень на основе очков."""
        new_level = ReferralLevel.objects.filter(
            points_required__lte=self.total_points
        ).order_by('-points_required').first()
        
        if new_level and new_level != self.current_level:
            self.current_level = new_level
            self.save(update_fields=['current_level', 'updated_at'])
            return True
        return False
    
    def add_points(self, points, reason=''):
        """Добавить очки."""
        self.total_points += points
        self.save(update_fields=['total_points', 'updated_at'])
        self.update_level()
        
        # Создаем запись в истории
        PointsTransaction.objects.create(
            user=self.user,
            points=points,
            reason=reason,
            balance_after=self.total_points
        )
    
    def get_max_bonus_payment_percent(self):
        """Получить максимальный процент оплаты бонусами."""
        if self.current_level:
            return self.current_level.bonus_payment_percent
        # Уровень 1 по умолчанию
        level1 = ReferralLevel.objects.filter(level=1).first()
        return level1.bonus_payment_percent if level1 else 60


class BonusAccount(models.Model):
    """
    Бонусный счет пользователя.
    """
    
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='bonus_account',
        verbose_name='Пользователь'
    )
    
    balance = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Баланс (₽)'
    )
    
    total_earned = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Всего заработано (₽)'
    )
    
    total_spent = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        validators=[MinValueValidator(0)],
        verbose_name='Всего потрачено (₽)'
    )
    
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Создан'
    )
    
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name='Обновлен'
    )
    
    class Meta:
        verbose_name = 'Бонусный счет'
        verbose_name_plural = 'Бонусные счета'
    
    def __str__(self):
        return f"{self.user.email} - {self.balance} ₽"
    
    def add_bonus(self, amount, reason=''):
        """Добавить бонусы."""
        amount = Decimal(str(amount))
        self.balance += amount
        self.total_earned += amount
        self.save(update_fields=['balance', 'total_earned', 'updated_at'])
        
        # Создаем транзакцию
        BonusTransaction.objects.create(
            user=self.user,
            amount=amount,
            transaction_type='earn',
            reason=reason,
            balance_after=self.balance
        )
    
    def spend_bonus(self, amount, reason=''):
        """Потратить бонусы."""
        amount = Decimal(str(amount))
        if amount > self.balance:
            raise ValueError('Недостаточно бонусов')
        
        self.balance -= amount
        self.total_spent += amount
        self.save(update_fields=['balance', 'total_spent', 'updated_at'])
        
        # Создаем транзакцию
        BonusTransaction.objects.create(
            user=self.user,
            amount=amount,
            transaction_type='spend',
            reason=reason,
            balance_after=self.balance
        )


class ReferralEarning(models.Model):
    """
    История заработков с рефералов.
    """
    
    referrer = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='referral_earnings',
        verbose_name='Реферер'
    )
    
    referral = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='generated_earnings',
        verbose_name='Реферал'
    )
    
    payment = models.ForeignKey(
        'subscriptions.Payment',
        on_delete=models.CASCADE,
        verbose_name='Платеж'
    )
    
    level = models.IntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(2)],
        verbose_name='Уровень',
        help_text='1 - прямой реферал, 2 - реферал реферала'
    )
    
    payment_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Сумма платежа (₽)'
    )
    
    commission_percent = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        validators=[MinValueValidator(0), MaxValueValidator(100)],
        verbose_name='Процент комиссии'
    )
    
    earned_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Заработано (₽)'
    )
    
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Создан'
    )
    
    class Meta:
        verbose_name = 'Заработок с реферала'
        verbose_name_plural = 'Заработки с рефералов'
        ordering = ['-created_at']
    
    def __str__(self):
        return f"{self.referrer.email} <- {self.referral.email}: {self.earned_amount} ₽"


class PointsTransaction(models.Model):
    """
    История начисления очков.
    """
    
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='points_transactions',
        verbose_name='Пользователь'
    )
    
    points = models.IntegerField(
        verbose_name='Очки'
    )
    
    reason = models.CharField(
        max_length=255,
        verbose_name='Причина'
    )
    
    balance_after = models.IntegerField(
        validators=[MinValueValidator(0)],
        verbose_name='Баланс после'
    )
    
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Создан'
    )
    
    class Meta:
        verbose_name = 'Транзакция очков'
        verbose_name_plural = 'Транзакции очков'
        ordering = ['-created_at']
    
    def __str__(self):
        return f"{self.user.email}: {self.points:+d} очков"


class BonusTransaction(models.Model):
    """
    История транзакций бонусного счета.
    """
    
    TRANSACTION_TYPES = [
        ('earn', 'Начисление'),
        ('spend', 'Списание'),
    ]
    
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='bonus_transactions',
        verbose_name='Пользователь'
    )
    
    amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Сумма (₽)'
    )
    
    transaction_type = models.CharField(
        max_length=10,
        choices=TRANSACTION_TYPES,
        verbose_name='Тип транзакции'
    )
    
    reason = models.CharField(
        max_length=255,
        verbose_name='Причина'
    )
    
    balance_after = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Баланс после (₽)'
    )
    
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Создан'
    )
    
    class Meta:
        verbose_name = 'Транзакция бонусов'
        verbose_name_plural = 'Транзакции бонусов'
        ordering = ['-created_at']
    
    def __str__(self):
        sign = '+' if self.transaction_type == 'earn' else '-'
        return f"{self.user.email}: {sign}{self.amount} ₽"


class ReferralInvitedEmail(models.Model):
    """
    Бэклог email-адресов, которые уже были приглашены (защита от накрутки).
    Один email может быть в списке только один раз.
    """
    email = models.EmailField(
        unique=True,
        db_index=True,
        verbose_name='Email приглашённого'
    )
    referrer = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='invited_emails',
        verbose_name='Реферер'
    )
    referred_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='+',
        verbose_name='Приглашённый пользователь'
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Дата'
    )

    class Meta:
        db_table = 'referrals_invited_emails'
        verbose_name = 'Приглашённый email'
        verbose_name_plural = 'Бэклог приглашённых email'
        ordering = ['-created_at']

    def __str__(self):
        return f"{self.email} (пригласил: {self.referrer.email})"


class UserActivityDay(models.Model):
    """
    Учёт дней активности пользователя на платформе (один день — одна запись).
    Используется для проверки «реферал был активен 20+ дней» перед начислением бонуса.
    """
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='activity_days',
        verbose_name='Пользователь'
    )
    date = models.DateField(
        verbose_name='Дата',
        db_index=True
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Создано'
    )

    class Meta:
        db_table = 'referrals_user_activity_days'
        verbose_name = 'День активности'
        verbose_name_plural = 'Дни активности'
        unique_together = [['user', 'date']]
        ordering = ['-date']
        indexes = [
            models.Index(fields=['user', 'date']),
        ]

    def __str__(self):
        return f"{self.user.email} — {self.date}"


class PendingReferralBonus(models.Model):
    """
    Ожидающее начисление бонуса за реферала.
    Начисляется после 30 дней при условии 20+ дней активности реферала,
    либо при достижении рефералом 21 дня активности (если был менее активен).
    """
    STATUS_PENDING = 'pending'
    STATUS_PAID = 'paid'
    STATUS_CANCELLED = 'cancelled'
    STATUS_CHOICES = [
        (STATUS_PENDING, 'Ожидает'),
        (STATUS_PAID, 'Начислено'),
        (STATUS_CANCELLED, 'Отменено'),
    ]

    referrer = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='pending_referral_bonuses',
        verbose_name='Реферер'
    )
    referred_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='pending_bonuses_for_me',
        verbose_name='Реферал'
    )
    referred_at = models.DateTimeField(
        verbose_name='Дата приглашения',
        db_index=True
    )
    points = models.IntegerField(
        validators=[MinValueValidator(0)],
        verbose_name='Очки к начислению'
    )
    level = models.IntegerField(
        default=1,
        validators=[MinValueValidator(1), MaxValueValidator(2)],
        verbose_name='Уровень (1 — прямой, 2 — непрямой)'
    )
    reason = models.CharField(
        max_length=255,
        blank=True,
        verbose_name='Причина'
    )
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default=STATUS_PENDING,
        db_index=True,
        verbose_name='Статус'
    )
    paid_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='Дата начисления'
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Создано'
    )

    class Meta:
        db_table = 'referrals_pending_referral_bonus'
        verbose_name = 'Ожидающий бонус за реферала'
        verbose_name_plural = 'Ожидающие бонусы за рефералов'
        ordering = ['referred_at']
        indexes = [
            models.Index(fields=['status', 'referred_at']),
        ]

    def __str__(self):
        return f"{self.referrer.email} <- {self.referred_user.email}: {self.points} очков ({self.get_status_display()})"


class PromoCode(models.Model):
    """
    Промокод для скидок на подписки.
    """
    
    code = models.CharField(
        max_length=50,
        unique=True,
        db_index=True,
        verbose_name='Код'
    )
    
    name = models.CharField(
        max_length=200,
        verbose_name='Название'
    )
    
    description = models.TextField(
        blank=True,
        verbose_name='Описание'
    )
    
    discount_type = models.CharField(
        max_length=10,
        choices=[
            ('percent', 'Процент'),
            ('fixed', 'Фиксированная сумма'),
        ],
        default='percent',
        verbose_name='Тип скидки'
    )
    
    discount_value = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Размер скидки',
        help_text='Процент (0-100) или фиксированная сумма'
    )
    
    # Применимость
    applicable_plans = models.ManyToManyField(
        'subscriptions.SubscriptionPlan',
        blank=True,
        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='Текущих использований'
    )
    
    valid_until = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name='Действителен до'
    )
    
    # Статус
    is_active = models.BooleanField(
        default=True,
        verbose_name='Активен'
    )
    
    # Статистика
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_promocodes',
        verbose_name='Создал'
    )
    
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Создан'
    )
    
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name='Обновлен'
    )
    
    class Meta:
        verbose_name = 'Промокод'
        verbose_name_plural = 'Промокоды'
        ordering = ['-created_at']
    
    def __str__(self):
        return f"{self.code} - {self.name}"
    
    def is_valid(self):
        """Проверить валидность промокода."""
        if not self.is_active:
            return False, 'Промокод неактивен'
        
        if self.valid_until and timezone.now() > self.valid_until:
            return False, 'Промокод истек'
        
        if self.max_uses and self.current_uses >= self.max_uses:
            return False, 'Промокод исчерпан'
        
        return True, 'OK'
    
    def can_apply_to_plan(self, plan):
        """Проверить применимость к плану."""
        if not self.applicable_plans.exists():
            return True  # Применим ко всем
        return self.applicable_plans.filter(id=plan.id).exists()
    
    def calculate_discount(self, amount):
        """Рассчитать размер скидки."""
        amount = Decimal(str(amount))
        
        if self.discount_type == 'percent':
            discount = amount * (self.discount_value / 100)
        else:  # fixed
            discount = min(self.discount_value, amount)
        
        return discount
    
    def use(self):
        """Использовать промокод."""
        self.current_uses += 1
        self.save(update_fields=['current_uses', 'updated_at'])


class PromoCodeUsage(models.Model):
    """
    История использования промокодов.
    """
    
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='promocode_usages',
        verbose_name='Пользователь'
    )
    
    promo_code = models.ForeignKey(
        PromoCode,
        on_delete=models.CASCADE,
        related_name='usages',
        verbose_name='Промокод'
    )
    
    payment = models.ForeignKey(
        'subscriptions.Payment',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        verbose_name='Платеж'
    )
    
    original_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Исходная сумма (₽)'
    )
    
    discount_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Размер скидки (₽)'
    )
    
    final_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)],
        verbose_name='Итоговая сумма (₽)'
    )
    
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name='Использован'
    )
    
    class Meta:
        verbose_name = 'Использование промокода'
        verbose_name_plural = 'Использования промокодов'
        ordering = ['-created_at']
    
    def __str__(self):
        return f"{self.user.email} - {self.promo_code.code}: -{self.discount_amount} ₽"

