"""
Модели для работы с фотографиями продуктов, комплектов и категорий.
Использует паттерн Template Method для устранения дублирования кода.
"""
from abc import abstractmethod
from django.db import models
from django.db.models import Q
from django.utils import timezone


# ============================================
# Функции для upload_to с поддержкой мультитенантности
# ============================================

def get_product_photo_upload_path(instance, filename):
    """
    Генерирует путь для загрузки фото товара.
    Путь: products/temp/{filename}

    Примечание: Tenant ID добавляется автоматически TenantAwareFileSystemStorage.
    Финальный путь: tenants/{tenant_id}/products/temp/{filename}

    Args:
        instance: Объект ProductPhoto
        filename: Исходное имя файла

    Returns:
        str: Путь для временного сохранения
    """
    return f'products/temp/{filename}'


def get_kit_photo_upload_path(instance, filename):
    """
    Генерирует путь для загрузки фото комплекта.
    Путь: kits/temp/{filename}

    Примечание: Tenant ID добавляется автоматически TenantAwareFileSystemStorage.
    Финальный путь: tenants/{tenant_id}/kits/temp/{filename}

    Args:
        instance: Объект ProductKitPhoto
        filename: Исходное имя файла

    Returns:
        str: Путь для временного сохранения
    """
    return f'kits/temp/{filename}'


def get_category_photo_upload_path(instance, filename):
    """
    Генерирует путь для загрузки фото категории.
    Путь: categories/temp/{filename}

    Примечание: Tenant ID добавляется автоматически TenantAwareFileSystemStorage.
    Финальный путь: tenants/{tenant_id}/categories/temp/{filename}

    Args:
        instance: Объект ProductCategoryPhoto
        filename: Исходное имя файла

    Returns:
        str: Путь для временного сохранения
    """
    return f'categories/temp/{filename}'


