"""
Сервис управления витринами - резервирование, продажа и разбор витринных букетов.

Новая архитектура с ShowcaseItem:
- ProductKit = шаблон (рецепт букета)
- ShowcaseItem = физический экземпляр на витрине
- Каждый ShowcaseItem имеет свой набор Reservation
- Защита от двойной продажи через OneToOneField на sold_order_item
"""
from decimal import Decimal
from django.db import transaction, IntegrityError
from django.utils import timezone
from django.core.exceptions import ValidationError

from inventory.models import Showcase, Reservation, Warehouse, ShowcaseItem, Stock
from products.models import ProductKit
from orders.models import Order, OrderItem, OrderStatus
from customers.models import Customer


class ShowcaseManager:
    """
    Менеджер для работы с витринами и витринными букетами.
    """

    @staticmethod
    def reserve_kit_to_showcase(product_kit, showcase, quantity=1):
        """
        Резервирует N экземпляров комплекта на витрину.
        Создаёт ShowcaseItem для каждого экземпляра и резервы компонентов.

        Args:
            product_kit: ProductKit - шаблон комплекта
            showcase: Showcase - витрина
            quantity: int - количество экземпляров (например, 5 одинаковых букетов)

        Returns:
            dict: {
                'success': bool,
                'showcase_items': list[ShowcaseItem],
                'reservations': list[Reservation],
                'message': str
            }
        """
        if not showcase.is_active:
            return {
                'success': False,
                'showcase_items': [],
                'reservations': [],
                'warnings': None,
                'message': f'Витрина "{showcase.name}" не активна'
            }

        warehouse = showcase.warehouse
        showcase_items = []
        all_reservations = []

        try:
            with transaction.atomic():
                kit_items = product_kit.kit_items.all()

                if not kit_items.exists():
                    return {
                        'success': False,
                        'showcase_items': [],
                        'reservations': [],
                        'warnings': None,
                        'message': f'Комплект "{product_kit.name}" не содержит компонентов'
                    }

                # Проверяем дефицит компонентов (предупреждение, не блокировка)
                warnings = []
                for kit_item in kit_items:
                    product = kit_item.product
                    if not product:
                        continue
                    qty_needed = (kit_item.quantity or Decimal('1')) * quantity
                    stock = Stock.objects.filter(product=product, warehouse=warehouse).first()
                    free_qty = stock.quantity_free if stock else Decimal('0')
                    if free_qty < qty_needed:
                        deficit = qty_needed - free_qty
                        warnings.append(f"{product.name}: не хватает {deficit} шт.")

                # Создаём N экземпляров
                for _ in range(quantity):
                    # 1. Создаём ShowcaseItem
                    showcase_item = ShowcaseItem.objects.create(
                        showcase=showcase,
                        product_kit=product_kit,
                        status='available'
                    )
                    showcase_items.append(showcase_item)

                    # 2. Создаём резервы ДЛЯ ЭТОГО ЭКЗЕМПЛЯРА
                    for kit_item in kit_items:
                        product_to_reserve = None

                        if kit_item.product:
                            product_to_reserve = kit_item.product
                        elif kit_item.variant_group:
                            # Группа вариантов - резервируем первый доступный вариант
                            variant_items = kit_item.variant_group.items.all()
                            if variant_items.exists():
                                product_to_reserve = variant_items.first().product

                        if product_to_reserve:
                            reservation = Reservation.objects.create(
                                product=product_to_reserve,
                                warehouse=warehouse,
                                showcase=showcase,
                                product_kit=product_kit,
                                showcase_item=showcase_item,  # Связь с экземпляром!
                                quantity=kit_item.quantity,
                                quantity_base=kit_item.quantity,  # Для корректного учёта в Stock
                                status='reserved'
                            )
                            all_reservations.append(reservation)

                # Обновляем агрегаты Stock
                affected_products = set(r.product_id for r in all_reservations)
                for product_id in affected_products:
                    stock, _ = Stock.objects.get_or_create(
                        product_id=product_id,
                        warehouse=warehouse
                    )
                    stock.refresh_from_batches()

                return {
                    'success': True,
                    'showcase_items': showcase_items,
                    'reservations': all_reservations,
                    'warnings': warnings if warnings else None,
                    'message': f'Создано {quantity} экз. комплекта "{product_kit.name}" на витрине "{showcase.name}"'
                }

        except Exception as e:
            return {
                'success': False,
                'showcase_items': [],
                'reservations': [],
                'warnings': None,
                'message': f'Ошибка резервирования: {str(e)}'
            }

    @staticmethod
    def sell_showcase_items(showcase_items, order_item):
        """
        Продаёт указанные экземпляры с витрины.
        Привязывает каждый ShowcaseItem к OrderItem и конвертирует резервы в продажи.

        Args:
            showcase_items: list[ShowcaseItem] - экземпляры для продажи
            order_item: OrderItem - позиция заказа

        Returns:
            dict: {
                'success': bool,
                'sold_count': int,
                'message': str
            }

        Raises:
            IntegrityError: если экземпляр уже был продан (защита на уровне БД)
        """
        sold_count = 0
        order = order_item.order

        try:
            with transaction.atomic():
                # Собираем ID для перезагрузки с блокировкой
                showcase_item_ids = [item.id for item in showcase_items]
                
                # Перезагружаем объекты из БД с блокировкой для актуального статуса
                showcase_items_locked = list(
                    ShowcaseItem.objects.select_for_update().filter(
                        id__in=showcase_item_ids
                    )
                )
                
                for showcase_item in showcase_items_locked:
                    # Проверяем статус перед продажей
                    if showcase_item.status == 'sold':
                        raise ValidationError(
                            f'Экземпляр "{showcase_item}" уже продан'
                        )
                    if showcase_item.status == 'dismantled':
                        raise ValidationError(
                            f'Экземпляр "{showcase_item}" был разобран'
                        )

                    # Помечаем как проданный (OneToOneField защитит от дублей!)
                    showcase_item.mark_sold(order_item)

                    # Конвертируем резервы этого экземпляра в продажи
                    reservations = Reservation.objects.filter(
                        showcase_item=showcase_item,
                        status='reserved'
                    )

                    for reservation in reservations:
                        # Проверяем что резерв ещё НЕ обработан
                        if reservation.status == 'converted_to_sale':
                            # Этот резерв уже преобразован в продажу, пропускаем
                            continue
                        
                        # Сначала устанавливаем order_item для правильного определения цены
                        reservation.order_item = order_item
                        # ВАЖНО: Мы НЕ создаём продажу (Sale) здесь и НЕ меняем статус на 'converted_to_sale'.
                        # Это сделает сигнал create_sale_on_order_completion автоматически.
                        # Таким образом обеспечивается единая точка создания продаж для всех типов товаров.
                        reservation.save()

                    sold_count += 1

                return {
                    'success': True,
                    'sold_count': sold_count,
                    'message': f'Продано {sold_count} экз.'
                }

        except ValidationError as e:
            # Ошибка валидации (например, экземпляр уже продан)
            return {
                'success': False,
                'sold_count': 0,
                'message': str(e)
            }

    @staticmethod
    def sell_from_showcase(product_kit, showcase, customer, payment_method='cash_to_courier',
                          custom_price=None, user=None, quantity=1):
        """
        Продаёт N экземпляров комплекта с витрины.
        Создаёт Order, OrderItem, выбирает доступные ShowcaseItem и продаёт их.

        Args:
            product_kit: ProductKit - шаблон комплекта
            showcase: Showcase - витрина
            customer: Customer - покупатель
            payment_method: str - способ оплаты
            custom_price: Decimal - кастомная цена за единицу (опционально)
            user: CustomUser - пользователь, выполняющий операцию
            quantity: int - количество экземпляров для продажи

        Returns:
            dict: {
                'success': bool,
                'order': Order or None,
                'sold_count': int,
                'message': str
            }
        """
        warehouse = showcase.warehouse

        try:
            with transaction.atomic():
                # Находим доступные экземпляры этого комплекта на витрине
                available_items = ShowcaseItem.objects.select_for_update(
                    skip_locked=True
                ).filter(
                    showcase=showcase,
                    product_kit=product_kit,
                    status='available'
                )[:quantity]

                available_items = list(available_items)

                if len(available_items) < quantity:
                    return {
                        'success': False,
                        'order': None,
                        'sold_count': 0,
                        'message': f'Доступно только {len(available_items)} из {quantity} запрошенных экземпляров'
                    }

                # Получаем статус "Завершён" для POS-продаж
                completed_status = OrderStatus.objects.filter(
                    code='completed',
                    is_positive_end=True
                ).first()

                if not completed_status:
                    completed_status = OrderStatus.objects.filter(
                        is_positive_end=True
                    ).first()

                # Создаём заказ
                order = Order.objects.create(
                    customer=customer,
                    status=completed_status,
                    is_paid=True,
                    modified_by=user
                )
                
                # Создаём доставку (самовывоз)
                from orders.models import Delivery
                from django.utils import timezone as tz
                now_local = tz.localtime(tz.now())
                
                Delivery.objects.create(
                    order=order,
                    delivery_type=Delivery.DELIVERY_TYPE_PICKUP,
                    pickup_warehouse=warehouse,
                    delivery_date=now_local.date(),
                    time_from=now_local.time(),
                    time_to=now_local.time(),
                    cost=0
                )

                # Определяем цену
                price = custom_price if custom_price else product_kit.actual_price
                is_custom = custom_price is not None

                # Создаём позицию заказа
                order_item = OrderItem.objects.create(
                    order=order,
                    product_kit=product_kit,
                    quantity=len(available_items),
                    price=price,
                    is_custom_price=is_custom,
                    is_from_showcase=True,
                    showcase=showcase
                )

                # Продаём экземпляры
                result = ShowcaseManager.sell_showcase_items(available_items, order_item)

                if not result['success']:
                    raise ValidationError(result['message'])

                # Пересчитываем итоговую сумму заказа
                order.calculate_total()
                order.amount_paid = order.total_amount
                order.update_payment_status()
                order.save()

                return {
                    'success': True,
                    'order': order,
                    'sold_count': result['sold_count'],
                    'message': f'Заказ #{order.order_number} создан. Продано {result["sold_count"]} экз. с витрины "{showcase.name}"'
                }

        except Exception as e:
            return {
                'success': False,
                'order': None,
                'sold_count': 0,
                'message': f'Ошибка продажи: {str(e)}'
            }

    @staticmethod
    def reserve_product_to_showcase(product, showcase, product_kit, quantity_per_item):
        """
        Дозарезервировать товар для всех АКТИВНЫХ экземпляров витринного комплекта.
        НЕ блокирует при нехватке товара - допускает резерв "в минус".

        Args:
            product: Product – товар-компонент
            showcase: Showcase – витрина
            product_kit: ProductKit – шаблон витринного комплекта
            quantity_per_item: Decimal или int – насколько увеличить количество
                               этого товара в ОДНОМ экземпляре букета

        Returns:
            dict: {
                'success': bool,
                'overdraft': Decimal,  # сколько не хватает (может быть 0)
                'message': str
            }
        """
        from decimal import Decimal

        if not showcase or not product_kit:
            return {
                'success': True,
                'overdraft': Decimal('0'),
                'message': 'Нет витрины или комплекта – резервы не изменены'
            }

        quantity_per_item = Decimal(str(quantity_per_item))
        if quantity_per_item <= 0:
            return {
                'success': True,
                'overdraft': Decimal('0'),
                'message': 'Изменение количества не требует дополнительного резерва'
            }

        # Берём только актуальные экземпляры на витрине
        # (available, in_cart, reserved — все физически присутствующие)
        active_items = ShowcaseItem.objects.filter(
            showcase=showcase,
            product_kit=product_kit,
            status__in=['available', 'in_cart', 'reserved'],
        )

        item_count = active_items.count()
        if item_count == 0:
            return {
                'success': True,
                'overdraft': Decimal('0'),
                'message': 'Нет активных экземпляров на витрине – резервы не изменены'
            }

        warehouse = showcase.warehouse
        total_needed = quantity_per_item * item_count

        # Проверяем, хватает ли свободного остатка (НЕ блокируем, только считаем дефицит)
        try:
            stock = Stock.objects.get(product=product, warehouse=warehouse)
            free_qty = stock.quantity_free
        except Stock.DoesNotExist:
            free_qty = Decimal('0')

        overdraft = max(Decimal('0'), total_needed - free_qty)

        # Создаём/увеличиваем резервы по каждому экземпляру (даже если не хватает!)
        with transaction.atomic():
            for showcase_item in active_items:
                reservation, created = Reservation.objects.get_or_create(
                    product=product,
                    warehouse=warehouse,
                    showcase=showcase,
                    product_kit=product_kit,
                    showcase_item=showcase_item,
                    status='reserved',
                    defaults={'quantity': quantity_per_item, 'quantity_base': quantity_per_item},
                )
                if not created:
                    reservation.quantity = (reservation.quantity or Decimal('0')) + quantity_per_item
                    reservation.quantity_base = (reservation.quantity_base or Decimal('0')) + quantity_per_item
                    reservation.save(update_fields=['quantity', 'quantity_base'])

        return {
            'success': True,
            'overdraft': overdraft,
            'message': (
                f'Зарезервировано {total_needed} ед. товара "{product.name}"'
                + (f' (не хватает {overdraft})' if overdraft > 0 else '')
            ),
        }

    @staticmethod
    def release_showcase_reservation(product, showcase, product_kit, quantity_per_item):
        """
        Освободить часть резерва товара для всех АКТИВНЫХ экземпляров витринного комплекта.

        Args:
            product: Product – товар-компонент
            showcase: Showcase – витрина
            product_kit: ProductKit – шаблон витринного комплекта
            quantity_per_item: Decimal или int – насколько уменьшить количество
                               этого товара в ОДНОМ экземпляре букета

        Returns:
            dict: {
                'success': bool,
                'released': Decimal,
                'message': str
            }
        """
        from decimal import Decimal

        if not showcase or not product_kit:
            return {
                'success': True,
                'released': Decimal('0'),
                'message': 'Нет витрины или комплекта – резервы не изменены',
            }

        quantity_per_item = Decimal(str(quantity_per_item))
        if quantity_per_item <= 0:
            return {
                'success': True,
                'released': Decimal('0'),
                'message': 'Изменение количества не требует освобождения резерва',
            }

        active_items = ShowcaseItem.objects.filter(
            showcase=showcase,
            product_kit=product_kit,
            status__in=['available', 'in_cart', 'reserved'],
        )

        if not active_items.exists():
            return {
                'success': True,
                'released': Decimal('0'),
                'message': 'Нет активных экземпляров на витрине – резервы не изменены',
            }

        released_total = Decimal('0')

        with transaction.atomic():
            reservations = Reservation.objects.filter(
                showcase_item__in=active_items,
                product=product,
                product_kit=product_kit,
                showcase=showcase,
                warehouse=showcase.warehouse,
                status='reserved',
            )

            for res in reservations:
                old_qty = res.quantity or Decimal('0')
                new_qty = old_qty - quantity_per_item

                if new_qty > 0:
                    res.quantity = new_qty
                    res.quantity_base = new_qty
                    res.save(update_fields=['quantity', 'quantity_base'])
                    released_amount = quantity_per_item
                else:
                    # Полностью освобождаем резерв
                    released_amount = old_qty
                    res.status = 'released'
                    res.released_at = timezone.now()
                    res.save(update_fields=['status', 'released_at'])

                released_total += released_amount

        return {
            'success': True,
            'released': released_total,
            'message': f'Освобождено {released_total} ед. товара "{product.name}"',
        }

    @staticmethod
    def dismantle_showcase_item(showcase_item):
        """
        Разбирает один экземпляр на витрине - освобождает его резервы.

        Args:
            showcase_item: ShowcaseItem - экземпляр для разбора

        Returns:
            dict: {
                'success': bool,
                'released_count': int,
                'message': str
            }
        """
        if showcase_item.status == 'sold':
            return {
                'success': False,
                'released_count': 0,
                'message': 'Нельзя разобрать проданный экземпляр'
            }

        try:
            with transaction.atomic():
                warehouse = showcase_item.showcase.warehouse

                # Находим резервы этого экземпляра
                reservations = Reservation.objects.filter(
                    showcase_item=showcase_item,
                    status='reserved'
                )

                released_count = reservations.count()
                affected_products = list(reservations.values_list('product_id', flat=True).distinct())

                # Освобождаем резервы
                reservations.update(
                    status='released',
                    released_at=timezone.now(),
                    showcase=None,
                    showcase_item=None
                )

                # Обновляем статус экземпляра
                showcase_item.status = 'dismantled'
                showcase_item.save()

                # Обновляем агрегаты Stock
                for product_id in affected_products:
                    try:
                        stock = Stock.objects.get(
                            product_id=product_id,
                            warehouse=warehouse
                        )
                        stock.refresh_from_batches()
                    except Stock.DoesNotExist:
                        pass

                return {
                    'success': True,
                    'released_count': released_count,
                    'message': f'Экземпляр разобран, освобождено {released_count} резервов'
                }

        except Exception as e:
            return {
                'success': False,
                'released_count': 0,
                'message': f'Ошибка разбора: {str(e)}'
            }

    @staticmethod
    def dismantle_from_showcase(showcase, product_kit=None):
        """
        Разбирает все экземпляры на витрине (или конкретного комплекта).
        Освобождает резервы.

        Args:
            showcase: Showcase - витрина
            product_kit: ProductKit - конкретный комплект (опционально)

        Returns:
            dict: {
                'success': bool,
                'released_count': int,
                'dismantled_items_count': int,
                'message': str
            }
        """
        try:
            with transaction.atomic():
                # Находим экземпляры для разбора
                items_qs = ShowcaseItem.objects.filter(
                    showcase=showcase,
                    status='available'
                )

                if product_kit:
                    items_qs = items_qs.filter(product_kit=product_kit)

                items_to_dismantle = list(items_qs)

                if not items_to_dismantle:
                    return {
                        'success': False,
                        'released_count': 0,
                        'dismantled_items_count': 0,
                        'message': f'На витрине "{showcase.name}" нет доступных экземпляров для разбора'
                    }

                total_released = 0
                for item in items_to_dismantle:
                    result = ShowcaseManager.dismantle_showcase_item(item)
                    if result['success']:
                        total_released += result['released_count']

                return {
                    'success': True,
                    'released_count': total_released,
                    'dismantled_items_count': len(items_to_dismantle),
                    'message': f'Разобрано {len(items_to_dismantle)} экз., освобождено {total_released} резервов'
                }

        except Exception as e:
            return {
                'success': False,
                'released_count': 0,
                'dismantled_items_count': 0,
                'message': f'Ошибка разбора: {str(e)}'
            }

    @staticmethod
    def write_off_from_showcase(showcase_item, reason='spoilage', notes=None, created_by=None):
        """
        Списывает экземпляр витринного комплекта:
        1. Создаёт документ списания с компонентами комплекта
        2. Преобразует резервы комплекта в позиции документа списания
        3. Помечает экземпляр как разобранный

        Args:
            showcase_item: ShowcaseItem - экземпляр для списания
            reason: str - причина списания (spoilage по умолчанию)
            notes: str - примечания
            created_by: User - пользователь

        Returns:
            dict: {
                'success': bool,
                'document_id': int,
                'document_number': str,
                'items_count': int,
                'message': str,
                'error': str (при ошибке)
            }
        """
        from inventory.services.writeoff_document_service import WriteOffDocumentService

        # Проверка статуса
        if showcase_item.status == 'sold':
            return {
                'success': False,
                'document_id': None,
                'message': 'Нельзя списать проданный экземпляр'
            }

        if showcase_item.status == 'dismantled':
            return {
                'success': False,
                'document_id': None,
                'message': 'Экземпляр уже разобран'
            }

        try:
            with transaction.atomic():
                warehouse = showcase_item.showcase.warehouse
                product_kit = showcase_item.product_kit

                # Создаём документ списания (черновик)
                document = WriteOffDocumentService.create_document(
                    warehouse=warehouse,
                    date=timezone.now().date(),
                    notes=f'Списание витринного комплекта: {product_kit.name}',
                    created_by=created_by
                )

                # Получаем резервы этого экземпляра
                reservations = Reservation.objects.filter(
                    showcase_item=showcase_item,
                    status='reserved'
                ).select_related('product')

                items_count = 0

                for reservation in reservations:
                    # Добавляем позицию в документ списания
                    # Используем add_item без создания резерва (меняем статус существующего)
                    from inventory.models import WriteOffDocumentItem

                    item = WriteOffDocumentItem.objects.create(
                        document=document,
                        product=reservation.product,
                        quantity=reservation.quantity,
                        reason=reason,
                        notes=notes
                    )

                    # Привязываем существующий резерв к позиции документа
                    reservation.writeoff_document_item = item
                    reservation.status = 'converted_to_writeoff'
                    reservation.converted_at = timezone.now()
                    reservation.save(update_fields=['writeoff_document_item', 'status', 'converted_at'])

                    items_count += 1

                # Помечаем экземпляр как разобранный
                showcase_item.status = 'dismantled'
                showcase_item.save(update_fields=['status'])

                # Помечаем шаблон комплекта как снятый
                if product_kit.status != 'discontinued':
                    product_kit.status = 'discontinued'
                    product_kit.save(update_fields=['status'])

                return {
                    'success': True,
                    'document_id': document.id,
                    'document_number': document.document_number,
                    'items_count': items_count,
                    'message': f'Создан документ {document.document_number} с {items_count} позициями'
                }

        except Exception as e:
            return {
                'success': False,
                'document_id': None,
                'message': f'Ошибка списания: {str(e)}'
            }

    @staticmethod
    def get_showcase_items_for_pos(showcase=None):
        """
        Возвращает витринные экземпляры, сгруппированные по шаблону комплекта.
        Для использования в POS интерфейсе.

        Args:
            showcase: Showcase - конкретная витрина (опционально, если None - все активные)

        Returns:
            list: [
                {
                    'kit_id': int,
                    'kit_name': str,
                    'kit_sku': str,
                    'price': Decimal,
                    'available_count': int,
                    'showcase_item_ids': list[int],
                    'showcase_id': int,
                    'showcase_name': str,
                    'type': 'showcase_kit'
                },
                ...
            ]
        """
        from django.db.models import Count

        # Базовый queryset
        qs = ShowcaseItem.objects.filter(
            status='available',
            showcase__is_active=True
        )

        if showcase:
            qs = qs.filter(showcase=showcase)

        # Группируем по (product_kit, showcase)
        grouped = qs.values(
            'product_kit_id',
            'product_kit__name',
            'product_kit__sku',
            'product_kit__price',
            'product_kit__sale_price',
            'showcase_id',
            'showcase__name'
        ).annotate(
            available_count=Count('id')
        ).order_by('showcase__name', 'product_kit__name')

        result = []
        for item in grouped:
            # Получаем IDs всех доступных экземпляров этой группы
            item_ids = list(ShowcaseItem.objects.filter(
                product_kit_id=item['product_kit_id'],
                showcase_id=item['showcase_id'],
                status='available'
            ).values_list('id', flat=True))

            # Определяем актуальную цену
            price = item['product_kit__sale_price'] or item['product_kit__price']

            result.append({
                'kit_id': item['product_kit_id'],
                'kit_name': item['product_kit__name'],
                'kit_sku': item['product_kit__sku'] or '',
                'price': str(price),
                'available_count': item['available_count'],
                'showcase_item_ids': item_ids,
                'showcase_id': item['showcase_id'],
                'showcase_name': item['showcase__name'],
                'type': 'showcase_kit'
            })

        return result

    @staticmethod
    def get_showcase_kits(showcase):
        """
        Возвращает список компонентов, зарезервированных на витрине.
        (Для совместимости со старым API)

        Args:
            showcase: Showcase

        Returns:
            list: список словарей с информацией о компонентах
        """
        reservations = Reservation.objects.filter(
            showcase=showcase,
            status='reserved'
        ).select_related('product').values('product__id', 'product__name', 'quantity')

        # Группируем по товарам
        products_dict = {}
        for res in reservations:
            product_id = res['product__id']
            if product_id not in products_dict:
                products_dict[product_id] = {
                    'product_name': res['product__name'],
                    'quantity': Decimal('0')
                }
            products_dict[product_id]['quantity'] += res['quantity']

        return [
            {
                'product_id': pid,
                'product_name': data['product_name'],
                'quantity': data['quantity']
            }
            for pid, data in products_dict.items()
        ]

    @staticmethod
    def lock_showcase_items_for_cart(showcase_item_ids, user, session_id=None, duration_minutes=30):
        """
        Блокирует указанные экземпляры для корзины.

        Args:
            showcase_item_ids: list[int] - ID экземпляров для блокировки
            user: User - пользователь (кассир)
            session_id: str - ID сессии корзины
            duration_minutes: int - длительность блокировки в минутах

        Returns:
            dict: {
                'success': bool,
                'locked_item_ids': list[int],
                'lock_expires_at': datetime,
                'message': str
            }
        """
        from datetime import timedelta

        try:
            with transaction.atomic():
                # Выбираем и блокируем экземпляры
                items = ShowcaseItem.objects.select_for_update(
                    skip_locked=True
                ).filter(
                    id__in=showcase_item_ids,
                    status='available'
                )

                items = list(items)

                if len(items) < len(showcase_item_ids):
                    # Некоторые экземпляры недоступны
                    unavailable_count = len(showcase_item_ids) - len(items)
                    return {
                        'success': False,
                        'locked_item_ids': [],
                        'lock_expires_at': None,
                        'message': f'{unavailable_count} экземпляр(ов) недоступны. Возможно, они уже в корзине или проданы.'
                    }

                lock_expires_at = timezone.now() + timedelta(minutes=duration_minutes)
                locked_ids = []

                for item in items:
                    item.lock_for_cart(user, session_id, duration_minutes)
                    locked_ids.append(item.id)

                return {
                    'success': True,
                    'locked_item_ids': locked_ids,
                    'lock_expires_at': lock_expires_at,
                    'message': f'Заблокировано {len(locked_ids)} экз.'
                }

        except Exception as e:
            return {
                'success': False,
                'locked_item_ids': [],
                'lock_expires_at': None,
                'message': f'Ошибка блокировки: {str(e)}'
            }

    @staticmethod
    def release_showcase_items_from_cart(showcase_item_ids, user):
        """
        Снимает блокировку с указанных экземпляров.

        Args:
            showcase_item_ids: list[int] - ID экземпляров
            user: User - пользователь (только владелец блокировки может снять)

        Returns:
            dict: {
                'success': bool,
                'released_count': int,
                'message': str
            }
        """
        try:
            # Снимаем блокировку только для экземпляров, заблокированных этим пользователем
            updated = ShowcaseItem.objects.filter(
                id__in=showcase_item_ids,
                status='in_cart',
                locked_by_user=user
            ).update(
                status='available',
                locked_by_user=None,
                cart_lock_expires_at=None,
                cart_session_id=None
            )

            return {
                'success': True,
                'released_count': updated,
                'message': f'Освобождено {updated} экз.'
            }

        except Exception as e:
            return {
                'success': False,
                'released_count': 0,
                'message': f'Ошибка освобождения: {str(e)}'
            }
