"""
Базовые модели для products приложения.
Содержит SKUCounter и BaseProductEntity (абстрактный базовый класс).

TODO: Унификация системы soft delete
    - Перевести ProductCategory с is_deleted на status (как в BaseProductEntity)
    - Упростить managers.py, убрав поддержку старой системы is_deleted
    - Унифицировать ProductTag (добавить status или оставить is_active)
"""
from django.db import models, transaction, IntegrityError
from django.db.models import Q
from django.utils import timezone
from django.utils.text import slugify
from unidecode import unidecode


class SKUCounter(models.Model):
    """
    Глобальные счетчики для генерации уникальных номеров артикулов.
    Используется для товаров (product), комплектов (kit) и категорий (category).
    """
    COUNTER_TYPE_CHOICES = [
        ('product', 'Product Counter'),
        ('kit', 'Kit Counter'),
        ('category', 'Category Counter'),
        ('configurable', 'Configurable Product Counter'),
    ]

    counter_type = models.CharField(
        max_length=20,
        unique=True,
        choices=COUNTER_TYPE_CHOICES,
        verbose_name="Тип счетчика"
    )
    current_value = models.IntegerField(
        default=0,
        verbose_name="Текущее значение"
    )

    class Meta:
        verbose_name = "Счетчик артикулов"
        verbose_name_plural = "Счетчики артикулов"

    def __str__(self):
        return f"{self.get_counter_type_display()}: {self.current_value}"

    @classmethod
    def get_next_value(cls, counter_type):
        """
        Получить следующее значение счетчика (thread-safe).
        Использует select_for_update для предотвращения race conditions.
        
        DEPRECATED: Используйте peek_next_value() + increment_counter() вместо этого метода.
        Этот метод инкрементирует счётчик немедленно, что может привести к пропускам номеров
        при ошибках сохранения объекта.
        """
        with transaction.atomic():
            counter, created = cls.objects.select_for_update().get_or_create(
                counter_type=counter_type,
                defaults={'current_value': 0}
            )
            counter.current_value += 1
            counter.save()
            return counter.current_value

    @classmethod
    def peek_next_value(cls, counter_type):
        """
        FIX: SKU counter bug - increment only after successful save
        
        Получить следующее значение счетчика БЕЗ инкремента (thread-safe).
        Используется для генерации SKU перед сохранением объекта.
        Фактический инкремент выполняется в post_save сигнале после успешного создания.
        
        Args:
            counter_type: Тип счётчика ('product', 'kit', 'category', 'configurable')
            
        Returns:
            int: Следующее значение счётчика (current_value + 1)
        """
        with transaction.atomic():
            counter, created = cls.objects.select_for_update().get_or_create(
                counter_type=counter_type,
                defaults={'current_value': 0}
            )
            return counter.current_value + 1

    @classmethod
    def increment_counter(cls, counter_type):
        """
        FIX: SKU counter bug - increment only after successful save
        
        Инкрементировать счётчик (thread-safe).
        Вызывается в post_save сигнале после успешного создания объекта с автогенерированным SKU.
        
        Args:
            counter_type: Тип счётчика ('product', 'kit', 'category', 'configurable')
            
        Returns:
            int: Новое значение счётчика после инкремента
        """
        with transaction.atomic():
            counter, created = cls.objects.select_for_update().get_or_create(
                counter_type=counter_type,
                defaults={'current_value': 0}
            )
            counter.current_value += 1
            counter.save()
            return counter.current_value


