from decimal import Decimal

from rest_framework import serializers
from api.models.data.currency import currency_details as _currency_details_fn, get_currency as _get_currency
from api.models.data.sales import Sales, SalesItems
from api.serializers.data.base import DataRootSerializer
from api.mixins.base_currency import BaseCurrencySerializerMixin
from api.utils.order_numeric import quantize_order_amount


def _currency_code(currency_id):
    c = _get_currency(currency_id)
    return c['code'] if c else str(currency_id or '')


def sales_item_commission_total(item) -> Decimal:
    """Line commission in AFN = quantity (selected unit) × commission per unit.

    Commission is always AFN and must never be FX-converted with the sale currency.
    """
    qty = Decimal(str(getattr(item, 'quantity', 0) or 0))
    rate = Decimal(str(getattr(item, 'commission_amount', 0) or 0))
    return quantize_order_amount(qty * rate)


class SalesItemsSerializer(DataRootSerializer):
    product_details = serializers.SerializerMethodField()
    stock_details = serializers.SerializerMethodField()
    storage_tank_details = serializers.SerializerMethodField()
    commission_total = serializers.SerializerMethodField()

    class Meta:
        model = SalesItems
        fields = "__all__"

    def get_commission_total(self, obj):
        return float(sales_item_commission_total(obj))

    def get_product_details(self, obj):
        if obj.sale_source == SalesItems.SOURCE_TANK and obj.storage_tank_id:
            from api.services.tank_inventory import tank_product_label
            return {
                'id': None,
                'name': tank_product_label(obj.storage_tank),
                'is_tank_fill': True,
            }
        if obj.stock and obj.stock.product:
            from api.serializers.data.products import serialize_product
            return serialize_product(obj.stock.product, self.context.get('request'))
        return None

    def get_stock_details(self, obj):
        if not obj.stock_id:
            return None
        from api.utils.packaging import (
            bottles_per_carton,
            cartons_from_pieces,
            price_per_carton_from_piece,
            remainder_pieces,
        )
        from api.serializers.data.products import serialize_product

        pieces = obj.stock.piece_amount or 0
        piece_price = obj.stock.buy_price or 0
        carton_size = bottles_per_carton(obj.stock.product)
        return {
            "id": obj.stock.id,
            "product_details": serialize_product(obj.stock.product, self.context.get('request')),
            "piece_amount": pieces,
            "bottles_per_carton": int(carton_size),
            "carton_amount": float(cartons_from_pieces(pieces, carton_size)),
            "loose_pieces": float(remainder_pieces(pieces, carton_size)),
            "buy_price": piece_price,
            "buy_price_per_carton": float(price_per_carton_from_piece(piece_price, carton_size)),
            "condition": getattr(obj.stock, 'condition', 'good'),
            "currency": obj.stock.currency,
            "currency_code": _currency_code(obj.stock.currency),
        }

    def get_storage_tank_details(self, obj):
        if not obj.storage_tank_id:
            return None
        from api.services.tank_inventory import get_tank_liquid_totals, tank_product_label

        tank = obj.storage_tank
        try:
            current_liters, _, cost_per_liter = get_tank_liquid_totals(
                tank,
                exclude_sale_item_id=obj.pk,
            )
        except Exception:
            current_liters, cost_per_liter = 0, 0
        return {
            'id': tank.id,
            'name': tank.name,
            'product_name': tank_product_label(tank),
            'current_liters': str(current_liters),
            'liquid_cost_per_liter_afn': str(cost_per_liter),
            'capacity_liters': str(tank.capacity_liters),
        }

    def validate(self, attrs):
        from api.utils.packaging import bottles_per_carton, pieces_from_units

        attrs = super().validate(attrs)
        source = attrs.get('sale_source')
        if source is None and self.instance is not None:
            source = self.instance.sale_source
        source = source or SalesItems.SOURCE_STOCK

        unit = attrs.get('unit')
        quantity = attrs.get('quantity')
        stock = attrs.get('stock', serializers.empty)
        tank = attrs.get('storage_tank', serializers.empty)
        if self.instance:
            if unit is None:
                unit = self.instance.unit
            if quantity is None:
                quantity = self.instance.quantity
            if stock is serializers.empty:
                stock = self.instance.stock
            if tank is serializers.empty:
                tank = self.instance.storage_tank
        else:
            if stock is serializers.empty:
                stock = None
            if tank is serializers.empty:
                tank = None

        attrs['sale_source'] = source

        if source == SalesItems.SOURCE_TANK:
            if tank is None:
                raise serializers.ValidationError({
                    'storage_tank': 'Select a storage tank for tank-fill sales.',
                })
            if stock is not None:
                attrs['stock'] = None
            attrs['unit'] = SalesItems.UNIT_LITER
            unit = SalesItems.UNIT_LITER
            if quantity is not None:
                attrs['quantity'] = quantize_order_amount(quantity)
                attrs['liter_amount'] = attrs['quantity']
        else:
            if stock is None:
                raise serializers.ValidationError({
                    'stock': 'Select packed stock for this sale line.',
                })
            if tank is not None:
                attrs['storage_tank'] = None
            if unit == SalesItems.UNIT_LITER:
                raise serializers.ValidationError({
                    'unit': 'Packed stock sales use carton, bottle, or plastic units.',
                })
            # Normalize legacy dana → bottle
            if unit == SalesItems.UNIT_DANA:
                unit = SalesItems.UNIT_BOTTLE
            unit = unit or SalesItems.UNIT_CARTON
            if unit not in {
                SalesItems.UNIT_CARTON,
                SalesItems.UNIT_BOTTLE,
                SalesItems.UNIT_PLASTIC,
                SalesItems.UNIT_DANA,
            }:
                raise serializers.ValidationError({
                    'unit': 'Packed stock sales use carton, bottle, or plastic.',
                })
            attrs['unit'] = unit
            if quantity is not None:
                carton_size = bottles_per_carton(getattr(stock, 'product', None))
                attrs['quantity'] = quantize_order_amount(quantity)
                attrs['liter_amount'] = quantize_order_amount(
                    pieces_from_units(attrs['quantity'], unit, carton_size)
                )

        for field in ('liter_amount', 'sell_price', 'unit_cost', 'quantity', 'commission_amount'):
            if field in attrs and attrs[field] is not None:
                attrs[field] = quantize_order_amount(attrs[field])

        commission = attrs.get('commission_amount')
        if commission is None and self.instance is not None:
            commission = self.instance.commission_amount
        if commission is not None and commission < 0:
            raise serializers.ValidationError({
                'commission_amount': 'Commission cannot be negative.',
            })

        return attrs


