# -*- coding: utf-8 -*-
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from django.db import transaction
from django.db.models import Prefetch, OuterRef, Subquery, DecimalField, F, Case, When, CharField
from django.db.models.functions import Coalesce
from django.utils import timezone
from django.core.exceptions import ValidationError
from decimal import Decimal, InvalidOperation
import json
import logging

from products.models import Product, ProductCategory, ProductKit, KitItem
from inventory.models import Showcase, Reservation, Warehouse, Stock, ShowcaseItem
from inventory.services import ShowcaseManager
from inventory.signals import skip_sale_creation, reset_sale_creation

logger = logging.getLogger(__name__)


def get_pos_warehouse(request):
    """
    Получить текущий склад для POS из сессии или выбрать дефолтный.
    Логика выбора:
    1. Если в сессии есть pos_warehouse_id - используем его
    2. Иначе берем склад с is_default=True
    3. Если нет is_default - берем первый активный
    4. Если нет активных складов - None
    """
    warehouse_id = request.session.get('pos_warehouse_id')
    
    if warehouse_id:
        try:
            return Warehouse.objects.get(id=warehouse_id, is_active=True)
        except Warehouse.DoesNotExist:
            # Склад был удален или деактивирован - сбрасываем сессию
            request.session.pop('pos_warehouse_id', None)
    
    # Ищем склад по умолчанию
    warehouse = Warehouse.objects.filter(is_active=True, is_default=True).first()
    
    if not warehouse:
        # Берем любой первый активный
        warehouse = Warehouse.objects.filter(is_active=True).first()
    
    # Сохраняем в сессию для следующих запросов
    if warehouse:
        request.session['pos_warehouse_id'] = warehouse.id
    
    return warehouse


def get_showcase_kits_for_pos():
    """
    Получает витринные комплекты для отображения в POS.

    НОВАЯ АРХИТЕКТУРА с ShowcaseItem:
    - Группирует экземпляры по (product_kit, showcase)
    - Показывает только ДОСТУПНЫЕ букеты (available)
    - В корзине (in_cart) тоже показываем для информации о текущих блокировках
    - Зарезервированные (reserved) НЕ показываем — они заняты под другие заказы
    - Возвращает available_count (сколько можно добавить) и total_count (всего видимых)
    """
    from products.models import ProductKitPhoto
    from inventory.models import ShowcaseItem
    from django.db.models import Count, Q

    # Группируем ShowcaseItem по (product_kit, showcase)
    # Включаем только available и in_cart (в корзине текущего пользователя)
    # reserved НЕ показываем - они заняты под отложенные заказы
    all_items = ShowcaseItem.objects.filter(
        status__in=['available', 'in_cart'],
        showcase__is_active=True
    ).select_related(
        'product_kit',
        'showcase'
    ).values(
        'product_kit_id',
        'product_kit__name',
        'product_kit__sku',
        'product_kit__price',
        'product_kit__sale_price',
        'product_kit__base_price',
        'product_kit__showcase_created_at',
        'showcase_id',
        'showcase__name'
    ).annotate(
        total_count=Count('id'),
        available_count=Count('id', filter=Q(status='available'))
    ).order_by('showcase__name', 'product_kit__name')

    if not all_items:
        return []

    # Получаем ID всех комплектов для загрузки фото
    kit_ids = list(set(item['product_kit_id'] for item in all_items))

    # Загружаем первые фото для комплектов
    kit_photos = {}
    photos = ProductKitPhoto.objects.filter(
        kit_id__in=kit_ids
    ).order_by('kit_id', 'order')

    for photo in photos:
        if photo.kit_id not in kit_photos:
            if photo and photo.image:
                thumbnail_url = photo.get_thumbnail_url()
                # Если миниатюра не найдена, возвращаем None (без фото)
                if not thumbnail_url:
                    thumbnail_url = None
                kit_photos[photo.kit_id] = thumbnail_url

    # Загружаем состав комплектов для проверки актуальности цен
    kit_items_data = {}
    for ki in KitItem.objects.filter(kit_id__in=kit_ids).select_related('product'):
        if ki.kit_id not in kit_items_data:
            kit_items_data[ki.kit_id] = []
        kit_items_data[ki.kit_id].append(ki)

    # Считаем актуальные цены для каждого комплекта
    kit_actual_prices = {}
    for kit_id, items in kit_items_data.items():
        actual_price = sum((ki.product.actual_price or 0) * (ki.quantity or 0) for ki in items)
        kit_actual_prices[kit_id] = actual_price

    # Формируем результат
    showcase_kits = []
    for item in all_items:
        kit_id = item['product_kit_id']
        showcase_id = item['showcase_id']

        # Получаем IDs только ДОСТУПНЫХ экземпляров этой группы
        available_item_ids = list(ShowcaseItem.objects.filter(
            product_kit_id=kit_id,
            showcase_id=showcase_id,
            status='available'
        ).values_list('id', flat=True))

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

        # Проверяем актуальность цены (сравниваем сохранённую цену с актуальной ценой товаров)
        actual_price = kit_actual_prices.get(kit_id, Decimal('0'))
        base_price = item['product_kit__base_price']
        price_outdated = base_price and abs(float(base_price) - float(actual_price)) > 0.01

        showcase_kits.append({
            'id': kit_id,
            'name': item['product_kit__name'],
            'price': str(price),
            'category_ids': [],
            'in_stock': item['available_count'] > 0,  # Есть ли доступные
            'sku': item['product_kit__sku'] or '',
            'image': kit_photos.get(kit_id),
            'type': 'showcase_kit',
            'showcase_name': item['showcase__name'],
            'showcase_id': showcase_id,
            # Количества
            'available_count': item['available_count'],  # Сколько можно добавить
            'total_count': item['total_count'],          # Всего на витрине (включая в корзине)
            'showcase_item_ids': available_item_ids,     # IDs только доступных
            # Флаг неактуальной цены
            'price_outdated': price_outdated,
            # Дата размещения на витрине
            'showcase_created_at': item.get('product_kit__showcase_created_at')
        })

    return showcase_kits


@login_required
def pos_terminal(request):
    """
    Tablet-friendly POS screen prototype.
    Товары загружаются прогрессивно через API при клике на категорию.
    Работает только с одним выбранным складом.
    """
    from customers.models import Customer
    from django.core.cache import cache

    # Получаем текущий склад для POS
    current_warehouse = get_pos_warehouse(request)

    if not current_warehouse:
        # Нет активных складов - информация отображается в блоке склада в шаблоне
        # Получаем системного клиента для корректного рендеринга JSON в шаблоне
        system_customer, _ = Customer.get_or_create_system_customer()
        context = {
            'categories_json': json.dumps([]),
            'items_json': json.dumps([]),
            'showcase_kits_json': json.dumps([]),
            'current_warehouse': None,
            'warehouses': [],
            'system_customer_json': json.dumps({
                'id': system_customer.id,
                'name': system_customer.name,
                'wallet_balance': float(system_customer.wallet_balance)
            }),
            'selected_customer_json': json.dumps({
                'id': system_customer.id,
                'name': system_customer.name,
                'wallet_balance': float(system_customer.wallet_balance)
            }),
            'cart_data': json.dumps({}),
            'title': 'POS Terminal',
        }
        return render(request, 'pos/terminal.html', context)

    # Получаем или создаём системного клиента
    system_customer, _ = Customer.get_or_create_system_customer()

    # Пытаемся получить сохраненного клиента из Redis
    selected_customer = None
    redis_key = f'pos:customer:{request.user.id}:{current_warehouse.id}'
    cached_customer_data = cache.get(redis_key)

    if cached_customer_data:
        # Проверяем что клиент еще существует в БД
        try:
            customer = Customer.objects.get(id=cached_customer_data['customer_id'])
            selected_customer = {
                'id': customer.id,
                'name': customer.name,
                'wallet_balance': float(customer.wallet_balance)
            }
        except Customer.DoesNotExist:
            # Клиент был удален - очищаем кэш
            cache.delete(redis_key)

    # Если нет сохраненного клиента - используем системного
    if not selected_customer:
        selected_customer = {
            'id': system_customer.id,
            'name': system_customer.name,
            'wallet_balance': float(system_customer.wallet_balance)
        }

    # Пытаемся получить сохраненную корзину из Redis
    cart_redis_key = f'pos:cart:{request.user.id}:{current_warehouse.id}'
    cached_cart_data = cache.get(cart_redis_key)
    cart_data = {}

    if cached_cart_data:
        # Валидируем товары и комплекты в корзине
        from products.models import Product, ProductKit
        from inventory.models import ShowcaseItem

        for cart_key, item in cached_cart_data.items():
            try:
                if item['type'] == 'product':
                    # Проверяем что товар существует
                    Product.objects.get(id=item['id'])
                    cart_data[cart_key] = item
                elif item['type'] == 'kit':
                    # Обычный комплект - только проверяем существование
                    ProductKit.objects.get(id=item['id'])
                    cart_data[cart_key] = item
                elif item['type'] == 'showcase_kit':
                    # Витринный комплект - проверяем существование И актуальность блокировки
                    kit = ProductKit.objects.get(id=item['id'])

                    # Получаем showcase_item_ids из корзины (если есть)
                    showcase_item_ids = item.get('showcase_item_ids', [])

                    if showcase_item_ids:
                        # Проверяем, что все указанные ShowcaseItem заблокированы на текущего пользователя
                        from accounts.models import CustomUser
                        if isinstance(request.user, CustomUser):
                            locked_items = ShowcaseItem.objects.filter(
                                id__in=showcase_item_ids,
                                product_kit=kit,
                                status='in_cart',
                                locked_by_user=request.user,
                                cart_lock_expires_at__gt=timezone.now()
                            )
                        else:
                            # Для PlatformAdmin используем проверку по сессии
                            session_id = request.session.session_key or ''
                            locked_items = ShowcaseItem.objects.filter(
                                id__in=showcase_item_ids,
                                product_kit=kit,
                                status='in_cart',
                                cart_session_id=session_id,
                                cart_lock_expires_at__gt=timezone.now()
                            )

                        locked_count = locked_items.count()

                        if locked_count > 0:
                            # Обновляем количество на актуальное (на случай если часть блокировок истекла)
                            item['qty'] = locked_count
                            item['showcase_item_ids'] = list(locked_items.values_list('id', flat=True))
                            cart_data[cart_key] = item
                        # Если ни один не заблокирован - не добавляем в корзину
                    # Если showcase_item_ids отсутствует - не добавляем (устаревший формат)
            except (Product.DoesNotExist, ProductKit.DoesNotExist):
                # Товар или комплект удален - пропускаем
                continue

    # Загружаем только категории
    categories_qs = ProductCategory.objects.filter(is_active=True)
    categories = [{'id': c.id, 'name': c.name} for c in categories_qs]

    # Список всех активных складов для модалки выбора
    warehouses = Warehouse.objects.filter(is_active=True).order_by('-is_default', 'name')
    warehouses_list = [{
        'id': w.id,
        'name': w.name,
        'is_default': w.is_default
    } for w in warehouses]

    context = {
        'categories_json': json.dumps(categories),
        'items_json': json.dumps([]),  # Пустой массив - загрузка по API при клике на категорию
        'showcase_kits_json': json.dumps([]),  # Пустой массив - загрузка по API
        'categories': categories_qs,  # QuerySet для компонента product_search_picker
        'current_warehouse': {
            'id': current_warehouse.id,
            'name': current_warehouse.name
        },
        'warehouses': warehouses_list,
        'system_customer_json': json.dumps({
            'id': system_customer.id,
            'name': system_customer.name,
            'wallet_balance': float(system_customer.wallet_balance)
        }),
        'selected_customer_json': json.dumps(selected_customer),  # Текущий выбранный клиент (из Redis или системный)
        'cart_data': json.dumps(cart_data),  # Сохраненная корзина из Redis
        'title': 'POS Terminal',
    }
    return render(request, 'pos/terminal.html', context)