class BasePhoto(models.Model):
    """
    Абстрактный базовый класс для всех фотомоделей.
    Объединяет общую логику обработки изображений для ProductPhoto, ProductKitPhoto, ProductCategoryPhoto.

    Паттерн: Template Method
    - Общие методы save(), delete() и get_*_url() определены здесь
    - Специфичные детали (related entity, upload path) задаются через абстрактные методы
    
    Главное фото:
    - is_main=True определяет главное фото (используется в карточках, каталоге, превью)
    - Constraint уникальности (только одно is_main=True на сущность) реализован в дочерних классах
    - order используется для сортировки остальных фото
    """
    image = models.ImageField(verbose_name="Оригинальное фото")
    is_main = models.BooleanField(
        default=False,
        db_index=True,
        verbose_name="Главное фото",
        help_text="Главное фото отображается в карточках, каталоге и превью. Может быть только одно."
    )
    order = models.PositiveIntegerField(default=0, verbose_name="Порядок")
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")

    class Meta:
        abstract = True
        ordering = ['-is_main', 'order', '-created_at']  # Главное фото всегда первое

    @abstractmethod
    def get_entity(self):
        """
        Возвращает связанную сущность (product, kit, category).
        Должен быть реализован в дочернем классе.

        Returns:
            Model: Связанный объект (Product, ProductKit или ProductCategory)
        """
        pass

    @abstractmethod
    def get_entity_type(self):
        """
        Возвращает тип сущности для путей изображений.

        Returns:
            str: Одно из значений: 'products', 'kits', 'categories'
        """
        pass

    def save(self, *args, **kwargs):
        """
        При загрузке нового изображения запускает асинхронную обработку через Celery.

        ВАЖНО: Асинхронная обработка!
        1. Сохраняем объект БЕЗ обработки изображения (быстро)
        2. Запускаем Celery task для обработки (в фоне)
        3. Пользователь видит "Обрабатывается..." + прогресс-бар
        4. Когда обработка завершится, фото обновляется

        Преимущества:
        - HTTP request не блокируется (не зависает UI)
        - Другие тенанты работают нормально
        - Можно обрабатывать много фото параллельно
        """
        import logging
        from django.db import connection
        from ..utils.image_processor import ImageProcessor

        logger = logging.getLogger(__name__)
        is_new = not self.pk
        use_async = kwargs.pop('use_async', True)  # Можно отключить для тестов/админки

        # Если это новый объект с изображением
        if is_new and self.image:
            temp_image = self.image
            # КРИТИЧНО: Сохраняем объект С ФАЙЛОМ сначала!
            # (потом Celery сможет прочитать файл)
            super().save(*args, **kwargs)

            if use_async:
                # АСИНХРОННАЯ ОБРАБОТКА через Celery
                try:
                    from ..tasks import process_product_photo_async

                    # Получаем текущую схему тенанта (для мультитенантности)
                    schema_name = connection.schema_name
                    logger.info(f"[BasePhoto.save] Photo {self.pk} submitted to Celery "
                               f"(schema: {schema_name})")

                    # Формируем полный путь к модели
                    photo_model_class = f"{self._meta.app_label}.{self.__class__.__name__}"

                    # Запускаем асинхронную задачу
                    task_result = process_product_photo_async.delay(
                        self.pk,
                        photo_model_class,
                        schema_name
                    )

                    logger.info(f"[BasePhoto.save] Task ID: {task_result.id}")

                    # Создаем запись о статусе обработки для фронтенда
                    PhotoProcessingStatus.objects.create(
                        photo_id=self.pk,
                        photo_model=photo_model_class,
                        status='pending',
                        task_id=task_result.id,
                        result_data={'temp_path': getattr(temp_image, 'name', None)}
                    )

                except ImportError:
                    logger.error("Celery task import failed, falling back to sync processing")
                    # Fallback на синхронную обработку если Celery недоступен
                    self._process_image_sync(temp_image, use_sync=True)

            else:
                # СИНХРОННАЯ ОБРАБОТКА (для совместимости и тестов)
                self._process_image_sync(temp_image)

        else:
            # Обновление существующего объекта (без изменения изображения)
            super().save(*args, **kwargs)

    def _process_image_sync(self, temp_image, use_sync=False):
        """
        Синхронная обработка изображения (fallback метод).
        Используется только если Celery недоступен.
        """
        from ..utils.image_processor import ImageProcessor
        from django.core.files.storage import default_storage

        entity = self.get_entity()
        entity_type = self.get_entity_type()

        # Сохраняем путь к временному файлу до перезаписи поля image
        temp_path = getattr(temp_image, 'name', None)

        processed_paths = ImageProcessor.process_image(
            temp_image,
            entity_type,
            entity_id=entity.id,
            photo_id=self.id
        )

        self.image = processed_paths['original']
        self.quality_level = processed_paths.get('quality_level', 'acceptable')
        self.quality_warning = processed_paths.get('quality_warning', False)

        super().save(update_fields=['image', 'quality_level', 'quality_warning'])

        # Удаляем временный файл из temp после успешной обработки
        try:
            if temp_path and default_storage.exists(temp_path):
                default_storage.delete(temp_path)
        except Exception:
            pass

    def delete(self, *args, **kwargs):
        """Удаляет все версии изображения при удалении фото"""
        import logging
        from ..utils.image_processor import ImageProcessor
        from django.core.files.storage import default_storage

        logger = logging.getLogger(__name__)

        if self.image:
            try:
                entity = self.get_entity()
                entity_type = self.get_entity_type()
                logger.info(f"[{self.__class__.__name__}.delete] Удаляем изображение: {self.image.name}")
                ImageProcessor.delete_all_versions(
                    entity_type,
                    self.image.name,
                    entity_id=entity.id,
                    photo_id=self.id
                )
                
                # Если фото так и осталось во временном пути (обработка не завершилась) — удаляем temp файл
                try:
                    if '/temp/' in self.image.name and default_storage.exists(self.image.name):
                        default_storage.delete(self.image.name)
                        logger.info(f"[{self.__class__.__name__}.delete] Deleted temp file: {self.image.name}")
                except Exception as del_exc:
                    logger.warning(f"[{self.__class__.__name__}.delete] Could not delete temp file {self.image.name}: {del_exc}")
                
                logger.info(f"[{self.__class__.__name__}.delete] ✓ Все версии изображения удалены")
            except Exception as e:
                logger.error(
                    f"[{self.__class__.__name__}.delete] ✗ Ошибка при удалении версий: {str(e)}",
                    exc_info=True
                )

        super().delete(*args, **kwargs)

    def get_thumbnail_url(self):
        """Получить URL миниатюры (150x150)"""
        from ..utils.image_service import ImageService
        return ImageService.get_thumbnail_url(self.image.name)

    def get_medium_url(self):
        """Получить URL среднего размера (400x400)"""
        from ..utils.image_service import ImageService
        return ImageService.get_medium_url(self.image.name)

    def get_large_url(self):
        """Получить URL большого размера (800x800)"""
        from ..utils.image_service import ImageService
        return ImageService.get_large_url(self.image.name)

    def get_original_url(self):
        """Получить URL оригинального изображения"""
        from ..utils.image_service import ImageService
        return ImageService.get_original_url(self.image.name)


