from django.db import models
from accounts.models import CustomUser
from customers.models import Customer
from .status import OrderStatus
from .recipient import Recipient


class Order(models.Model):
    """
    Заказ клиента для доставки цветов.

    ВАЖНО: Поле payment_method УДАЛЕНО для поддержки смешанной оплаты.
    Используйте модель Payment (один Order → много Payment) для платежей.
    """
    # Основная информация
    customer = models.ForeignKey(
        Customer,
        on_delete=models.PROTECT,
        related_name='orders',
        verbose_name="Клиент"
    )

    order_number = models.PositiveIntegerField(
        unique=True,
        editable=False,
        verbose_name="Номер заказа",
        help_text="Уникальный номер заказа"
    )

    # Статус заказа
    status = models.ForeignKey(
        'OrderStatus',
        on_delete=models.PROTECT,
        related_name='orders',
        null=True,
        blank=True,
        verbose_name="Статус заказа"
    )

    # Флаг для отслеживания возвратов
    is_returned = models.BooleanField(
        default=False,
        verbose_name="Возвращен",
        help_text="True если заказ был выполнен, но потом отменен или возвращен клиентом"
    )

    # Автосохранение (для черновиков)
    last_autosave_at = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name="Последнее автосохранение",
        help_text="Время последнего автоматического сохранения черновика"
    )

    # Оплата
    # УДАЛЕНО: PAYMENT_METHOD_CHOICES и payment_method поле
    # Вместо этого используйте модель Payment для смешанной оплаты

    is_paid = models.BooleanField(
        default=False,
        verbose_name="Оплачен"
    )

    total_amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        verbose_name="Итоговая сумма заказа",
        help_text="Общая сумма заказа"
    )

    # === СКИДКИ ===
    # Скидки хранятся в модели DiscountApplication (через related_name='discount_applications')
    # Старые поля applied_discount, discount_amount, applied_promo_code УДАЛЕНЫ
    # Используйте свойства ниже для доступа к скидкам

    # Частичная оплата
    amount_paid = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0,
        verbose_name="Оплачено",
        help_text="Сумма, внесенная клиентом"
    )

    PAYMENT_STATUS_CHOICES = [
        ('unpaid', 'Не оплачен'),
        ('partial', 'Частично оплачен'),
        ('paid', 'Оплачен полностью'),
    ]

    payment_status = models.CharField(
        max_length=20,
        choices=PAYMENT_STATUS_CHOICES,
        default='unpaid',
        verbose_name="Статус оплаты",
        help_text="Обновляется автоматически при добавлении платежей"
    )

    # Информация о получателе
    # Получатель (если None - получатель = покупатель, иначе - отдельный получатель)
    recipient = models.ForeignKey(
        Recipient,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='orders',
        verbose_name="Получатель",
        help_text="Заполняется, если покупатель не является получателем"
    )

    is_anonymous = models.BooleanField(
        default=False,
        verbose_name="Анонимная доставка",
        help_text="Не сообщать получателю имя отправителя"
    )

    special_instructions = models.TextField(
        blank=True,
        null=True,
        verbose_name="Особые пожелания",
        help_text="Комментарии и пожелания к заказу"
    )

    summary = models.TextField(
        blank=True,
        null=True,
        verbose_name="Резюме заказа",
        help_text="Краткое описание заказа на естественном языке (например: '21 фридом 60 см в упаковке на наше усмотрение')"
    )

    # Фото
    needs_product_photo = models.BooleanField(
        default=False,
        verbose_name="Необходимо фото товара",
        help_text="Требуется фотография товара перед отправкой"
    )

    needs_delivery_photo = models.BooleanField(
        default=False,
        verbose_name="Необходимо фото вручения",
        help_text="Требуется фотография процесса вручения заказа"
    )

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

    updated_at = models.DateTimeField(
        auto_now=True,
        verbose_name="Дата обновления"
    )

    modified_by = models.ForeignKey(
        CustomUser,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='modified_orders',
        verbose_name="Изменен пользователем",
        help_text="Последний пользователь, изменивший заказ"
    )

    # TODO: История изменений (simple_history) - вернуть позже
    # history = HistoricalRecords()

    class Meta:
        verbose_name = "Заказ"
        verbose_name_plural = "Заказы"
        indexes = [
            models.Index(fields=['customer']),
            models.Index(fields=['status']),
            models.Index(fields=['payment_status']),
            models.Index(fields=['created_at']),
            models.Index(fields=['order_number']),
        ]
        ordering = ['-created_at']

    def __str__(self):
        return f"Заказ #{self.order_number} - {self.customer}"

    def get_absolute_url(self):
        """Возвращает канонический URL для заказа"""
        from django.urls import reverse
        return reverse('orders:order-detail', kwargs={'order_number': self.order_number})

    def save(self, *args, **kwargs):
        from django.db import transaction
        from django.core.exceptions import ValidationError
        import logging
        logger = logging.getLogger(__name__)
        
        # Генерируем уникальный номер заказа при создании (начиная с 100 для 3-значного поиска)
        if not self.order_number:
            last_order = Order.objects.order_by('-order_number').first()
            if last_order:
                # Если ранее нумерация была ниже 100, начинаем с 100; иначе инкремент
                self.order_number = max(last_order.order_number + 1, 100)
            else:
                self.order_number = 100
        
        # === АВТОМАТИЧЕСКИЙ ПРОМЕЖУТОЧНЫЙ ПЕРЕХОД: cancelled → draft → completed ===
        # При прямом переходе из отрицательного (cancelled) в положительный (completed) статус,
        # делаем промежуточный переход через нейтральный статус 'draft'.
        # Это гарантирует корректную работу всех сигналов:
        # 1. cancelled → draft: витринные букеты available → reserved
        # 2. draft → completed: резервы и букеты корректно финализируются в sold
        if self.pk:  # Только при редактировании
            try:
                old_instance = Order.objects.get(pk=self.pk)
                old_status = old_instance.status
                new_status = self.status
                
                # Проверяем: переход от отрицательного к положительному?
                if (old_status and old_status.is_negative_end and 
                    new_status and new_status.is_positive_end):
                    
                    logger.info(
                        f"🔄 Заказ #{self.order_number}: Обнаружен прямой переход "
                        f"{old_status.name} (отрицательный) → {new_status.name} (положительный). "
                        f"Выполняем автоматический промежуточный переход через 'draft'..."
                    )
                    
                    # Получаем статус 'draft'
                    try:
                        draft_status = OrderStatus.objects.get(code='draft', is_system=True)
                    except OrderStatus.DoesNotExist:
                        raise ValidationError(
                            f"Невозможно выполнить переход из '{old_status.name}' в '{new_status.name}': "
                            f"системный статус 'draft' не найден. Обратитесь к администратору."
                        )
                    
                    # Сохраняем целевой статус для второго шага
                    target_status = new_status
                    
                    # ШАГ 1: cancelled → draft
                    logger.info(f"  📍 Шаг 1/2: {old_status.name} → draft")
                    self.status = draft_status
                    with transaction.atomic():
                        super().save(*args, **kwargs)
                    
                    # Обновляем old_instance для следующего шага
                    old_instance.refresh_from_db()
                    
                    # ШАГ 2: draft → completed
                    logger.info(f"  📍 Шаг 2/2: draft → {target_status.name}")
                    self.status = target_status
                    with transaction.atomic():
                        super().save(*args, **kwargs)
                    
                    logger.info(
                        f"✅ Заказ #{self.order_number}: Промежуточный переход завершён успешно. "
                        f"Итоговый статус: {target_status.name}"
                    )
                    return  # Выходим, т.к. save() уже вызван дважды
                
            except Order.DoesNotExist:
                # Заказ ещё не создан в БД (не должно произойти, но на всякий случай)
                pass
        
        # === ВАЛИДАЦИЯ: Проверяем доступность витринных комплектов ===
        # При переходе ИЗ cancelled к любому не-отменённому статусу
        if self.pk:  # Только при редактировании
            try:
                # Получаем старый статус из БД
                old_instance = Order.objects.get(pk=self.pk)
                old_status = old_instance.status
                new_status = self.status
                
                # Проверяем: переход от cancelled к не-cancelled?
                if (old_status and old_status.is_negative_end and 
                    new_status and not new_status.is_negative_end):
                    
                    # Находим все витринные комплекты в этом заказе
                    from orders.models import OrderItem
                    showcase_items = OrderItem.objects.filter(
                        order=self,
                        product_kit__is_temporary=True,
                        product_kit__showcase__isnull=False
                    ).select_related('product_kit')
                    
                    if showcase_items.exists():
                        # Проверяем доступность резервов для каждого комплекта
                        from inventory.models import Reservation
                        unavailable_kits = []
                        
                        for item in showcase_items:
                            kit = item.product_kit
                            
                            # КРИТИЧНО: Ищем ВСЕ витринные резервы этого комплекта
                            # Проверяем не привязаны ли они к ДРУГОМУ заказу (в ЛЮБОМ статусе)
                            occupied_reservations = Reservation.objects.filter(
                                product_kit=kit,
                                showcase__isnull=False,
                                order_item__isnull=False  # Привязаны к какому-то заказу
                            ).exclude(
                                order_item__order=self  # Исключаем текущий заказ
                            ).select_related('order_item__order')
                            
                            if occupied_reservations.exists():
                                # Резервы заняты другим заказом - блокируем переход
                                occupied_res = occupied_reservations.first()
                                other_order_number = occupied_res.order_item.order.order_number
                                other_order_status = occupied_res.order_item.order.status.name if occupied_res.order_item.order.status else 'неизвестен'
                                
                                unavailable_kits.append(
                                    f"Витринный комплект '{kit.name}' занят заказом #{other_order_number} (статус: {other_order_status})"
                                )
                            else:
                                # Проверяем что вообще есть резервы для этого комплекта
                                any_reservations = Reservation.objects.filter(
                                    product_kit=kit,
                                    showcase__isnull=False
                                ).exists()
                                
                                if not any_reservations:
                                    # Комплект демонтирован или удалён
                                    unavailable_kits.append(
                                        f"Витринный комплект '{kit.name}' больше не существует на витрине"
                                    )
                        
                        # Если есть недоступные комплекты - блокируем переход
                        if unavailable_kits:
                            error_message = (
                                f"Невозможно восстановить заказ #{self.order_number}. "
                                f"Витринные комплекты уже проданы:\n\n" +
                                "\n".join(f"\u2022 {msg}" for msg in unavailable_kits) +
                                f"\n\nОтмените сначала соответствующие заказы или удалите эти позиции из заказа."
                            )
                            raise ValidationError(error_message)
            
            except Order.DoesNotExist:
                # Заказ ещё не создан в БД (не должно произойти, но на всякий случай)
                pass

        # Оборачиваем в транзакцию чтобы ValidationError в сигналах откатывал save()
        with transaction.atomic():
            super().save(*args, **kwargs)

    def recalculate_amount_paid(self):
        """
        Пересчитывает оплаченную сумму на основе транзакций.
        amount_paid = сумма_платежей - сумма_возвратов
        """
        from django.db.models import Sum, Q
        from decimal import Decimal
        
        payments_sum = self.transactions.filter(
            transaction_type='payment'
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0')
        
        refunds_sum = self.transactions.filter(
            transaction_type='refund'
        ).aggregate(total=Sum('amount'))['total'] or Decimal('0')
        
        self.amount_paid = payments_sum - refunds_sum
        self.update_payment_status()
    
    def update_payment_status(self):
        """Автоматически обновляет статус оплаты на основе amount_paid"""
        if self.amount_paid >= self.total_amount:
            self.payment_status = 'paid'
            self.is_paid = True
        elif self.amount_paid > 0:
            self.payment_status = 'partial'
            self.is_paid = False
        else:
            self.payment_status = 'unpaid'
            self.is_paid = False
        # Сохраняем только изменённые поля для оптимизации
        self.save(update_fields=['payment_status', 'is_paid', 'amount_paid'])

    def is_draft(self):
        """Проверяет, является ли заказ черновиком"""
        return self.status and self.status.code == 'draft'

    @property
    def amount_due(self):
        """Остаток к оплате"""
        return max(self.total_amount - self.amount_paid, 0)

    @property
    def overpayment(self):
        """Переплата (если amount_paid > total_amount)"""
        return max(self.amount_paid - self.total_amount, 0)

    @property
    def subtotal(self):
        """Сумма только товаров (без доставки)"""
        return sum(item.get_total_price() for item in self.items.all())

    def calculate_total(self):
        """
        Пересчитывает итоговую сумму заказа.
        total_amount = subtotal + delivery_cost - discount_amount
        Скидка берётся из DiscountApplication
        """
        from decimal import Decimal

        subtotal = self.subtotal
        delivery_cost = Decimal('0')

        # Получаем стоимость доставки из связанной модели Delivery
        if hasattr(self, 'delivery'):
            delivery_cost = self.delivery.cost

        # Вычитаем скидку на весь заказ (из DiscountApplication через свойство)
        order_discount = Decimal(str(self.discount_amount)) if self.discount_amount else Decimal('0')

        self.total_amount = subtotal + delivery_cost - order_discount
        self.save(update_fields=['total_amount'])

    def reset_delivery_cost(self):
        """
        Сбрасывает стоимость доставки.
        Если есть Delivery, устанавливает cost = 0.
        """
        if hasattr(self, 'delivery'):
            self.delivery.cost = 0
            self.delivery.save(update_fields=['cost'])

    # === Свойства для доступа к скидкам (через DiscountApplication) ===

    @property
    def order_discounts(self):
        """Скидки на заказ (QuerySet DiscountApplication с target='order')"""
        from discounts.models import DiscountApplication
        return self.discount_applications.filter(target='order').select_related('discount', 'promo_code')

    @property
    def item_discounts(self):
        """Скидки на позиции (QuerySet DiscountApplication с target='order_item')"""
        from discounts.models import DiscountApplication
        return self.discount_applications.filter(target='order_item').select_related('discount', 'order_item')

    @property
    def discount_amount(self):
        """Общая сумма скидки на заказ"""
        from decimal import Decimal
        return self.order_discounts.aggregate(
            total=models.Sum('discount_amount')
        )['total'] or Decimal('0')

    @property
    def applied_discount(self):
        """Первая применённая скидка (для обратной совместимости)"""
        first = self.order_discounts.first()
        return first.discount if first else None

    @property
    def applied_promo_code(self):
        """Промокод из первой записи (для обратной совместимости)"""
        first = self.order_discounts.first()
        if first and first.promo_code:
            return first.promo_code.code
        return None

    # === Свойства обратной совместимости для доступа к полям доставки ===
    # Эти свойства обеспечивают доступ к полям Delivery через Order для обратной совместимости
    # после рефакторинга, когда поля доставки были перенесены в отдельную модель Delivery

    @property
    def delivery_date(self):
        """Дата доставки (обратная совместимость)"""
        if hasattr(self, 'delivery') and self.delivery:
            return self.delivery.delivery_date
        return None

    @property
    def delivery_time_start(self):
        """Время начала доставки (обратная совместимость)"""
        if hasattr(self, 'delivery') and self.delivery:
            return self.delivery.time_from
        return None

    @property
    def delivery_time_end(self):
        """Время окончания доставки (обратная совместимость)"""
        if hasattr(self, 'delivery') and self.delivery:
            return self.delivery.time_to
        return None

    @property
    def delivery_time_window(self):
        """Форматированное окно времени доставки (обратная совместимость)"""
        if hasattr(self, 'delivery') and self.delivery:
            if self.delivery.time_from and self.delivery.time_to:
                return f"{self.delivery.time_from.strftime('%H:%M')} - {self.delivery.time_to.strftime('%H:%M')}"
        return None

    @property
    def delivery_time(self):
        """Время доставки (обратная совместимость, использует delivery_time_window)"""
        return self.delivery_time_window

    @property
    def is_delivery(self):
        """Является ли заказ доставкой (обратная совместимость)"""
        if hasattr(self, 'delivery') and self.delivery:
            from .delivery import Delivery
            return self.delivery.delivery_type == Delivery.DELIVERY_TYPE_COURIER
        return False

    @property
    def delivery_address(self):
        """Адрес доставки (обратная совместимость)"""
        if hasattr(self, 'delivery') and self.delivery:
            return self.delivery.address
        return None

    @property
    def delivery_cost(self):
        """Стоимость доставки (обратная совместимость)"""
        if hasattr(self, 'delivery') and self.delivery:
            return self.delivery.cost
        return 0

    @property
    def pickup_warehouse(self):
        """Склад самовывоза (обратная совместимость)"""
        if hasattr(self, 'delivery') and self.delivery:
            return self.delivery.pickup_warehouse
        return None

    @property
    def is_customer_recipient(self):
        """Является ли покупатель получателем (обратная совместимость)"""
        return self.recipient is None
