"""
CRUD представления для категорий товаров (ProductCategory).
Включает иерархическое дерево категорий с товарами и комплектами.
"""
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView, CreateView, DetailView, UpdateView, DeleteView
from django.urls import reverse_lazy, reverse
from django.shortcuts import redirect
from django.db.models import Q, Prefetch
from django.db import IntegrityError

from ..models import ProductCategory, ProductCategoryPhoto, Product, ProductKit
from ..forms import ProductCategoryForm
from .utils import handle_photos


class TreeItem:
    """
    Универсальный элемент дерева категорий.
    Может представлять категорию, товар или набор.
    """
    def __init__(self, obj, item_type, depth, parent_category_id=None):
        self.obj = obj
        self.item_type = item_type  # 'category', 'product', 'kit'
        self.depth = depth
        self.pk = obj.pk
        self.name = obj.name
        self.sku = getattr(obj, 'sku', None)

        if item_type == 'category':
            self.has_children = obj.children.exists()
            self.parent_id = obj.parent.pk if obj.parent else None
        else:
            # Товары и наборы не имеют детей
            self.has_children = False
            # Виртуальный parent_id = ID категории (для JavaScript)
            self.parent_id = parent_category_id

        # Получение цены
        if item_type == 'product':
            self.price = obj.sale_price
        elif item_type == 'kit':
            self.price = obj.actual_price
        else:
            self.price = None


class ProductCategoryListView(LoginRequiredMixin, ListView):
    model = ProductCategory
    template_name = 'products/category_list.html'
    context_object_name = 'categories'
    paginate_by = None  # Отключаем пагинацию для иерархического отображения

    def get_queryset(self):
        queryset = super().get_queryset()
        # Добавляем prefetch_related для оптимизации запросов к фото
        queryset = queryset.prefetch_related('photos', 'children')

        # Поиск по названию и артикулу
        search_query = self.request.GET.get('search')
        if search_query:
            queryset = queryset.filter(
                Q(name__icontains=search_query) |
                Q(sku__icontains=search_query) |
                Q(slug__icontains=search_query)
            )

        # Фильтр по статусу
        is_active = self.request.GET.get('is_active')
        if is_active == '1':
            queryset = queryset.filter(is_active=True)
        elif is_active == '0':
            queryset = queryset.filter(is_active=False)

        return queryset

    def build_category_tree(self, queryset):
        """
        Строит иерархическое дерево категорий с товарами и наборами.
        Возвращает плоский список TreeItem объектов.
        """
        # Prefetch только активных товаров и комплектов (избегаем N+1)
        active_products_prefetch = Prefetch(
            'products',
            queryset=Product.objects.filter(status='active').order_by('name')
        )
        active_kits_prefetch = Prefetch(
            'kits',
            queryset=ProductKit.objects.filter(status='active').order_by('name')
        )
        
        # Получаем все категории из queryset с prefetch для товаров и наборов
        all_categories = list(queryset.select_related('parent')
                             .prefetch_related('photos', 'children',
                                               active_products_prefetch,
                                               active_kits_prefetch))

        # Создаем словарь для быстрого доступа по ID
        categories_dict = {cat.pk: cat for cat in all_categories}

        # Добавляем служебные поля
        for cat in all_categories:
            cat.depth = 0
            cat.has_children = cat.children.exists()

        # Находим корневые категории (parent=None или parent не в нашем queryset)
        queryset_ids = set(categories_dict.keys())
        root_categories = [
            cat for cat in all_categories
            if cat.parent is None or cat.parent.pk not in queryset_ids
        ]

        # Сортируем корневые по имени
        root_categories.sort(key=lambda x: x.name.lower())

        # Рекурсивно строим дерево
        result = []

        def add_category_and_children(category, depth=0):
            # 1. Добавляем категорию как TreeItem
            tree_item = TreeItem(category, 'category', depth)
            result.append(tree_item)

            # 2. Добавляем активные товары этой категории (уже отфильтрованы и отсортированы через Prefetch)
            for product in category.products.all():
                product_item = TreeItem(product, 'product', depth + 1, category.pk)
                result.append(product_item)

            # 3. Добавляем активные наборы этой категории (уже отфильтрованы и отсортированы через Prefetch)
            for kit in category.kits.all():
                kit_item = TreeItem(kit, 'kit', depth + 1, category.pk)
                result.append(kit_item)

            # 4. Рекурсивно обрабатываем дочерние категории
            children = [
                cat for cat in all_categories
                if cat.parent and cat.parent.pk == category.pk
            ]
            # Сортируем детей по имени
            children.sort(key=lambda x: x.name.lower())

            # Рекурсивно добавляем детей
            for child in children:
                add_category_and_children(child, depth + 1)

        # Обходим все корневые категории
        for root in root_categories:
            add_category_and_children(root, 0)

        return result

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        # Строим иерархическое дерево
        queryset = self.get_queryset()
        category_tree = self.build_category_tree(queryset)
        context['category_tree'] = category_tree

        # Флаг для автоматического раскрытия при поиске
        context['has_search'] = bool(self.request.GET.get('search'))

        # Фильтры для панели фильтрации
        context['filters'] = {
            'search': self.request.GET.get('search', ''),
            'is_active': self.request.GET.get('is_active', ''),
            'current': {
                'search': self.request.GET.get('search', ''),
                'is_active': self.request.GET.get('is_active', ''),
            }
        }
        # Кнопки действий
        context['action_buttons'] = [
            {'url': reverse('products:category-create'), 'text': 'Создать категорию', 'icon': 'plus-circle'}
        ]
        return context