class ProductPhoto(BasePhoto):
    """
    Модель для хранения фото товара (один товар может иметь несколько фото).
    Автоматически создает несколько размеров при загрузке: original, thumbnail, medium, large.

    Каждое фото автоматически оценивается по качеству на основе размера:
    - quality_level: Уровень качества (excellent/good/acceptable/poor/very_poor)
    - quality_warning: True если требует обновления перед выгрузкой на сайт

    МУЛЬТИТЕНАНТНОСТЬ:
    Файлы сохраняются с автоматическим добавлением tenant_id в путь.
    Структура на диске: media/tenants/{tenant_id}/products/{entity_id}/{photo_id}/{size}.ext
    В БД сохраняется (для экономии места): products/{entity_id}/{photo_id}/{size}.ext
    TenantAwareFileSystemStorage добавляет tenant_id автоматически при сохранении/удалении файлов.
    """
    QUALITY_LEVEL_CHOICES = [
        ('excellent', 'Отлично (>= 2052px)'),
        ('good', 'Хорошо (1512-2051px)'),
        ('acceptable', 'Приемлемо (864-1511px)'),
        ('poor', 'Плохо (432-863px)'),
        ('very_poor', 'Очень плохо (< 432px)'),
    ]

    product = models.ForeignKey(
        'Product',
        on_delete=models.CASCADE,
        related_name='photos',
        verbose_name="Товар"
    )
    image = models.ImageField(upload_to=get_product_photo_upload_path, verbose_name="Оригинальное фото")

    # Оценка качества
    quality_level = models.CharField(
        max_length=15,
        choices=QUALITY_LEVEL_CHOICES,
        default='acceptable',
        db_index=True,
        verbose_name="Уровень качества",
        help_text='Определяется автоматически на основе размера изображения'
    )
    quality_warning = models.BooleanField(
        default=False,
        db_index=True,
        verbose_name="Требует обновления",
        help_text='True если нужно обновить фото перед выгрузкой на сайт (poor или very_poor)'
    )

    class Meta:
        verbose_name = "Фото товара"
        verbose_name_plural = "Фото товаров"
        ordering = ['-is_main', 'order', '-created_at']
        indexes = [
            models.Index(fields=['quality_level']),
            models.Index(fields=['quality_warning']),
            models.Index(fields=['quality_warning', 'product']),  # Для поиска товаров требующих обновления фото
        ]
        constraints = [
            models.UniqueConstraint(
                fields=['product'],
                condition=Q(is_main=True),
                name='unique_main_photo_per_product'
            )
        ]

    def __str__(self):
        return f"Фото для {self.product.name} ({self.get_quality_level_display()})"

    def get_entity(self):
        """Возвращает связанный товар"""
        return self.product

    def get_entity_type(self):
        """Возвращает тип сущности для путей"""
        return 'products'