@login_required
@require_http_methods(["POST"])
def save_cart(request):
    """
    Сохранить корзину POS в Redis для текущего пользователя и склада.
    TTL: 2 часа (7200 секунд)
    """
    from django.core.cache import cache
    import json

    # Получаем текущий склад
    current_warehouse = get_pos_warehouse(request)
    if not current_warehouse:
        return JsonResponse({'success': False, 'error': 'Не выбран активный склад'}, status=400)

    try:
        # Получаем данные корзины из тела запроса
        body = json.loads(request.body)
        cart_data = body.get('cart', {})

        # Валидация структуры данных корзины
        if not isinstance(cart_data, dict):
            return JsonResponse({'success': False, 'error': 'Неверный формат данных корзины'}, status=400)

        # Сохраняем в Redis
        redis_key = f'pos:cart:{request.user.id}:{current_warehouse.id}'
        cache.set(redis_key, cart_data, timeout=7200)  # 2 часа

        return JsonResponse({
            'success': True,
            'items_count': len(cart_data)
        })

    except json.JSONDecodeError:
        return JsonResponse({'success': False, 'error': 'Неверный формат JSON'}, status=400)
    except Exception as e:
        return JsonResponse({'success': False, 'error': str(e)}, status=500)


@login_required
@require_http_methods(["POST"])
def set_customer(request, customer_id):
    """
    Сохранить выбранного клиента в Redis для текущего пользователя и склада.
    TTL: 2 часа (7200 секунд)
    """
    from customers.models import Customer
    from django.core.cache import cache

    # Получаем текущий склад
    current_warehouse = get_pos_warehouse(request)
    if not current_warehouse:
        return JsonResponse({'success': False, 'error': 'Не выбран активный склад'}, status=400)

    # Проверяем, что клиент существует
    try:
        customer = Customer.objects.get(id=customer_id)
    except Customer.DoesNotExist:
        return JsonResponse({'success': False, 'error': 'Клиент не найден'}, status=404)

    # Сохраняем в Redis
    redis_key = f'pos:customer:{request.user.id}:{current_warehouse.id}'
    customer_data = {
        'customer_id': customer.id,
        'customer_name': customer.name,
        'wallet_balance': float(customer.wallet_balance)
    }
    cache.set(redis_key, customer_data, timeout=7200)  # 2 часа

    return JsonResponse({
        'success': True,
        'customer_id': customer.id,
        'customer_name': customer.name,
        'wallet_balance': float(customer.wallet_balance)
    })


@login_required
@require_http_methods(["POST"])
def set_warehouse(request, warehouse_id):
    """
    Установить текущий склад для POS.
    Сохраняет выбор в сессию.
    """
    try:
        warehouse = Warehouse.objects.get(id=warehouse_id, is_active=True)
        request.session['pos_warehouse_id'] = warehouse.id
        
        return JsonResponse({
            'success': True,
            'warehouse_id': warehouse.id,
            'warehouse_name': warehouse.name
        })
    except Warehouse.DoesNotExist:
        return JsonResponse({
            'success': False,
            'error': 'Склад не найден или неактивен'
        }, status=404)


@login_required
@require_http_methods(["GET"])
def get_showcases_api(request):
    """
    API endpoint для получения списка активных витрин.
    Используется для выбора витрины при создании временного комплекта.
    """
    showcases = Showcase.objects.filter(is_active=True).select_related('warehouse')

    showcases_data = [{
        'id': s.id,
        'name': s.name,
        'warehouse_name': s.warehouse.name,
        'warehouse_id': s.warehouse.id,
        'is_default': s.is_default  # Флаг витрины по умолчанию для автовыбора
    } for s in showcases]

    return JsonResponse({
        'success': True,
        'showcases': showcases_data
    })


@login_required
@require_http_methods(["GET"])
def get_showcase_kits_api(request):
    """
    API endpoint для получения актуального списка витринных комплектов.
    Используется для динамического обновления после создания нового букета.
    Включает информацию о блокировках в корзинах.
    """
    from datetime import timedelta
    from inventory.models import ShowcaseItem

    # Очищаем только ИСТЁКШИЕ блокировки (cart_lock_expires_at < now)
    expired_locks = ShowcaseItem.objects.filter(
        status='in_cart',
        cart_lock_expires_at__lt=timezone.now()
    )
    expired_count = expired_locks.update(
        status='available',
        locked_by_user=None,
        cart_lock_expires_at=None,
        cart_session_id=None
    )
    if expired_count > 0:
        logger.info(f'Очищено {expired_count} истёкших блокировок ShowcaseItem')

    showcase_kits_data = get_showcase_kits_for_pos()

    # Добавляем информацию о блокировках для каждого комплекта
    kit_ids = [kit['id'] for kit in showcase_kits_data]

    # Получаем активные блокировки (не истекшие)
    active_locks = Reservation.objects.filter(
        product_kit_id__in=kit_ids,
        cart_lock_expires_at__gt=timezone.now(),
        status='reserved'
    ).select_related('locked_by_user').annotate(
        # На уровне БД выбираем: если name есть - берем name, иначе email
        locked_by_user_display=Case(
            When(locked_by_user__name__isnull=False, then=F('locked_by_user__name')),
            When(locked_by_user__name='', then=F('locked_by_user__email')),
            default=F('locked_by_user__email'),
            output_field=CharField()
        )
    ).values(
        'product_kit_id',
        'locked_by_user_id',
        'locked_by_user_display',
        'cart_lock_expires_at'
    )

    # Индексируем блокировки по kit_id
    locks_by_kit = {}
    for lock in active_locks:
        kit_id = lock['product_kit_id']
        if kit_id not in locks_by_kit:
            locks_by_kit[kit_id] = lock

    # Дополняем данные о комплектах информацией о блокировках
    for kit in showcase_kits_data:
        lock_info = locks_by_kit.get(kit['id'])

        if lock_info:
            is_locked_by_me = lock_info['locked_by_user_id'] == request.user.id
            kit['is_locked'] = True
            kit['locked_by_me'] = is_locked_by_me
            kit['locked_by_user'] = lock_info['locked_by_user_display']
            kit['lock_expires_at'] = lock_info['cart_lock_expires_at'].isoformat()
        else:
            kit['is_locked'] = False
            kit['locked_by_me'] = False

    return JsonResponse({
        'success': True,
        'items': showcase_kits_data
    })


