"""
Сервис для расчета себестоимости товаров на основе партий (FIFO).
Извлекает сложную бизнес-логику из модели.
"""
from decimal import Decimal, InvalidOperation
import logging

logger = logging.getLogger(__name__)


class ProductCostCalculator:
    """
    Калькулятор себестоимости для Product.
    Рассчитывает средневзвешенную стоимость на основе активных партий товара.
    """

    @staticmethod
    def calculate_weighted_average_cost(product):
        """
        Рассчитать средневзвешенную себестоимость из активных партий товара.

        Логика:
        - Если нет активных партий с quantity > 0: возвращает 0.00
        - Если есть партии: (сумма(quantity * cost_price) / сумма(quantity))

        Args:
            product: Объект Product для расчета себестоимости

        Returns:
            Decimal: Средневзвешенная себестоимость, округленная до 2 знаков
        """
        from inventory.models import StockBatch

        try:
            # Получаем все активные партии товара с остатками
            batches = StockBatch.objects.filter(
                product=product,
                is_active=True,
                quantity__gt=0
            ).values('quantity', 'cost_price')

            if not batches:
                logger.debug(f"Товар {product.sku} не имеет активных партий. Себестоимость = 0")
                return Decimal('0.00')

            # Рассчитываем средневзвешенную стоимость
            total_value = Decimal('0.00')
            total_quantity = Decimal('0.00')

            for batch in batches:
                quantity = Decimal(str(batch['quantity']))
                cost_price = Decimal(str(batch['cost_price']))

                total_value += quantity * cost_price
                total_quantity += quantity

            if total_quantity == 0:
                logger.debug(f"Товар {product.sku} имеет партии, но общее количество = 0. Себестоимость = 0")
                return Decimal('0.00')

            # Рассчитываем средневзвешенную стоимость
            weighted_cost = total_value / total_quantity

            # Округляем до 2 знаков после запятой
            result = weighted_cost.quantize(Decimal('0.01'))

            logger.debug(
                f"Товар {product.sku}: средневзвешенная себестоимость = {result} "
                f"(партий: {len(batches)}, количество: {total_quantity})"
            )

            return result

        except (InvalidOperation, ZeroDivisionError) as e:
            logger.error(
                f"Ошибка при расчете себестоимости для товара {product.sku}: {e}",
                exc_info=True
            )
            return Decimal('0.00')

    @staticmethod
    def update_product_cost(product, save=True):
        """
        Обновить кешированную себестоимость товара.

        Рассчитывает новую себестоимость и обновляет поле cost_price,
        если значение изменилось.

        Args:
            product: Объект Product для обновления
            save: Если True, сохраняет изменения в БД (default: True)

        Returns:
            tuple: (old_cost, new_cost, was_updated)
        """
        old_cost = product.cost_price
        new_cost = ProductCostCalculator.calculate_weighted_average_cost(product)

        was_updated = False

        if old_cost != new_cost:
            product.cost_price = new_cost

            if save:
                product.save(update_fields=['cost_price'])
                logger.info(
                    f"Обновлена себестоимость товара {product.sku}: "
                    f"{old_cost} -> {new_cost}"
                )

            was_updated = True
        else:
            logger.debug(
                f"Себестоимость товара {product.sku} не изменилась: {old_cost}"
            )

        return (old_cost, new_cost, was_updated)

    @staticmethod
    def get_cost_details(product):
        """
        Получить детальную информацию о расчете себестоимости товара.

        Возвращает детали по каждой партии для отображения в UI.

        Args:
            product: Объект Product

        Returns:
            dict: {
                'cached_cost': Decimal,  # Кешированная себестоимость
                'calculated_cost': Decimal,  # Рассчитанная себестоимость
                'is_synced': bool,  # Совпадают ли значения
                'total_quantity': Decimal,  # Общее количество в партиях
                'batches': [  # Список партий
                    {
                        'warehouse_name': str,
                        'warehouse_id': int,
                        'quantity': Decimal,
                        'cost_price': Decimal,
                        'total_value': Decimal,
                        'created_at': datetime,
                    },
                    ...
                ]
            }
        """
        from inventory.models import StockBatch

        cached_cost = product.cost_price
        calculated_cost = ProductCostCalculator.calculate_weighted_average_cost(product)

        # Получаем все активные партии товара с остатками
        batches_qs = StockBatch.objects.filter(
            product=product,
            is_active=True,
            quantity__gt=0
        ).select_related('warehouse').order_by('created_at')

        batches_list = []
        total_quantity = Decimal('0.00')

        for batch in batches_qs:
            quantity = batch.quantity
            cost_price = batch.cost_price
            total_value = quantity * cost_price

            batches_list.append({
                'warehouse_name': batch.warehouse.name,
                'warehouse_id': batch.warehouse.id,
                'quantity': quantity,
                'cost_price': cost_price,
                'total_value': total_value,
                'created_at': batch.created_at,
            })

            total_quantity += quantity

        return {
            'cached_cost': cached_cost,
            'calculated_cost': calculated_cost,
            'is_synced': cached_cost == calculated_cost,
            'total_quantity': total_quantity,
            'batches': batches_list,
        }