class ProductKitPhoto(BasePhoto):
    """
    Модель для хранения фото комплекта (один комплект может иметь несколько фото).
    Автоматически создает несколько размеров при загрузке: original, thumbnail, medium, large.

    Каждое фото автоматически оценивается по качеству на основе размера.

    МУЛЬТИТЕНАНТНОСТЬ:
    Файлы сохраняются с автоматическим добавлением tenant_id в путь.
    Структура на диске: media/tenants/{tenant_id}/kits/{entity_id}/{photo_id}/{size}.ext
    В БД сохраняется (для экономии места): kits/{entity_id}/{photo_id}/{size}.ext
    TenantAwareFileSystemStorage добавляет tenant_id автоматически при сохранении/удалении файлов.
    """
    QUALITY_LEVEL_CHOICES = [
        ('excellent', 'Отлично (>= 2052px)'),
        ('good', 'Хорошо (1512-2051px)'),
        ('acceptable', 'Приемлемо (864-1511px)'),
        ('poor', 'Плохо (432-863px)'),
        ('very_poor', 'Очень плохо (< 432px)'),
    ]

    kit = models.ForeignKey(
        'ProductKit',
        on_delete=models.CASCADE,
        related_name='photos',
        verbose_name="Комплект"
    )
    image = models.ImageField(upload_to=get_kit_photo_upload_path, verbose_name="Оригинальное фото")

    # Оценка качества
    quality_level = models.CharField(
        max_length=15,
        choices=QUALITY_LEVEL_CHOICES,
        default='acceptable',
        db_index=True,
        verbose_name="Уровень качества",
        help_text='Определяется автоматически на основе размера изображения'
    )
    quality_warning = models.BooleanField(
        default=False,
        db_index=True,
        verbose_name="Требует обновления",
        help_text='True если нужно обновить фото перед выгрузкой на сайт'
    )

    class Meta:
        verbose_name = "Фото комплекта"
        verbose_name_plural = "Фото комплектов"
        ordering = ['-is_main', 'order', '-created_at']
        indexes = [
            models.Index(fields=['quality_level']),
            models.Index(fields=['quality_warning']),
            models.Index(fields=['quality_warning', 'kit']),
        ]
        constraints = [
            models.UniqueConstraint(
                fields=['kit'],
                condition=Q(is_main=True),
                name='unique_main_photo_per_kit'
            )
        ]

    def __str__(self):
        return f"Фото для {self.kit.name} ({self.get_quality_level_display()})"

    def get_entity(self):
        """Возвращает связанный комплект"""
        return self.kit

    def get_entity_type(self):
        """Возвращает тип сущности для путей"""
        return 'kits'