@login_required
@require_http_methods(["POST"])
def add_showcase_kit_to_cart(request, kit_id):
    """
    API endpoint для добавления N экземпляров витринного комплекта в корзину.

    НОВАЯ АРХИТЕКТУРА с ShowcaseItem:
    - Принимает quantity (количество экземпляров)
    - Блокирует конкретные ShowcaseItem
    - Возвращает список заблокированных showcase_item_ids

    Payload (JSON): { "quantity": 2 }

    Returns:
        JSON: {
            'success': bool,
            'message': str,
            'locked_item_ids': list[int],
            'lock_expires_at': ISO datetime,
            'error': str (если success=False)
        }
    """
    from datetime import timedelta
    from inventory.models import ShowcaseItem
    from inventory.services.showcase_manager import ShowcaseManager

    try:
        # Получаем количество из тела запроса
        try:
            body = json.loads(request.body) if request.body else {}
            quantity = int(body.get('quantity', 1))
        except (json.JSONDecodeError, ValueError):
            quantity = 1

        if quantity < 1:
            return JsonResponse({
                'success': False,
                'error': 'Количество должно быть больше 0'
            }, status=400)

        # Проверяем что комплект существует
        kit = ProductKit.objects.get(
            id=kit_id,
            is_temporary=True,
            status='active'
        )

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

            available_items = list(available_items)

            if len(available_items) < quantity:
                return JsonResponse({
                    'success': False,
                    'error': f'Доступно только {len(available_items)} из {quantity} запрошенных экземпляров. '
                             f'Возможно, часть уже в корзине другого кассира.'
                }, status=409)

            # Блокируем экземпляры
            lock_expires_at = timezone.now() + timedelta(minutes=30)
            session_id = request.session.session_key or ''
            locked_ids = []

            for item in available_items:
                item.lock_for_cart(
                    user=request.user,
                    session_id=session_id,
                    duration_minutes=30
                )
                locked_ids.append(item.id)

        return JsonResponse({
            'success': True,
            'message': f'Добавлено {len(locked_ids)} экз. "{kit.name}" в корзину',
            'locked_item_ids': locked_ids,
            'lock_expires_at': lock_expires_at.isoformat(),
            'locked_until_minutes': 30
        })

    except ProductKit.DoesNotExist:
        return JsonResponse({
            'success': False,
            'error': 'Витринный комплект не найден'
        }, status=404)
    except Exception as e:
        logger.error(f'Ошибка добавления витринного букета в корзину: {str(e)}', exc_info=True)
        return JsonResponse({
            'success': False,
            'error': f'Ошибка при добавлении в корзину: {str(e)}'
        }, status=500)


@login_required
@require_http_methods(["POST"])
def remove_showcase_kit_from_cart(request, kit_id):
    """
    API endpoint для снятия блокировки витринных экземпляров при удалении из корзины.

    НОВАЯ АРХИТЕКТУРА с ShowcaseItem:
    - Принимает опционально showcase_item_ids для снятия блокировки с конкретных экземпляров
    - Если не указаны - снимает блокировку со всех экземпляров данного комплекта

    Payload (JSON): { "showcase_item_ids": [10, 11] }  // опционально

    Returns:
        JSON: {
            'success': bool,
            'message': str,
            'released_count': int,
            'error': str (если success=False)
        }
    """
    from inventory.models import ShowcaseItem

    try:
        # Получаем комплект
        kit = ProductKit.objects.get(
            id=kit_id,
            is_temporary=True
        )

        # Получаем список ID экземпляров из тела запроса (опционально)
        try:
            body = json.loads(request.body) if request.body else {}
            showcase_item_ids = body.get('showcase_item_ids', [])
        except (json.JSONDecodeError, ValueError):
            showcase_item_ids = []

        # Базовый фильтр - экземпляры этого комплекта, заблокированные текущим пользователем
        from accounts.models import CustomUser
        
        if isinstance(request.user, CustomUser):
            qs = ShowcaseItem.objects.filter(
                product_kit=kit,
                status='in_cart',
                locked_by_user=request.user
            )
        else:
            # Для PlatformAdmin используем проверку по сессии
            session_id = request.session.session_key or ''
            qs = ShowcaseItem.objects.filter(
                product_kit=kit,
                status='in_cart',
                cart_session_id=session_id
            )

        # Если указаны конкретные ID - фильтруем только их
        if showcase_item_ids:
            qs = qs.filter(id__in=showcase_item_ids)

        # Снимаем блокировку
        updated_count = qs.update(
            status='available',
            locked_by_user=None,
            cart_lock_expires_at=None,
            cart_session_id=None
        )

        if updated_count == 0:
            return JsonResponse({
                'success': True,
                'message': 'Экземпляры не были заблокированы вами',
                'released_count': 0
            })

        return JsonResponse({
            'success': True,
            'message': f'Освобождено {updated_count} экз. "{kit.name}"',
            'released_count': updated_count
        })

    except ProductKit.DoesNotExist:
        return JsonResponse({
            'success': False,
            'error': 'Комплект не найден'
        }, status=404)
    except Exception as e:
        logger.error(f'Ошибка снятия блокировки витринного букета: {str(e)}', exc_info=True)
        return JsonResponse({
            'success': False,
            'error': f'Ошибка при снятии блокировки: {str(e)}'
        }, status=500)


@login_required
@require_http_methods(["POST"])
def release_all_my_showcase_locks(request):
    """
    API endpoint для сброса ВСЕХ блокировок витринных букетов текущего пользователя.
    Используется при загрузке POS если корзина пустая, чтобы освободить зависшие блокировки.
    """
    from inventory.models import ShowcaseItem

    try:
        # Снимаем ВСЕ блокировки текущего пользователя
        from accounts.models import CustomUser
        
        if isinstance(request.user, CustomUser):
            qs_to_release = ShowcaseItem.objects.filter(
                status='in_cart',
                locked_by_user=request.user
            )
        else:
            # Для PlatformAdmin фильтруем по сессии
            session_id = request.session.session_key or ''
            qs_to_release = ShowcaseItem.objects.filter(
                status='in_cart',
                cart_session_id=session_id
            )
            
        updated_count = qs_to_release.update(
            status='available',
            locked_by_user=None,
            cart_lock_expires_at=None,
            cart_session_id=None
        )

        return JsonResponse({
            'success': True,
            'message': f'Освобождено {updated_count} блокировок',
            'released_count': updated_count
        })

    except Exception as e:
        logger.error(f'Ошибка сброса блокировок: {str(e)}', exc_info=True)
        return JsonResponse({
            'success': False,
            'error': str(e)
        }, status=500)