class BaseProductEntity(models.Model):
    """
    Абстрактный базовый класс для Product и ProductKit.
    Объединяет общие поля идентификации, описания, статуса и soft delete.

    Используется как основа для:
    - Product (простой товар)
    - ProductKit (комплект товаров)
    """
    # Идентификация
    name = models.CharField(
        max_length=200,
        verbose_name="Название"
    )
    sku = models.CharField(
        max_length=100,
        blank=True,
        null=True,
        verbose_name="Артикул",
        db_index=True
    )
    slug = models.SlugField(
        max_length=200,
        unique=True,
        blank=True,
        verbose_name="URL-идентификатор"
    )

    # Описания
    description = models.TextField(
        blank=True,
        null=True,
        verbose_name="Описание"
    )
    short_description = models.TextField(
        blank=True,
        null=True,
        verbose_name="Краткое описание",
        help_text="Используется для карточек товаров, превью и площадок"
    )

    # Статусы товаров
    STATUS_CHOICES = [
        ('active', 'Активный'),           # На продажу
        ('archived', 'Архивный'),         # Скрыт (можно вернуть в сезон)
        ('discontinued', 'Снят'),         # Морально устарел, на удаление
    ]

    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='active',
        db_index=True,
        verbose_name="Статус"
    )

    # Маркетинговые флаги для внешних площадок (Recommerce и др.)
    is_new = models.BooleanField(
        default=False,
        verbose_name="Новинка",
        help_text="Отображать как новый товар"
    )
    is_popular = models.BooleanField(
        default=False,
        verbose_name="Популярный",
        help_text="Отображать как популярный товар"
    )
    is_special = models.BooleanField(
        default=False,
        verbose_name="Спецпредложение",
        help_text="Отображать как спецпредложение (акция)"
    )

    # Временные метки
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name="Дата создания"
    )
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name="Дата обновления"
    )

    # История архивирования
    archived_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name="Время архивирования"
    )
    archived_by = models.ForeignKey(
        'accounts.CustomUser',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='archived_%(class)s_set',
        verbose_name="Архивировано пользователем"
    )

    # Внешняя категория для интеграций
    external_category = models.ForeignKey(
        'ProductCategory',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='external_%(class)ss',
        verbose_name="Внешняя категория",
        help_text="Категория для интеграций с внешними площадками (Recommerce, WooCommerce и др.)"
    )

    # Manager
    objects = models.Manager()

    class Meta:
        abstract = True
        indexes = [
            models.Index(fields=['status']),
            models.Index(fields=['status', 'created_at']),
            models.Index(fields=['created_at']),
        ]
        constraints = [
            models.UniqueConstraint(
                fields=['name'],
                condition=Q(status='active'),
                name='unique_active_%(class)s_name'
            ),
        ]

    def __str__(self):
        return self.name

    def archive(self, user=None):
        """Архивирование товара (скрыть, но можно восстановить)"""
        self.status = 'archived'
        self.archived_at = timezone.now()
        if user:
            self.archived_by = user
        self.save(update_fields=['status', 'archived_at', 'archived_by'])

    def restore(self):
        """Восстановление архивированного товара"""
        self.status = 'active'
        self.archived_at = None
        self.archived_by = None
        self.save(update_fields=['status', 'archived_at', 'archived_by'])

    def discontinue(self, user=None):
        """Пометить товар как снятый (устарел, готов к удалению)"""
        self.status = 'discontinued'
        if user:
            self.archived_by = user
        self.save(update_fields=['status', 'archived_by'])

    def delete(self, *args, **kwargs):
        """Для совместимости: вызывает discontinue() - статус СНЯТ"""
        user = kwargs.pop('user', None)
        self.discontinue(user=user)
        return 1, {self.__class__._meta.label: 1}

    def hard_delete(self):
        """Физическое удаление из БД (необратимо! только для старых товаров)"""
        super().delete()

    @property
    def is_active(self):
        """Возвращает True если товар активен"""
        return self.status == 'active'

    def get_category_for_integration(self, integration_type: str = 'recommerce'):
        """
        Получить категорию товара для конкретной интеграции.

        Приоритет:
        1. external_category (если указана и есть маппинг)
        2. Первая категория из categories с существующим маппингом (если есть M2M)

        Args:
            integration_type: Тип интеграции ('recommerce', 'woocommerce')

        Returns:
            tuple: (ProductCategory, external_sku) или (None, None)
        """
        from integrations.models import IntegrationCategoryMapping

        # Приоритет 1: external_category
        if self.external_category:
            mapping = IntegrationCategoryMapping.objects.filter(
                category=self.external_category,
                integration_type=integration_type
            ).first()
            if mapping:
                return self.external_category, mapping.external_category_sku

        # Приоритет 2: первая категория из M2M (если есть)
        if hasattr(self, 'categories'):
            for category in self.categories.all():
                mapping = IntegrationCategoryMapping.objects.filter(
                    category=category,
                    integration_type=integration_type
                ).first()
                if mapping:
                    return category, mapping.external_category_sku

        return None, None

    def save(self, *args, **kwargs):
        """
        Автогенерация slug из name если не задан.
        Использует transaction.atomic() и retry логику для обработки race condition.
        """
        from ..services.slug_service import SlugService

        # Генерируем базовый slug
        if not self.slug or self.slug.strip() == '':
            base_slug = slugify(unidecode(self.name))
        else:
            base_slug = self.slug

        # Пытаемся сохранить с retry при IntegrityError
        max_retries = 5
        for attempt in range(max_retries):
            try:
                with transaction.atomic():
                    # Попытка 1: используем обычный способ генерации
                    if not self.slug or self.slug.strip() == '':
                        if attempt == 0:
                            # Первая попытка - используем обычный generate_unique_slug
                            self.slug = SlugService.generate_unique_slug(
                                self.name,
                                self.__class__,
                                self.pk
                            )
                        else:
                            # Retry попытки - используем get_next_available_slug с суффиксом
                            try:
                                self.slug = SlugService.get_next_available_slug(
                                    base_slug,
                                    self.__class__,
                                    self.pk,
                                    start_counter=attempt
                                )
                            except ValueError:
                                # Если все попытки с суффиксом исчерпаны, добавляем timestamp
                                import time
                                self.slug = f"{base_slug}-{int(time.time() % 10000)}"

                    # Основное сохранение
                    super().save(*args, **kwargs)
                    return  # Успешно сохранили, выходим

            except IntegrityError as e:
                # Если это последняя попытка, выбрасываем исключение
                if attempt == max_retries - 1:
                    raise
                # Иначе пытаемся снова со следующим slug'ом
                continue