class ProductCategoryPhoto(BasePhoto):
    """
    Модель для хранения фото категории (одна категория может иметь несколько фото).
    Автоматически создает несколько размеров при загрузке: original, thumbnail, medium, large.

    Каждое фото автоматически оценивается по качеству на основе размера.

    МУЛЬТИТЕНАНТНОСТЬ:
    Файлы сохраняются с автоматическим добавлением tenant_id в путь.
    Структура на диске: media/tenants/{tenant_id}/categories/{entity_id}/{photo_id}/{size}.ext
    В БД сохраняется (для экономии места): categories/{entity_id}/{photo_id}/{size}.ext
    TenantAwareFileSystemStorage добавляет tenant_id автоматически при сохранении/удалении файлов.
    """
    QUALITY_LEVEL_CHOICES = [
        ('excellent', 'Отлично (>= 2052px)'),
        ('good', 'Хорошо (1512-2051px)'),
        ('acceptable', 'Приемлемо (864-1511px)'),
        ('poor', 'Плохо (432-863px)'),
        ('very_poor', 'Очень плохо (< 432px)'),
    ]

    category = models.ForeignKey(
        'ProductCategory',
        on_delete=models.CASCADE,
        related_name='photos',
        verbose_name="Категория"
    )
    image = models.ImageField(upload_to=get_category_photo_upload_path, verbose_name="Оригинальное фото")

    # Оценка качества
    quality_level = models.CharField(
        max_length=15,
        choices=QUALITY_LEVEL_CHOICES,
        default='acceptable',
        db_index=True,
        verbose_name="Уровень качества",
        help_text='Определяется автоматически на основе размера изображения'
    )
    quality_warning = models.BooleanField(
        default=False,
        db_index=True,
        verbose_name="Требует обновления",
        help_text='True если нужно обновить фото перед выгрузкой на сайт'
    )

    class Meta:
        verbose_name = "Фото категории"
        verbose_name_plural = "Фото категорий"
        ordering = ['-is_main', 'order', '-created_at']
        indexes = [
            models.Index(fields=['quality_level']),
            models.Index(fields=['quality_warning']),
            models.Index(fields=['quality_warning', 'category']),
        ]
        constraints = [
            models.UniqueConstraint(
                fields=['category'],
                condition=Q(is_main=True),
                name='unique_main_photo_per_category'
            )
        ]

    def __str__(self):
        return f"Фото для {self.category.name} ({self.get_quality_level_display()})"

    def get_entity(self):
        """Возвращает связанную категорию"""
        return self.category

    def get_entity_type(self):
        """Возвращает тип сущности для путей"""
        return 'categories'


class PhotoProcessingStatus(models.Model):
    """
    Модель для отслеживания статуса асинхронной обработки фото через Celery.
    Используется для показа прогресса пользователю во время загрузки.

    Каждая загрузка фото создает запись с информацией о статусе обработки.
    Фронтенд опрашивает этот статус через API.
    """
    STATUS_CHOICES = [
        ('pending', 'В очереди'),
        ('processing', 'Обрабатывается'),
        ('completed', 'Завершено'),
        ('failed', 'Ошибка'),
    ]

    photo_id = models.IntegerField(
        verbose_name="ID фото",
        help_text='ID объекта ProductPhoto/ProductKitPhoto/ProductCategoryPhoto'
    )
    photo_model = models.CharField(
        max_length=100,
        verbose_name="Модель фото",
        help_text='Полный путь модели (e.g., products.ProductPhoto)'
    )
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='pending',
        db_index=True,
        verbose_name="Статус обработки"
    )
    task_id = models.CharField(
        max_length=255,
        blank=True,
        verbose_name="ID задачи Celery",
        help_text='Уникальный ID задачи для отслеживания',
        db_index=True
    )
    error_message = models.TextField(
        blank=True,
        verbose_name="Сообщение об ошибке",
        help_text='Детальное описание ошибки при обработке'
    )
    result_data = models.JSONField(
        default=dict,
        blank=True,
        verbose_name="Результаты обработки",
        help_text='JSON с информацией о качестве, путях и метаданных'
    )
    created_at = models.DateTimeField(
        auto_now_add=True,
        verbose_name="Дата создания"
    )
    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name="Дата обновления"
    )
    started_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name="Время начала обработки"
    )
    completed_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name="Время завершения обработки"
    )

    class Meta:
        verbose_name = "Статус обработки фото"
        verbose_name_plural = "Статусы обработки фото"
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['photo_id', 'photo_model']),
            models.Index(fields=['task_id']),
            models.Index(fields=['status']),
            models.Index(fields=['status', 'created_at']),
        ]

    def __str__(self):
        return f"{self.photo_model}#{self.photo_id} - {self.get_status_display()}"

    @property
    def is_processing(self):
        """Проверяет находится ли фото в обработке"""
        return self.status in ['pending', 'processing']

    @property
    def is_completed(self):
        """Проверяет завершена ли обработка успешно"""
        return self.status == 'completed'

    @property
    def is_failed(self):
        """Проверяет произошла ли ошибка"""
        return self.status == 'failed'