@login_required
@require_http_methods(["GET"])
def get_items_api(request):
    """
    API endpoint для получения товаров и комплектов с пагинацией и поиском.
    Параметры:
    - category_id: ID категории (опционально, для фильтрации)
    - query: поисковый запрос по name или sku (опционально)
    - page: номер страницы (по умолчанию 1)
    - page_size: размер страницы (по умолчанию 60)
    Сортировка по умолчанию: по свободному остатку (available - reserved) DESC
    """
    from products.models import ProductPhoto, ProductKitPhoto
    from django.core.paginator import Paginator
    from django.db.models import Q
    
    # Получаем текущий склад
    current_warehouse = get_pos_warehouse(request)
    if not current_warehouse:
        return JsonResponse({
            'success': False,
            'error': 'Нет активного склада'
        }, status=400)
    
    # Параметры пагинации и поиска
    category_id = request.GET.get('category_id')
    search_query = request.GET.get('query', '').strip()
    page = int(request.GET.get('page', 1))
    page_size = int(request.GET.get('page_size', 60))
    
    # Prefetch для первого фото товаров
    first_product_photo = Prefetch(
        'photos',
        queryset=ProductPhoto.objects.order_by('order')[:1],
        to_attr='first_photo_list'
    )
    
    # Подзапросы для остатков по текущему складу
    stock_available_subquery = Stock.objects.filter(
        product=OuterRef('pk'),
        warehouse=current_warehouse
    ).values('quantity_available')[:1]
    
    stock_reserved_subquery = Stock.objects.filter(
        product=OuterRef('pk'),
        warehouse=current_warehouse
    ).values('quantity_reserved')[:1]
    
    # Фильтруем только активные товары
    products_qs = Product.objects.filter(status='active').annotate(
        available_qty=Coalesce(
            Subquery(stock_available_subquery, output_field=DecimalField()),
            Decimal('0'),
            output_field=DecimalField()
        ),
        reserved_qty=Coalesce(
            Subquery(stock_reserved_subquery, output_field=DecimalField()),
            Decimal('0'),
            output_field=DecimalField()
        )
    ).prefetch_related(
        'categories',
        first_product_photo,
        'sales_units'  # Загружаем единицы продажи для POS
    )
    
    # Фильтруем по категории, если указана И нет поискового запроса
    # При поиске игнорируем категорию - ищем по всем товарам
    if category_id and not search_query:
        products_qs = products_qs.filter(categories__id=category_id)
    
    # Фильтруем по поисковому запросу (name или sku) - разбиваем на токены
    if search_query:
        tokens = search_query.split()
        for token in tokens:
            if token:  # Пропускаем пустые токены
                products_qs = products_qs.filter(
                    Q(name__icontains=token) | Q(sku__icontains=token)
                )
    
    # Сериализуем товары
    products = []
    for p in products_qs:
        image_url = None
        if hasattr(p, 'first_photo_list') and p.first_photo_list:
            photo = p.first_photo_list[0]
            if photo and photo.image:
                image_url = photo.get_thumbnail_url()
                # Если миниатюра не найдена, возвращаем None (без фото)
                if not image_url:
                    image_url = None
        
        available = p.available_qty
        reserved = p.reserved_qty
        free_qty = available - reserved

        # Получаем активные единицы продажи
        active_sales_units = [su for su in p.sales_units.all() if su.is_active]
        sales_units_count = len(active_sales_units)
        has_sales_units = sales_units_count > 0

        # Находим единицу продажи по умолчанию
        default_sales_unit = None
        available_qty_in_unit = free_qty  # Количество в единицах продажи
        price_in_unit = str(p.actual_price)  # Цена в единицах продажи

        if has_sales_units:
            # Ищем единицу с is_default=True или берем первую активную
            default_unit = next((su for su in active_sales_units if su.is_default), active_sales_units[0])
            if default_unit and default_unit.conversion_factor and default_unit.conversion_factor > 0:
                # Конвертируем свободное количество в единицы продажи
                available_qty_in_unit = free_qty * default_unit.conversion_factor
                price_in_unit = str(default_unit.actual_price)
                default_sales_unit = {
                    'id': default_unit.id,
                    'name': default_unit.name,
                    'price': str(default_unit.actual_price),
                    'conversion_factor': str(default_unit.conversion_factor),
                    'min_quantity': str(default_unit.min_quantity),
                    'quantity_step': str(default_unit.quantity_step),
                    'is_default': default_unit.is_default
                }

        products.append({
            'id': p.id,
            'name': p.name,
            'price': str(p.actual_price),
            'category_ids': [c.id for c in p.categories.all()],
            'in_stock': p.in_stock,
            'sku': p.sku or '',
            'image': image_url,
            'type': 'product',
            'available_qty': str(available),
            'reserved_qty': str(reserved),
            'free_qty': str(free_qty),  # Передаём как строку для сохранения точности
            'free_qty_sort': float(available_qty_in_unit if has_sales_units and default_sales_unit else free_qty),  # Для сортировки
            'sales_units_count': sales_units_count,
            'has_sales_units': has_sales_units,
            'default_sales_unit': default_sales_unit,
            'available_qty_in_unit': str(available_qty_in_unit),
            'price_in_unit': price_in_unit
        })
    
    # Prefetch для первого фото комплектов
    first_kit_photo = Prefetch(
        'photos',
        queryset=ProductKitPhoto.objects.order_by('order')[:1],
        to_attr='first_photo_list'
    )
    
    # Активные комплекты (не временные)
    kits_qs = ProductKit.objects.filter(
        is_temporary=False,
        status='active'
    ).prefetch_related(
        'categories',
        first_kit_photo
    )
    
    # Фильтруем комплекты по категории, если указана И нет поискового запроса
    # При поиске игнорируем категорию - ищем по всем комплектам
    if category_id and not search_query:
        kits_qs = kits_qs.filter(categories__id=category_id)
    
    # Фильтруем комплекты по поисковому запросу (name или sku) - разбиваем на токены
    if search_query:
        tokens = search_query.split()
        for token in tokens:
            if token:  # Пропускаем пустые токены
                kits_qs = kits_qs.filter(
                    Q(name__icontains=token) | Q(sku__icontains=token)
                )
    
    # Сериализуем комплекты
    kits = []
    for k in kits_qs:
        image_url = None
        if hasattr(k, 'first_photo_list') and k.first_photo_list:
            photo = k.first_photo_list[0]
            if photo and photo.image:
                image_url = photo.get_thumbnail_url()
                # Если миниатюра не найдена, возвращаем None (без фото)
                if not image_url:
                    image_url = None
        
        # Рассчитываем доступное количество комплектов на текущем складе
        available_kits = k.calculate_available_quantity(warehouse=current_warehouse)
        
        kits.append({
            'id': k.id,
            'name': k.name,
            'price': str(k.actual_price),
            'category_ids': [c.id for c in k.categories.all()],
            'in_stock': available_kits > 0,  # Доступен если можно собрать хоть один комплект
            'sku': k.sku or '',
            'image': image_url,
            'type': 'kit',
            'free_qty': str(available_kits),  # Количество комплектов которые можно собрать
            'free_qty_sort': float(available_kits)  # Для сортировки
        })
    
    # Объединяем и сортируем по free_qty_sort DESC
    all_items = products + kits
    all_items.sort(key=lambda x: x['free_qty_sort'], reverse=True)
    
    # Пагинация
    paginator = Paginator(all_items, page_size)
    page_obj = paginator.get_page(page)
    
    # Удаляем временное поле free_qty_sort из результата (оставляем free_qty как строку)
    items_to_return = []
    for item in page_obj.object_list:
        item_copy = item.copy()
        item_copy.pop('free_qty_sort', None)  # Удаляем только free_qty_sort
        items_to_return.append(item_copy)
    
    return JsonResponse({
        'success': True,
        'items': items_to_return,
        'has_more': page_obj.has_next(),
        'next_page': page_obj.next_page_number() if page_obj.has_next() else None,
        'total_pages': paginator.num_pages,
        'total_items': paginator.count
    })


@login_required
@require_http_methods(["GET"])
def get_product_kit_details(request, kit_id):
    """
    API endpoint для получения полных данных комплекта для редактирования.
    """
    try:
        kit = ProductKit.objects.prefetch_related('kit_items__product', 'photos').get(id=kit_id)
        
        # Получаем витрину, на которой размещен комплект
        showcase_reservation = Reservation.objects.filter(
            product__in=kit.kit_items.values_list('product_id', flat=True),
            showcase__isnull=False,
            showcase__is_active=True,
            status='reserved'
        ).select_related('showcase').first()
        
        showcase_id = showcase_reservation.showcase.id if showcase_reservation else None

        # Собираем данные о составе
        # Используем unit_price если есть (зафиксированная цена), иначе актуальную цену товара
        items = []
        for ki in kit.kit_items.all():
            # Зафиксированная цена или актуальная цена товара
            item_price = ki.unit_price if ki.unit_price is not None else ki.product.actual_price
            item_data = {
                'product_id': ki.product.id,
                'name': ki.product.name,
                'qty': str(ki.quantity),
                'price': str(item_price)
            }
            # Для временных комплектов добавляем актуальную цену из каталога для сравнения
            if kit.is_temporary and ki.unit_price is not None:
                item_data['actual_catalog_price'] = str(ki.product.actual_price)
            items.append(item_data)

        # Фото (используем миниатюру для быстрой загрузки)
        photo_url = None
        if kit.photos.exists():
            first_photo = kit.photos.first()
            if first_photo and first_photo.image:
                photo_url = first_photo.get_thumbnail_url()
                # Если миниатюра не найдена, возвращаем None (без фото)
                if not photo_url:
                    photo_url = None
        
        return JsonResponse({
            'success': True,
            'kit': {
                'id': kit.id,
                'name': kit.name,
                'description': kit.description or '',
                'price_adjustment_type': kit.price_adjustment_type,
                'price_adjustment_value': str(kit.price_adjustment_value),
                'sale_price': str(kit.sale_price) if kit.sale_price else '',
                'base_price': str(kit.base_price),
                'final_price': str(kit.actual_price),
                'showcase_id': showcase_id,
                'items': items,
                'photo_url': photo_url,
                'showcase_created_at': kit.showcase_created_at.isoformat() if kit.showcase_created_at else None
            }
        })
    except ProductKit.DoesNotExist:
        return JsonResponse({'success': False, 'error': 'Комплект не найден'}, status=404)