class SalesSerializer(BaseCurrencySerializerMixin, DataRootSerializer):
    base_currency_amount_fields = ('total_amount', 'paid_amount')
    base_currency_date_field = 'sale_date'

    currency = serializers.IntegerField()
    customer_details = serializers.SerializerMethodField()
    salesman_details = serializers.SerializerMethodField()
    currency_details = serializers.SerializerMethodField()
    items = SalesItemsSerializer(many=True, read_only=True, source='sales_items')
    subtotal_amount = serializers.SerializerMethodField()
    discount_amount = serializers.SerializerMethodField()
    final_total = serializers.SerializerMethodField()
    remaining_amount = serializers.SerializerMethodField()
    total_commission = serializers.SerializerMethodField()
    documents = serializers.SerializerMethodField()

    class Meta:
        model = Sales
        fields = "__all__"
        read_only_fields = (
            'exchange_rate', 'total_amount_base', 'paid_amount_base',
        )

    def validate(self, attrs):
        attrs = super().validate(attrs)
        for field in ('total_amount', 'paid_amount'):
            if field in attrs and attrs[field] is not None:
                attrs[field] = quantize_order_amount(attrs[field])

        customer = attrs.get('customer')
        if customer is None and self.instance is not None:
            customer = self.instance.customer
        if customer is None:
            raise serializers.ValidationError({'customer': 'Customer is required.'})

        currency = attrs.get('currency')
        if currency is None and self.instance is not None:
            currency = self.instance.currency
        # Sales invoices are AFN (1) or USD (2) only — inventory costs are AFN.
        if currency is not None and int(currency) not in (1, 2):
            raise serializers.ValidationError({
                'currency': 'Sales currency must be AFN or USD.',
            })

        salesman = attrs.get('salesman', serializers.empty)
        if salesman is serializers.empty and self.instance is not None:
            salesman = self.instance.salesman
        if salesman is serializers.empty:
            salesman = None
        # Empty string / 0 from clients → null
        if salesman in ('', 0, '0'):
            attrs['salesman'] = None
            salesman = None

        return attrs

    def get_customer_details(self, obj):
        if obj.customer:
            return {
                "id": obj.customer.id,
                "name": obj.customer.name,
                "phone": obj.customer.phone,
                "email": obj.customer.email,
                "address": obj.customer.address,
            }
        return None

    def get_salesman_details(self, obj):
        if obj.salesman_id and obj.salesman:
            return {
                "id": obj.salesman.id,
                "name": obj.salesman.name,
                "phone": obj.salesman.phone,
            }
        return None

    def get_total_commission(self, obj):
        """Sum of line commissions in AFN (independent of sale invoice currency)."""
        total = Decimal('0')
        for item in obj.sales_items.all():
            total += sales_item_commission_total(item)
        return float(total)

    def get_currency_details(self, obj):
        return _currency_details_fn(obj.currency)

    def get_subtotal_amount(self, obj):
        return float(obj.subtotal_amount)

    def get_discount_amount(self, obj):
        return float(obj.discount_amount)

    def get_final_total(self, obj):
        return float(obj.final_total)

    def get_remaining_amount(self, obj):
        total = float(obj.final_total if hasattr(obj, 'final_total') else (obj.total_amount or 0))
        paid = float(obj.paid_amount or 0)
        return max(0.0, total - paid)

    def get_documents(self, obj):
        from api.services.attachments import serialize_document

        docs = getattr(obj, 'documents', None)
        if docs is None:
            return []
        request = self.context.get('request')
        return [serialize_document(doc, request) for doc in docs.all()]


class SalesListSerializer(SalesSerializer):
    """Lightweight list/picker payload — no line items."""

    class Meta(SalesSerializer.Meta):
        fields = (
            'id',
            'invoice_number',
            'sale_date',
            'status',
            'customer',
            'customer_details',
            'salesman',
            'salesman_details',
            'currency',
            'currency_details',
            'total_amount',
            'total_amount_base',
            'paid_amount',
            'paid_amount_base',
            'exchange_rate',
            'final_total',
            'remaining_amount',
            'total_commission',
            'discount_percentage',
            'notes',
        )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields.pop('documents', None)