class ProductCategoryCreateView(LoginRequiredMixin, CreateView):
    model = ProductCategory
    form_class = ProductCategoryForm
    template_name = 'products/category_form.html'
    success_url = reverse_lazy('products:category-list')

    def form_valid(self, form):
        try:
            # Сохраняем категорию
            self.object = form.save()
            messages.success(self.request, f'Категория "{self.object.name}" создана успешно.')

            # Обработка загрузки фотографий
            errors = handle_photos(self.request, self.object, ProductCategoryPhoto, 'category')
            if errors:
                for error in errors:
                    messages.warning(self.request, error)

            return redirect(self.get_success_url())

        except IntegrityError as e:
            # Обработка нарушения уникальности
            error_msg = str(e).lower()
            if 'unique_active_category_name' in error_msg or ('name' in error_msg and 'duplicate' in error_msg):
                messages.error(
                    self.request,
                    f'Ошибка: категория с названием "{form.instance.name}" уже существует. '
                    'Пожалуйста, используйте другое название.'
                )
            elif 'slug' in error_msg or 'unique' in error_msg:
                messages.error(
                    self.request,
                    'Ошибка: категория с таким названием уже существует. '
                    'Пожалуйста, используйте другое название.'
                )
            elif 'sku' in error_msg:
                messages.error(
                    self.request,
                    f'Ошибка: категория с артикулом "{form.cleaned_data.get("sku", "")}" уже существует. '
                    'Пожалуйста, используйте другой артикул.'
                )
            else:
                messages.error(
                    self.request,
                    'Ошибка при сохранении категории. Пожалуйста, проверьте введённые данные.'
                )
            return self.form_invalid(form)

    def form_invalid(self, form):
        messages.error(self.request, 'Пожалуйста, исправьте ошибки в форме.')
        return super().form_invalid(form)


class ProductCategoryDetailView(LoginRequiredMixin, DetailView):
    model = ProductCategory
    template_name = 'products/category_detail.html'
    context_object_name = 'category'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Получаем фотографии категории
        context['category_photos'] = self.object.photos.all().order_by('order')
        context['photos_count'] = context['category_photos'].count()
        # Получаем дочерние категории
        context['children_categories'] = self.object.children.filter(is_active=True)
        # Получаем товары в категории
        context['products'] = self.object.products.filter(status='active')[:20]
        context['products_count'] = self.object.products.filter(status='active').count()
        return context


class ProductCategoryUpdateView(LoginRequiredMixin, UpdateView):
    model = ProductCategory
    form_class = ProductCategoryForm
    template_name = 'products/category_form.html'
    success_url = reverse_lazy('products:category-list')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Добавляем фотографии для отображения в форме
        context['category_photos'] = self.object.photos.all().order_by('order')
        context['photos_count'] = context['category_photos'].count()
        return context

    def form_valid(self, form):
        try:
            # Сохраняем категорию
            self.object = form.save()
            messages.success(self.request, f'Категория "{self.object.name}" обновлена успешно.')

            # Обработка загрузки новых фотографий
            errors = handle_photos(self.request, self.object, ProductCategoryPhoto, 'category')
            if errors:
                for error in errors:
                    messages.warning(self.request, error)

            return redirect(self.get_success_url())

        except IntegrityError as e:
            # Обработка нарушения уникальности
            error_msg = str(e).lower()
            if 'unique_active_category_name' in error_msg or ('name' in error_msg and 'duplicate' in error_msg):
                messages.error(
                    self.request,
                    f'Ошибка: категория с названием "{form.instance.name}" уже существует. '
                    'Пожалуйста, используйте другое название.'
                )
            elif 'slug' in error_msg or 'unique' in error_msg:
                messages.error(
                    self.request,
                    'Ошибка: категория с таким названием уже существует. '
                    'Пожалуйста, используйте другое название.'
                )
            elif 'sku' in error_msg:
                messages.error(
                    self.request,
                    f'Ошибка: категория с артикулом "{form.cleaned_data.get("sku", "")}" уже существует. '
                    'Пожалуйста, используйте другой артикул.'
                )
            else:
                messages.error(
                    self.request,
                    'Ошибка при сохранении категории. Пожалуйста, проверьте введённые данные.'
                )
            return self.form_invalid(form)

    def form_invalid(self, form):
        messages.error(self.request, 'Пожалуйста, исправьте ошибки в форме.')
        return super().form_invalid(form)


class ProductCategoryDeleteView(LoginRequiredMixin, DeleteView):
    model = ProductCategory
    template_name = 'products/category_confirm_delete.html'
    success_url = reverse_lazy('products:category-list')
    context_object_name = 'category'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Проверяем, есть ли товары в категории
        context['products_count'] = self.object.products.count()
        context['children_count'] = self.object.children.count()
        return context

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()

        # Проверяем, есть ли товары в категории
        if self.object.products.exists():
            messages.error(
                request,
                f'Невозможно удалить категорию "{self.object.name}", '
                f'так как в ней есть товары ({self.object.products.count()} шт.). '
                f'Сначала удалите или переместите товары.'
            )
            return redirect('products:category-detail', pk=self.object.pk)

        # Проверяем, есть ли дочерние категории
        if self.object.children.exists():
            messages.error(
                request,
                f'Невозможно удалить категорию "{self.object.name}", '
                f'так как у неё есть подкатегории ({self.object.children.count()} шт.). '
                f'Сначала удалите или переместите подкатегории.'
            )
            return redirect('products:category-detail', pk=self.object.pk)

        messages.success(request, f'Категория "{self.object.name}" удалена успешно.')
        return super().post(request, *args, **kwargs)