@login_required
@require_http_methods(["POST"])
def create_temp_kit_to_showcase(request):
    """
    API endpoint для создания временного комплекта из корзины POS
    и резервирования его на витрину.
    
    Ожидаемый payload (multipart/form-data):
    - kit_name: Название комплекта
    - showcase_id: ID витрины
    - items: JSON список [{product_id, quantity}, ...]
    - description: Описание (опционально)
    - price_adjustment_type: Тип корректировки (опционально)
    - price_adjustment_value: Значение корректировки (опционально)
    - sale_price: Ручная финальная цена (опционально)
    - photo: Файл изображения (опционально)
    """
    try:
        # Получаем данные из FormData
        kit_name = request.POST.get('kit_name', '').strip()
        showcase_id = request.POST.get('showcase_id')
        description = request.POST.get('description', '').strip()
        items_json = request.POST.get('items', '[]')
        price_adjustment_type = request.POST.get('price_adjustment_type', 'none')
        price_adjustment_value = Decimal(str(request.POST.get('price_adjustment_value', 0)))
        sale_price_str = request.POST.get('sale_price', '')
        photo_file = request.FILES.get('photo')
        showcase_kit_quantity = int(request.POST.get('quantity', 1))  # Количество букетов на витрину
        showcase_created_at_str = request.POST.get('showcase_created_at', '').strip()

        # Парсим items из JSON
        items = json.loads(items_json)
        
        # Sale price (опционально)
        sale_price = None
        if sale_price_str:
            try:
                sale_price = Decimal(str(sale_price_str))
                if sale_price <= 0:
                    sale_price = None
            except (ValueError, InvalidOperation):
                sale_price = None

        # Showcase created at (опционально)
        showcase_created_at = None
        if showcase_created_at_str:
            try:
                from datetime import datetime
                showcase_created_at = datetime.fromisoformat(showcase_created_at_str)
            except ValueError:
                try:
                    from datetime import datetime
                    showcase_created_at = datetime.strptime(showcase_created_at_str, '%Y-%m-%dT%H:%M')
                except ValueError:
                    pass  # Неверный формат, оставляем как None

        # Если не указана - устанавливаем текущее время для новых комплектов
        if not showcase_created_at:
            showcase_created_at = timezone.now()

        # Валидация
        if not kit_name:
            return JsonResponse({
                'success': False,
                'error': 'Необходимо указать название комплекта'
            }, status=400)
        
        if not showcase_id:
            return JsonResponse({
                'success': False,
                'error': 'Необходимо выбрать витрину'
            }, status=400)
        
        if not items or len(items) == 0:
            return JsonResponse({
                'success': False,
                'error': 'Корзина пуста. Добавьте товары для создания комплекта'
            }, status=400)
        
        # Проверяем что витрина существует и активна
        try:
            showcase = Showcase.objects.select_related('warehouse').get(id=showcase_id, is_active=True)
        except Showcase.DoesNotExist:
            return JsonResponse({
                'success': False,
                'error': 'Витрина не найдена или неактивна'
            }, status=404)
        
        # Проверяем что все товары из корзины - это Product (не Kit)
        product_ids = [item['product_id'] for item in items]
        products = Product.objects.in_bulk(product_ids)
        
        if len(products) != len(product_ids):
            return JsonResponse({
                'success': False,
                'error': 'Некоторые товары не найдены'
            }, status=400)
        
        # Агрегируем дубликаты (если один товар добавлен несколько раз)
        # Сохраняем также цену из корзины (unit_price)
        aggregated_items = {}
        for item in items:
            product_id = item['product_id']
            quantity = Decimal(str(item['quantity']))
            unit_price = item.get('unit_price')  # Цена из корзины (может быть изменена пользователем)

            if product_id in aggregated_items:
                aggregated_items[product_id]['quantity'] += quantity
            else:
                aggregated_items[product_id] = {
                    'quantity': quantity,
                    'unit_price': Decimal(str(unit_price)) if unit_price is not None else None
                }
        
        # Создаём временный комплект и резервируем на витрину
        with transaction.atomic():
            # 1. Создаём ProductKit (is_temporary=True)
            kit = ProductKit.objects.create(
                name=kit_name,
                description=description,
                is_temporary=True,
                status='active',
                price_adjustment_type=price_adjustment_type,
                price_adjustment_value=price_adjustment_value,
                sale_price=sale_price,
                showcase=showcase,
                showcase_created_at=showcase_created_at
            )
            
            # 2. Создаём KitItem для каждого товара из корзины
            for product_id, item_data in aggregated_items.items():
                product = products[product_id]
                # Используем цену из корзины, если передана, иначе из каталога
                final_price = item_data['unit_price'] if item_data['unit_price'] is not None else product.actual_price
                KitItem.objects.create(
                    kit=kit,
                    product=product,
                    quantity=item_data['quantity'],
                    unit_price=final_price  # Фиксируем цену из корзины (с учётом изменений пользователя)
                )

            # 3. Пересчитываем цену комплекта
            kit.recalculate_base_price()
            
            # 4. Загружаем фото, если есть
            if photo_file:
                from products.models import ProductKitPhoto
                ProductKitPhoto.objects.create(
                    kit=kit,
                    image=photo_file,
                    order=0
                )
            
            # 5. Резервируем комплект на витрину (создаём N экземпляров)
            result = ShowcaseManager.reserve_kit_to_showcase(
                product_kit=kit,
                showcase=showcase,
                quantity=showcase_kit_quantity
            )
            
            if not result['success']:
                # Откатываем транзакцию через raise
                raise Exception(result['message'])

        # Получаем ID созданных ShowcaseItem
        showcase_item_ids = [item.id for item in result.get('showcase_items', [])]

        created_count = len(showcase_item_ids)
        qty_text = f'{created_count} шт.' if created_count > 1 else ''

        return JsonResponse({
            'success': True,
            'message': f'Букет "{kit_name}" ({qty_text}) создан на витрине "{showcase.name}"' if created_count > 1 else f'Букет "{kit_name}" создан на витрине "{showcase.name}"',
            'kit_id': kit.id,
            'kit_name': kit.name,
            'kit_price': str(kit.actual_price),
            'reservations_count': len(result['reservations']),
            'showcase_item_ids': showcase_item_ids,
            'available_count': created_count,
            'warnings': result.get('warnings')
        })
        
    except json.JSONDecodeError as e:
        logger.error(f'JSON decode error при создании временного комплекта: {str(e)}')
        return JsonResponse({
            'success': False,
            'error': 'Неверный формат данных корзины'
        }, status=400)
    except Showcase.DoesNotExist:
        logger.warning(f'Попытка создать комплект на несуществующей витрине (ID: {request.POST.get("showcase_id")})')
        return JsonResponse({
            'success': False,
            'error': 'Выбранная витрина не найдена'
        }, status=404)
    except ValidationError as e:
        logger.error(f'Validation error при создании временного комплекта: {str(e)}', exc_info=True)
        return JsonResponse({
            'success': False,
            'error': f'Ошибка валидации: {str(e)}'
        }, status=400)
    except Exception as e:
        # Детальное логирование для диагностики 500 ошибок
        logger.error(
            f'Непредвиденная ошибка при создании временного комплекта:\n'
            f'  Название: {request.POST.get("kit_name")}\n'
            f'  Витрина ID: {request.POST.get("showcase_id")}\n'
            f'  Товары: {request.POST.get("items")}\n'
            f'  Пользователь: {str(request.user)}\n'
            f'  Ошибка: {str(e)}',
            exc_info=True
        )

        # Проверяем на типичные ошибки и даём понятные сообщения
        error_msg = str(e).lower()

        if 'недостаточно' in error_msg or 'insufficient' in error_msg or 'stock' in error_msg:
            return JsonResponse({
                'success': False,
                'error': f'Недостаточно товара на складе. {str(e)}'
            }, status=400)
        elif 'integrity' in error_msg or 'constraint' in error_msg:
            return JsonResponse({
                'success': False,
                'error': 'Ошибка целостности данных. Проверьте, что все товары существуют и витрина активна.'
            }, status=400)
        elif 'lock' in error_msg or 'blocked' in error_msg or 'заблокирован' in error_msg:
            return JsonResponse({
                'success': False,
                'error': f'Конфликт блокировки: {str(e)}'
            }, status=409)
        else:
            return JsonResponse({
                'success': False,
                'error': f'Не удалось создать комплект: {str(e)}. Проверьте консоль сервера для деталей.'
            }, status=500)


