from decimal import Decimal
from django.db import transaction
from django.core.exceptions import ValidationError


class DiscountApplier:
    """
    Сервис для применения скидок к заказам.
    Поддерживает комбинирование скидок по combine_mode.
    Все операции атомарны.
    """

    @staticmethod
    @transaction.atomic
    def apply_promo_code(order, promo_code, user=None):
        """
        Применить промокод к заказу.
        Поддерживает комбинирование скидок.

        Args:
            order: Order
            promo_code: str
            user: CustomUser (применивший скидку)

        Returns:
            dict: {
                'success': bool,
                'discounts': [{'discount': Discount, 'amount': Decimal}, ...],
                'total_amount': Decimal,
                'error': str
            }
        """
        from discounts.models import PromoCode, DiscountApplication
        from discounts.services.calculator import DiscountCalculator

        # Удаляем предыдущую скидку на заказ
        DiscountApplier._remove_order_discount_only(order)

        # Рассчитываем скидку
        result = DiscountCalculator.calculate_order_discount(order, promo_code)

        if result['error']:
            return {
                'success': False,
                'error': result['error']
            }

        promo = result['promo_code']
        discounts_data = result['discounts']
        total_amount = result['total_amount']

        # Создаем записи о применении для каждой скидки в DiscountApplication
        for disc_data in discounts_data:
            discount = disc_data['discount']
            amount = disc_data['amount']

            DiscountApplication.objects.create(
                order=order,
                discount=discount,
                promo_code=promo,
                target='order',
                base_amount=order.subtotal,
                discount_amount=amount,
                final_amount=order.subtotal - amount,
                customer=order.customer,
                applied_by=user or order.modified_by
            )

            # Увеличиваем счетчик использований скидки
            discount.current_usage_count += 1
            discount.save(update_fields=['current_usage_count'])

        # Пересчитываем total_amount (использует DiscountApplication)
        order.calculate_total()

        # Регистрируем использование промокода
        promo.record_usage(order.customer)

        return {
            'success': True,
            'discounts': discounts_data,
            'total_amount': total_amount
        }

    @staticmethod
    @transaction.atomic
    def apply_auto_discounts(order, user=None):
        """
        Применить автоматические скидки к заказу и позициям.
        Поддерживает комбинирование скидок.

        Args:
            order: Order
            user: CustomUser

        Returns:
            dict: {
                'order_discounts': [...],
                'item_discounts': [...],
                'total_discount': Decimal
            }
        """
        from discounts.models import Discount, DiscountApplication
        from discounts.services.calculator import DiscountCalculator

        result = {
            'order_discounts': [],
            'item_discounts': [],
            'total_discount': Decimal('0')
        }

        # 1. Применяем скидки на заказ (может быть несколько)
        order_result = DiscountCalculator.calculate_order_discount(order)

        if order_result['discounts'] and not order_result['error']:
            total_order_amount = order_result['total_amount']

            # Создаем записи о применении для всех скидок
            for disc_data in order_result['discounts']:
                discount = disc_data['discount']
                amount = disc_data['amount']

                DiscountApplication.objects.create(
                    order=order,
                    discount=discount,
                    target='order',
                    base_amount=order.subtotal,
                    discount_amount=amount,
                    final_amount=order.subtotal - amount,
                    customer=order.customer,
                    applied_by=user
                )

                # Увеличиваем счетчик
                discount.current_usage_count += 1
                discount.save(update_fields=['current_usage_count'])

            result['order_discounts'] = order_result['discounts']
            result['total_discount'] += total_order_amount

        # 2. Применяем скидки на позиции
        available_product_discounts = list(DiscountCalculator.get_available_discounts(
            scope='product',
            auto_only=True
        ))

        for item in order.items.all():
            item_result = DiscountCalculator.calculate_item_discount(
                item, available_product_discounts
            )

            if item_result['discounts']:
                total_item_amount = item_result['total_amount']

                # Создаем записи о применении для всех скидок
                base_amount = item.price * item.quantity
                for disc_data in item_result['discounts']:
                    discount = disc_data['discount']
                    amount = disc_data['amount']

                    DiscountApplication.objects.create(
                        order=order,
                        order_item=item,
                        discount=discount,
                        target='order_item',
                        base_amount=base_amount,
                        discount_amount=amount,
                        final_amount=base_amount - amount,
                        customer=order.customer,
                        applied_by=user
                    )

                    # Увеличиваем счетчик
                    discount.current_usage_count += 1
                    discount.save(update_fields=['current_usage_count'])

                result['item_discounts'].append({
                    'item': item,
                    'discounts': item_result['discounts'],
                    'total_amount': total_item_amount
                })
                result['total_discount'] += total_item_amount

        # Пересчитываем итоговую сумму
        order.calculate_total()

        return result

    @staticmethod
    @transaction.atomic
    def remove_discount_from_order(order):
        """
        Удалить скидку с заказа.

        Args:
            order: Order
        """
        # Удаляем записи о применении
        from discounts.models import DiscountApplication
        DiscountApplication.objects.filter(order=order).delete()

        # Пересчитываем
        order.calculate_total()

    @staticmethod
    @transaction.atomic
    def apply_manual_discount(order, discount, user=None):
        """
        Применить скидку вручную к заказу.

        Args:
            order: Order
            discount: Discount
            user: CustomUser (применивший скидку)

        Returns:
            dict: {
                'success': bool,
                'discount_amount': Decimal,
                'error': str
            }
        """
        from discounts.models import DiscountApplication

        # Проверяем scope скидки
        if discount.scope != 'order':
            return {'success': False, 'error': 'Эта скидка не применяется к заказу'}

        # Проверяем мин. сумму
        if discount.min_order_amount and order.subtotal < discount.min_order_amount:
            return {'success': False, 'error': f'Мин. сумма заказа: {discount.min_order_amount} руб.'}

        # Удаляем предыдущую скидку на заказ
        DiscountApplier._remove_order_discount_only(order)

        # Рассчитываем сумму
        discount_amount = discount.calculate_discount_amount(Decimal(order.subtotal))

        # Создаем запись о применении
        DiscountApplication.objects.create(
            order=order,
            discount=discount,
            target='order',
            base_amount=order.subtotal,
            discount_amount=discount_amount,
            final_amount=order.subtotal - discount_amount,
            customer=order.customer,
            applied_by=user
        )

        # Увеличиваем счетчик использований скидки
        discount.current_usage_count += 1
        discount.save(update_fields=['current_usage_count'])

        # Пересчитываем total_amount
        order.calculate_total()

        return {
            'success': True,
            'discount_amount': discount_amount
        }

    @staticmethod
    def _remove_order_discount_only(order):
        """
        Удалить только скидку с заказа (не трогая позиции).

        Args:
            order: Order
        """
        from discounts.models import DiscountApplication

        # Удаляем записи о применении скидок к заказу
        DiscountApplication.objects.filter(order=order, target='order').delete()

        # Пересчитываем (order.discount_amount теперь свойство, берущее из DiscountApplication)
        order.calculate_total()