@login_required
@require_http_methods(["POST"])
def update_product_kit(request, kit_id):
    """
    API endpoint для обновления временного комплекта.
    
    Payload (multipart/form-data):
    - kit_name: Новое название
    - description: Описание
    - items: JSON список [{product_id, quantity}, ...]
    - price_adjustment_type, price_adjustment_value, sale_price
    - photo: Новое фото (опционально)
    - remove_photo: '1' для удаления фото
    """
    try:
        kit = ProductKit.objects.prefetch_related('kit_items__product', 'photos').get(id=kit_id, is_temporary=True)
        
        # Получаем данные
        kit_name = request.POST.get('kit_name', '').strip()
        description = request.POST.get('description', '').strip()
        items_json = request.POST.get('items', '[]')
        price_adjustment_type = request.POST.get('price_adjustment_type', 'none')
        price_adjustment_value = Decimal(str(request.POST.get('price_adjustment_value', 0)))
        sale_price_str = request.POST.get('sale_price', '')
        photo_file = request.FILES.get('photo')
        remove_photo = request.POST.get('remove_photo', '') == '1'
        showcase_created_at_str = request.POST.get('showcase_created_at', '').strip()

        items = json.loads(items_json)
        
        sale_price = None
        if sale_price_str:
            try:
                sale_price = Decimal(str(sale_price_str))
                if sale_price <= 0:
                    sale_price = None
            except (ValueError, InvalidOperation):
                sale_price = None

        # Showcase created at (опционально)
        showcase_created_at = None
        if showcase_created_at_str:
            try:
                from datetime import datetime
                showcase_created_at = datetime.fromisoformat(showcase_created_at_str)
            except ValueError:
                try:
                    showcase_created_at = datetime.strptime(showcase_created_at_str, '%Y-%m-%dT%H:%M')
                except ValueError:
                    pass  # Неверный формат, оставляем как есть

            # Делаем datetime timezone-aware
            if showcase_created_at and showcase_created_at.tzinfo is None:
                from django.utils import timezone
                showcase_created_at = timezone.make_aware(showcase_created_at)

        # Валидация
        if not kit_name:
            return JsonResponse({'success': False, 'error': 'Необходимо указать название'}, status=400)
        
        if not items:
            return JsonResponse({'success': False, 'error': 'Состав не может быть пустым'}, status=400)
        
        # Проверяем товары
        product_ids = [item['product_id'] for item in items]
        products = Product.objects.in_bulk(product_ids)
        
        if len(products) != len(product_ids):
            return JsonResponse({'success': False, 'error': 'Некоторые товары не найдены'}, status=400)
        
        # Агрегируем количества и цены
        aggregated_items = {}
        for item in items:
            product_id = item['product_id']
            quantity = Decimal(str(item['quantity']))
            unit_price = item.get('unit_price')
            if product_id in aggregated_items:
                aggregated_items[product_id]['quantity'] += quantity
            else:
                aggregated_items[product_id] = {
                    'quantity': quantity,
                    'unit_price': Decimal(str(unit_price)) if unit_price is not None else None
                }
        
        with transaction.atomic():
            # Получаем старый состав для сравнения
            old_items = {ki.product_id: ki.quantity for ki in kit.kit_items.all()}
            
            # Получаем витрину для резервов
            showcase_reservation = Reservation.objects.filter(
                product_kit=kit,
                showcase__isnull=False,
                status='reserved'
            ).select_related('showcase').first()
            
            showcase = showcase_reservation.showcase if showcase_reservation else kit.showcase
            
            # Вычисляем разницу в составе и собираем информацию о дефиците
            all_product_ids = set(old_items.keys()) | set(aggregated_items.keys())
            stock_warnings = []  # Список товаров с нехваткой остатков
            
            for product_id in all_product_ids:
                old_qty = old_items.get(product_id, Decimal('0'))
                new_qty = aggregated_items.get(product_id, {}).get('quantity', Decimal('0'))
                diff = new_qty - old_qty
                
                if diff > 0 and showcase:
                    # Нужно дозарезервировать (на каждый экземпляр)
                    result = ShowcaseManager.reserve_product_to_showcase(
                        product=products[product_id],
                        showcase=showcase,
                        product_kit=kit,
                        quantity_per_item=diff,
                    )
                    # Собираем информацию о дефиците
                    if result.get('overdraft', Decimal('0')) > 0:
                        stock_warnings.append({
                            'product_name': products[product_id].name,
                            'overdraft': str(result['overdraft'])
                        })
                
                elif diff < 0 and showcase:
                    # Нужно освободить резерв (на каждый экземпляр)
                    ShowcaseManager.release_showcase_reservation(
                        product=products[product_id],
                        showcase=showcase,
                        product_kit=kit,
                        quantity_per_item=abs(diff),
                    )
            
            # Обновляем комплект
            kit.name = kit_name
            kit.description = description
            kit.price_adjustment_type = price_adjustment_type
            kit.price_adjustment_value = price_adjustment_value
            kit.sale_price = sale_price
            if showcase_created_at is not None:  # Обновляем только если передана
                kit.showcase_created_at = showcase_created_at
            kit.save()
            
            # Обновляем состав
            kit.kit_items.all().delete()
            for product_id, item_data in aggregated_items.items():
                product = products[product_id]
                # Используем переданную цену, если есть, иначе актуальную из каталога
                final_price = item_data['unit_price'] if item_data['unit_price'] is not None else product.actual_price
                KitItem.objects.create(
                    kit=kit,
                    product=product,
                    quantity=item_data['quantity'],
                    unit_price=final_price
                )
            
            kit.recalculate_base_price()
            
            # Обновляем фото
            if remove_photo:
                kit.photos.all().delete()
            
            if photo_file:
                from products.models import ProductKitPhoto
                kit.photos.all().delete()  # Удаляем старое
                ProductKitPhoto.objects.create(kit=kit, image=photo_file, order=0)
        
        return JsonResponse({
            'success': True,
            'message': f'Комплект "{kit.name}" обновлён',
            'kit_id': kit.id,
            'kit_name': kit.name,
            'kit_price': str(kit.actual_price),
            'stock_warning': len(stock_warnings) > 0,
            'stock_warnings': stock_warnings
        })
        
    except ProductKit.DoesNotExist:
        return JsonResponse({'success': False, 'error': 'Комплект не найден'}, status=404)
    except json.JSONDecodeError:
        return JsonResponse({'success': False, 'error': 'Неверный формат данных'}, status=400)
    except Exception as e:
        return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)


@login_required
@require_http_methods(["POST"])
def disassemble_product_kit(request, kit_id):
    """
    Разбирает витринный комплект: освобождает резервы и устанавливает статус 'discontinued'.

    Args:
        request: HTTP запрос
        kit_id: ID комплекта для разбора

    Returns:
        JSON: {
            'success': bool,
            'released_count': int,
            'message': str,
            'error': str (если failed)
        }
    """
    try:
        # Получаем комплект с витриной (только временные комплекты)
        kit = ProductKit.objects.select_related('showcase').get(id=kit_id, is_temporary=True)

        # Проверяем, что комплект ещё не разобран
        if kit.status == 'discontinued':
            return JsonResponse({
                'success': False,
                'error': 'Комплект уже разобран (статус: Снят)'
            }, status=400)

        # Проверяем, что у комплекта есть привязанная витрина
        if not kit.showcase:
            return JsonResponse({
                'success': False,
                'error': 'Комплект не привязан к витрине'
            }, status=400)

        # Освобождаем резервы и устанавливаем статус
        # ShowcaseManager.dismantle_from_showcase уже использует transaction.atomic()
        result = ShowcaseManager.dismantle_from_showcase(
            showcase=kit.showcase,
            product_kit=kit
        )

        if not result['success']:
            return JsonResponse({
                'success': False,
                'error': result['message']
            }, status=400)

        # Устанавливаем статус комплекта 'discontinued'
        kit.discontinue(user=request.user)

        return JsonResponse({
            'success': True,
            'released_count': result['released_count'],
            'message': f'Комплект "{kit.name}" разобран. Статус изменён на "Снят".'
        })

    except ProductKit.DoesNotExist:
        return JsonResponse({
            'success': False,
            'error': 'Комплект не найден'
        }, status=404)
    except Exception as e:
        return JsonResponse({
            'success': False,
            'error': f'Ошибка при разборе: {str(e)}'
        }, status=500)


@login_required
@require_http_methods(["POST"])
def write_off_showcase_kit(request, kit_id):
    """
    Списывает витринный комплект с созданием документа списания.

    Args:
        request: HTTP запрос
        kit_id: ID комплекта для списания

    Returns:
        JSON: {
            'success': bool,
            'document_id': int,
            'document_number': str,
            'redirect_url': str,
            'message': str,
            'error': str (если failed)
        }
    """
    try:
        # Получаем комплект с витриной (только временные комплекты)
        kit = ProductKit.objects.select_related('showcase').get(id=kit_id, is_temporary=True)

        # Проверяем, что комплект ещё не разобран
        if kit.status == 'discontinued':
            return JsonResponse({
                'success': False,
                'error': 'Комплект уже разобран (статус: Снят)'
            }, status=400)

        # Проверяем, что у комплекта есть привязанная витрина
        if not kit.showcase:
            return JsonResponse({
                'success': False,
                'error': 'Комплект не привязан к витрине'
            }, status=400)

        # Находим экземпляр на витрине
        showcase_item = ShowcaseItem.objects.filter(
            showcase=kit.showcase,
            product_kit=kit,
            status='available'
        ).first()

        if not showcase_item:
            return JsonResponse({
                'success': False,
                'error': 'Экземпляр комплекта не найден на витрине'
            }, status=404)

        # Создаём документ списания
        result = ShowcaseManager.write_off_from_showcase(
            showcase_item=showcase_item,
            reason='spoilage',
            notes=f'Витринный букет: {kit.name}',
            created_by=request.user
        )

        if not result['success']:
            return JsonResponse({
                'success': False,
                'error': result['message']
            }, status=400)

        # Формируем URL для перенаправления
        redirect_url = reverse('inventory:writeoff-document-detail', kwargs={'pk': result['document_id']})

        return JsonResponse({
            'success': True,
            'document_id': result['document_id'],
            'document_number': result['document_number'],
            'redirect_url': redirect_url,
            'message': result['message']
        })

    except ProductKit.DoesNotExist:
        return JsonResponse({'success': False, 'error': 'Комплект не найден'}, status=404)
    except Exception as e:
        return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)


@login_required
@require_http_methods(["POST"])
def pos_checkout(request):
    """
    Создать заказ и провести оплату в POS-терминале.

    Payload (JSON):
    {
        "customer_id": int,
        "warehouse_id": int,
        "items": [
            {"type": "product"|"kit"|"showcase_kit", "id": int, "quantity": float, "price": float},
            ...
        ],
        "payments": [
            {"payment_method": "cash"|"card"|"online"|"account_balance", "amount": float, "notes": str},
            ...
        ],
        "notes": str (optional),
        "promo_code": str (optional) - Промокод для скидки
        "manual_discount_id": int (optional) - ID выбранной вручную скидки
        "custom_discount": dict (optional) - Произвольная скидка {"value": float, "is_percent": bool}
    }
    """
    from orders.models import Order, OrderItem, OrderStatus
    from orders.services.transaction_service import TransactionService
    from customers.models import Customer
    from products.models import Product, ProductKit
    from inventory.models import Warehouse, Reservation
    from django.db import transaction as db_transaction
    from decimal import Decimal
    import json

    try:
        body = json.loads(request.body)

        # Валидация
        customer_id = body.get('customer_id')
        warehouse_id = body.get('warehouse_id')
        items_data = body.get('items', [])
        payments_data = body.get('payments', [])
        order_notes = body.get('notes', '')
        promo_code = body.get('promo_code')  # Промокод для скидки
        manual_discount_id = body.get('manual_discount_id')  # ID выбранной вручную скидки
        custom_discount = body.get('custom_discount')  # Произвольная скидка {"value": float, "is_percent": bool}

        if not customer_id:
            return JsonResponse({'success': False, 'error': 'Не указан клиент'}, status=400)
        if not warehouse_id:
            return JsonResponse({'success': False, 'error': 'Не указан склад'}, status=400)
        if not items_data:
            return JsonResponse({'success': False, 'error': 'Корзина пуста'}, status=400)
        if not payments_data:
            return JsonResponse({'success': False, 'error': 'Не указаны способы оплаты'}, status=400)

        # Получаем объекты
        customer = get_object_or_404(Customer, id=customer_id)
        warehouse = get_object_or_404(Warehouse, id=warehouse_id, is_active=True)

        try:
            completed_status = OrderStatus.objects.get(code='completed', is_system=True)
        except OrderStatus.DoesNotExist:
            return JsonResponse({
                'success': False,
                'error': 'Статус "Выполнен" не найден в системе'
            }, status=500)

        # Атомарная операция
        with db_transaction.atomic():
            # ВАЖНО: Устанавливаем флаг для пропуска автоматического создания Sale в сигнале.
            # Sale будет создан ЯВНО после применения всех скидок.
            skip_sale_creation()

            # 1. Создаём заказ с текущей датой и временем в локальном часовом поясе (Europe/Minsk)
            from django.utils import timezone as tz
            from orders.models import Delivery
            now_utc = tz.now()  # Текущее время в UTC
            now_local = tz.localtime(now_utc)  # Конвертируем в локальный часовой пояс (Europe/Minsk)
            current_time = now_local.time()  # Извлекаем время в минском часовом поясе

            order = Order.objects.create(
                customer=customer,
                status=completed_status,  # Сразу "Выполнен"
                special_instructions=order_notes,
                modified_by=request.user
            )
            
            # Создаём связанную доставку (самовывоз для POS)
            Delivery.objects.create(
                order=order,
                delivery_type=Delivery.DELIVERY_TYPE_PICKUP,  # POS - всегда самовывоз
                pickup_warehouse=warehouse,
                delivery_date=now_local.date(),  # Текущая дата в минском часовом поясе
                time_from=current_time,  # Текущее время (Минск)
                time_to=current_time,  # То же время (точное время)
                cost=0  # Самовывоз бесплатный
            )

            # 2. Добавляем товары
            from inventory.models import ShowcaseItem
            from inventory.services.showcase_manager import ShowcaseManager

            for item_data in items_data:
                item_type = item_data['type']
                item_id = item_data['id']
                quantity = Decimal(str(item_data['quantity']))
                price = Decimal(str(item_data['price']))

                if item_type == 'product':
                    product = Product.objects.get(id=item_id)
                    # Получаем sales_unit_id если передан
                    sales_unit_id = item_data.get('sales_unit_id')
                    sales_unit = None
                    if sales_unit_id:
                        from products.models import ProductSalesUnit
                        try:
                            sales_unit = ProductSalesUnit.objects.get(id=sales_unit_id, product=product)
                        except ProductSalesUnit.DoesNotExist:
                            pass
                    
                    OrderItem.objects.create(
                        order=order,
                        product=product,
                        quantity=quantity,
                        price=price,
                        is_custom_price=False,
                        sales_unit=sales_unit
                    )
                elif item_type == 'kit':
                    # Обычный комплект (не витринный)
                    kit = ProductKit.objects.get(id=item_id)
                    OrderItem.objects.create(
                        order=order,
                        product_kit=kit,
                        quantity=quantity,
                        price=price,
                        is_custom_price=False
                    )
                elif item_type == 'showcase_kit':
                    # Витринный букет - работаем через ShowcaseItem
                    kit = ProductKit.objects.get(id=item_id)
                    showcase_item_ids = item_data.get('showcase_item_ids', [])

                    if not showcase_item_ids:
                        # Обратная совместимость: если showcase_item_ids не передан,
                        # ищем заблокированные экземпляры этого комплекта
                        showcase_item_ids = list(ShowcaseItem.objects.filter(
                            product_kit_id=item_id,
                            status='in_cart',
                            locked_by_user=request.user
                        ).values_list('id', flat=True)[:int(quantity)])

                    # Создаём OrderItem
                    order_item = OrderItem.objects.create(
                        order=order,
                        product_kit=kit,
                        quantity=len(showcase_item_ids) if showcase_item_ids else int(quantity),
                        price=price,
                        is_custom_price=False,
                        is_from_showcase=True
                    )

                    # Продаём экземпляры через ShowcaseManager
                    if showcase_item_ids:
                        # ShowcaseManager.sell_showcase_items выполнит всю валидацию с блокировкой БД
                        showcase_items = list(ShowcaseItem.objects.filter(
                            id__in=showcase_item_ids
                        ))

                        if showcase_items:
                            result = ShowcaseManager.sell_showcase_items(showcase_items, order_item)
                            if not result['success']:
                                raise ValidationError(result['message'])

            # 3. Пересчитываем итоговую стоимость
            # Обновляем объект заказа из БД, чтобы получить все связанные товары
            order.refresh_from_db()
            order.calculate_total()

            # 4. Применяем скидки
            if custom_discount:
                # Применяем произвольную скидку
                from decimal import Decimal
                discount_value = Decimal(str(custom_discount.get('value', 0)))
                is_percent = custom_discount.get('is_percent', False)

                if is_percent:
                    # Процентная скидка
                    discount_amount = order.subtotal * discount_value / 100
                    discount_name = f'Произвольная скидка {discount_value}%'
                else:
                    # Фиксированная скидка
                    discount_amount = min(discount_value, order.subtotal)
                    discount_name = f'Произвольная скидка {discount_value} руб.'

                # Создаем запись о применении в истории скидок
                from discounts.models import DiscountApplication
                DiscountApplication.objects.create(
                    order=order,
                    target='order',
                    base_amount=order.subtotal,
                    discount_amount=discount_amount,
                    final_amount=order.subtotal - discount_amount,
                    customer=customer,
                    applied_by=request.user
                )

                # Пересчитываем total_amount (использует DiscountApplication)
                order.calculate_total()
            elif manual_discount_id:
                from discounts.services.applier import DiscountApplier
                from discounts.models import Discount

                try:
                    discount = Discount.objects.get(id=manual_discount_id, is_active=True)
                    apply_result = DiscountApplier.apply_manual_discount(
                        order=order,
                        discount=discount,
                        user=request.user
                    )
                    if not apply_result['success']:
                        raise ValidationError(apply_result['error'])
                except Discount.DoesNotExist:
                    pass
            elif promo_code:
                from discounts.services.applier import DiscountApplier
                apply_result = DiscountApplier.apply_promo_code(
                    order=order,
                    promo_code=promo_code,
                    user=request.user
                )
                if not apply_result['success']:
                    raise ValidationError(apply_result['error'])
            else:
                # Применяем автоматические скидки
                from discounts.services.applier import DiscountApplier
                DiscountApplier.apply_auto_discounts(order, user=request.user)

            # 5. Проводим платежи
            payments_list = []
            for payment_data in payments_data:
                payments_list.append({
                    'payment_method': payment_data['payment_method'],
                    'amount': Decimal(str(payment_data['amount'])),
                    'notes': payment_data.get('notes', f"Оплата POS: {payment_data['payment_method']}")
                })

            transactions = TransactionService.create_multiple_payments(
                order=order,
                payments_list=payments_list,
                user=request.user
            )

            # 5. Обновляем статус оплаты
            order.update_payment_status()

            # 6. Очищаем корзину из Redis
            from django.core.cache import cache
            cart_key = f'pos:cart:{request.user.id}:{warehouse_id}'
            cache.delete(cart_key)

            # 7. Явно создаём Sale после применения всех скидок
            # Сбрасываем флаг пропуска и вызываем save() для активации сигнала
            reset_sale_creation()
            order.save()  # Триггерит сигнал create_sale_on_order_completion

        return JsonResponse({
            'success': True,
            'order_number': order.order_number,
            'order_id': order.id,
            'total_amount': float(order.total_amount),
            'amount_paid': float(order.amount_paid),
            'amount_due': float(order.amount_due),
            'payments_count': len(transactions),
            'message': f'Заказ #{order.order_number} успешно создан и оплачен'
        })

    except (Customer.DoesNotExist, Warehouse.DoesNotExist, Product.DoesNotExist, ProductKit.DoesNotExist) as e:
        return JsonResponse({'success': False, 'error': 'Объект не найден'}, status=404)
    except ValidationError as e:
        return JsonResponse({'success': False, 'error': str(e)}, status=400)
    except json.JSONDecodeError:
        return JsonResponse({'success': False, 'error': 'Неверный формат JSON'}, status=400)
    except Exception as e:
        logger.error(f'Ошибка при проведении продажи POS: {str(e)}', exc_info=True)
        return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)


@login_required
@require_http_methods(["POST"])
def create_order_draft(request):
    """
    Создает черновик заказа из корзины POS и сохраняет в Redis.
    Возвращает токен для передачи в orders/create/.
    
    Payload (JSON):
    {
        "customer_id": int,
        "items": [
            {"type": "product"|"kit"|"showcase_kit", "id": int, "quantity": float, "price": float,
             "showcase_item_ids": [int, ...] // для showcase_kit
            },
            ...
        ]
    }
    
    Response:
    {
        "success": true,
        "token": "abc123..."
    }
    """
    from django.core.cache import cache
    import secrets
    
    try:
        data = json.loads(request.body)
        customer_id = data.get('customer_id')
        items = data.get('items', [])
        
        if not items:
            return JsonResponse({'success': False, 'error': 'Корзина пуста'}, status=400)
        
        # Генерируем уникальный токен
        token = secrets.token_urlsafe(16)
        
        # Сохраняем в Redis с TTL 1 час
        cache_key = f'pos_draft:{token}'
        draft_data = {
            'customer_id': customer_id,
            'items': items,  # items уже содержат showcase_item_ids для showcase_kit
        }
        cache.set(cache_key, draft_data, timeout=3600)  # 1 час
        
        return JsonResponse({
            'success': True,
            'token': token
        })
        
    except json.JSONDecodeError:
        return JsonResponse({'success': False, 'error': 'Неверный формат JSON'}, status=400)
    except Exception as e:
        logger.error(f'Ошибка при создании черновика заказа: {str(e)}', exc_info=True)
        return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)


# ============================================
# DISCOUNT API
# ============================================

@login_required
@require_http_methods(["POST"])
def validate_promo_code(request):
    """
    Валидировать промокод.

    Payload JSON:
        {
            'promo_code': str,
            'cart_total': decimal (optional)
        }

    Returns JSON:
        {
            'success': true/false,
            'promo_code': {...},
            'error': str
        }
    """
    from discounts.services.validator import DiscountValidator
    from customers.models import Customer

    try:
        data = json.loads(request.body)
        promo_code = data.get('promo_code', '')
        cart_total = data.get('cart_total', '0')

        # Получаем текущего клиента из Redis
        from django.core.cache import cache
        cart_key = f'pos:customer:{request.user.id}'
        customer_id = cache.get(cart_key)

        customer = None
        if customer_id:
            try:
                customer = Customer.objects.get(id=customer_id)
            except Customer.DoesNotExist:
                pass

        is_valid, promo, error = DiscountValidator.validate_promo_code(
            promo_code, customer, Decimal(str(cart_total)) if cart_total else None
        )

        if is_valid:
            return JsonResponse({
                'success': True,
                'promo_code': {
                    'code': promo.code,
                    'discount_id': promo.discount.id,
                    'discount_name': promo.discount.name,
                    'discount_type': promo.discount.discount_type,
                    'discount_value': float(promo.discount.value),
                    'scope': promo.discount.scope,
                }
            })
        else:
            return JsonResponse({'success': False, 'error': error})

    except json.JSONDecodeError:
        return JsonResponse({'success': False, 'error': 'Неверный формат JSON'}, status=400)
    except Exception as e:
        logger.error(f'Ошибка при валидации промокода: {str(e)}', exc_info=True)
        return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)


@login_required
@require_http_methods(["POST"])
def calculate_cart_discounts(request):
    """
    Рассчитать скидки для корзины POS.
    Поддерживает комбинирование скидок по combine_mode.

    Payload JSON:
        {
            'items': [...],
            'promo_code': str (optional),
            'customer_id': int (optional),
            'manual_discount_id': int (optional),
            'skip_auto_discount': bool (optional)
        }

    Returns JSON:
        {
            'success': true,
            'cart_subtotal': float,
            'order_discounts': [
                {'discount_id': int, 'discount_name': str, 'discount_amount': float, 'combine_mode': str},
                ...
            ],
            'total_order_discount': float,
            'item_discounts': [
                {
                    'cart_index': int,
                    'discounts': [
                        {'discount_id': int, 'discount_name': str, 'discount_amount': float, 'combine_mode': str},
                        ...
                    ],
                    'total_discount': float
                },
                ...
            ],
            'total_discount': float,
            'final_total': float,
            'excluded_by': {'id': int, 'name': str} или None
        }
    """
    from discounts.services.calculator import DiscountCalculator
    from customers.models import Customer

    try:
        data = json.loads(request.body)
        items_data = data.get('items', [])
        promo_code = data.get('promo_code')
        customer_id = data.get('customer_id')
        skip_auto_discount = data.get('skip_auto_discount', False)

        customer = None
        if customer_id:
            try:
                customer = Customer.objects.get(id=customer_id)
            except Customer.DoesNotExist:
                pass

        result = DiscountCalculator.calculate_cart_discounts(
            items_data, promo_code, customer, skip_auto_discount=skip_auto_discount
        )

        # Форматируем item_discounts для JSON (Decimal -> float)
        formatted_item_discounts = []
        for item in result['item_discounts']:
            formatted_discounts = []
            for disc in item['discounts']:
                formatted_discounts.append({
                    'discount_id': disc['discount_id'],
                    'discount_name': disc['discount_name'],
                    'discount_amount': float(disc['discount_amount']),
                    'combine_mode': disc['combine_mode']
                })
            formatted_item_discounts.append({
                'cart_index': item['cart_index'],
                'discounts': formatted_discounts,
                'total_discount': float(item['total_discount'])
            })

        # Форматируем order_discounts для JSON
        formatted_order_discounts = []
        for disc in result['order_discounts']:
            formatted_order_discounts.append({
                'discount_id': disc['discount_id'],
                'discount_name': disc['discount_name'],
                'discount_amount': float(disc['discount_amount']),
                'combine_mode': disc['combine_mode']
            })

        response_data = {
            'success': True,
            'cart_subtotal': float(result['cart_subtotal']),
            'order_discounts': formatted_order_discounts,
            'total_order_discount': float(result['total_order_discount']),
            'item_discounts': formatted_item_discounts,
            'total_discount': float(result['total_discount']),
            'final_total': float(result['final_total']),
            'excluded_by': result.get('excluded_by')
        }

        return JsonResponse(response_data)

    except json.JSONDecodeError:
        return JsonResponse({'success': False, 'error': 'Неверный формат JSON'}, status=400)
    except Exception as e:
        logger.error(f'Ошибка при расчете скидок: {str(e)}', exc_info=True)
        return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)


@login_required
@require_http_methods(["GET"])
def get_available_discounts(request):
    """
    Возвращает список доступных скидок для ручного применения в POS.

    Query params:
        - cart_total: сумма корзины (для фильтрации по min_order_amount)

    Returns JSON:
        {
            'success': true,
            'order_discounts': [
                {
                    'id': 1,
                    'name': 'Скидка 5%',
                    'discount_type': 'percentage',
                    'value': 5,
                    'min_order_amount': 50
                },
                ...
            ],
            'auto_order_discount': {...}  # если есть автоскидка на заказ
        }
    """
    from discounts.services.calculator import DiscountCalculator

    try:
        cart_total = request.GET.get('cart_total', '0')
        cart_total = Decimal(str(cart_total))

        # Получаем НЕ автоматические скидки на заказ для ручного применения
        order_discounts = DiscountCalculator.get_available_discounts(
            scope='order',
            auto_only=False
        ).filter(is_auto=False)

        # Фильтруем по мин. сумме заказа
        result_discounts = []
        for d in order_discounts:
            if d.min_order_amount and cart_total < d.min_order_amount:
                continue
            result_discounts.append({
                'id': d.id,
                'name': d.name,
                'discount_type': d.discount_type,
                'value': float(d.value),
                'min_order_amount': float(d.min_order_amount) if d.min_order_amount else None,
                'combine_mode': d.combine_mode
            })

        # Получаем автоматическую скидку (только одну для отображения)
        auto_discounts = DiscountCalculator.get_available_discounts(
            scope='order',
            auto_only=True
        )

        auto_discount_data = None
        for d in auto_discounts:
            if d.min_order_amount and cart_total < d.min_order_amount:
                continue
            # Рассчитываем сумму
            discount_amount = d.calculate_discount_amount(cart_total)
            auto_discount_data = {
                'id': d.id,
                'name': d.name,
                'discount_type': d.discount_type,
                'value': float(d.value),
                'discount_amount': float(discount_amount)
            }
            break

        return JsonResponse({
            'success': True,
            'order_discounts': result_discounts,
            'auto_order_discount': auto_discount_data
        })

    except Exception as e:
        logger.error(f'Ошибка при получении скидок: {str(e)}', exc_info=True)
        return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)
