from decimal import Decimal
from rest_framework import serializers
from api.models.data.raw_materials import RawMaterial, RawMaterialPurchase, RawMaterialPurchaseItem, RawMaterialStock
from api.models.data.currency import CURRENCY_CHOICES, currency_details as _currency_details_fn
from api.serializers.data.base import DataRootSerializer
from api.mixins.base_currency import BaseCurrencySerializerMixin
from api.utils.order_numeric import quantize_order_amount


class RawMaterialSerializer(DataRootSerializer):
    """Serializer for RawMaterial model."""
    class Meta:
        model = RawMaterial
        fields = "__all__"
        read_only_fields = ['created_at', 'updated_at']

    def validate(self, attrs):
        attrs = super().validate(attrs)
        material_type = attrs.get('material_type', getattr(
            self.instance, 'material_type', None))
        capacity = attrs.get('capacity_liters', getattr(
            self.instance, 'capacity_liters', None))
        bottles_per_carton = attrs.get('bottles_per_carton', getattr(
            self.instance, 'bottles_per_carton', None))

        if material_type in {RawMaterial.TYPE_BOTTLE, RawMaterial.TYPE_PLASTIC, RawMaterial.TYPE_CARTON}:
            if capacity is None:
                raise serializers.ValidationError({
                    'capacity_liters': (
                        'Bottle volume is required for bottle materials. '
                        'Plastic and carton must set the matching bottle size.'
                    )
                })
            if Decimal(str(capacity)) <= 0:
                raise serializers.ValidationError({
                    'capacity_liters': 'Bottle volume / matching bottle size must be greater than zero.'
                })
        elif capacity is not None and Decimal(str(capacity)) <= 0:
            raise serializers.ValidationError({
                'capacity_liters': 'Capacity must be greater than zero when provided.'
            })

        if material_type == RawMaterial.TYPE_CARTON:
            if bottles_per_carton is None:
                raise serializers.ValidationError({
                    'bottles_per_carton': 'Bottles per carton is required for carton materials.'
                })
            if int(bottles_per_carton) < 1:
                raise serializers.ValidationError({
                    'bottles_per_carton': 'Bottles per carton must be at least 1.'
                })
        elif 'bottles_per_carton' in attrs:
            attrs['bottles_per_carton'] = None

        display_unit = attrs.get(
            'capacity_display_unit',
            getattr(self.instance, 'capacity_display_unit', None),
        )
        if display_unit not in {
            RawMaterial.CAPACITY_UNIT_ML,
            RawMaterial.CAPACITY_UNIT_L,
            None,
            '',
        }:
            raise serializers.ValidationError({
                'capacity_display_unit': 'Capacity display unit must be ml or L.',
            })
        if material_type not in {
            RawMaterial.TYPE_BOTTLE,
            RawMaterial.TYPE_PLASTIC,
            RawMaterial.TYPE_CARTON,
        }:
            if 'capacity_display_unit' in attrs:
                attrs['capacity_display_unit'] = RawMaterial.CAPACITY_UNIT_ML

        return attrs

    def validate_unit(self, value):
        if self.instance and self.instance.unit != value:
            if self.instance.purchase_items.exists() or self.instance.stocks.filter(quantity__gt=0).exists():
                raise serializers.ValidationError(
                    'Unit cannot be changed after this raw material has purchase or stock history.'
                )
        return value


class RawMaterialPurchaseItemSerializer(DataRootSerializer):
    """Serializer for RawMaterialPurchaseItem model."""
    raw_material_details = serializers.SerializerMethodField()

    class Meta:
        model = RawMaterialPurchaseItem
        fields = "__all__"
        read_only_fields = ['total_price',
                            'price_per_unit_base', 'total_price_base']

    def get_raw_material_details(self, obj):
        if obj.raw_material:
            return {
                "id": obj.raw_material.id,
                "name": obj.raw_material.name,
                "unit": obj.raw_material.unit,
                "material_type": obj.raw_material.material_type,
                "capacity_liters": str(obj.raw_material.capacity_liters) if obj.raw_material.capacity_liters is not None else None,
                "capacity_display_unit": obj.raw_material.capacity_display_unit,
                "bottles_per_carton": obj.raw_material.bottles_per_carton,
            }
        return None

    def validate(self, attrs):
        attrs = super().validate(attrs)

        quantity = attrs.get('quantity')
        price_per_unit = attrs.get('price_per_unit')
        raw_material = attrs.get('raw_material')
        if raw_material is None and self.instance is not None:
            raw_material = self.instance.raw_material

        piece_count = attrs.get('piece_count')
        if piece_count is None and self.instance is not None:
            piece_count = self.instance.piece_count
        if raw_material and raw_material.unit == RawMaterial.UNIT_PIECE and piece_count:
            raise serializers.ValidationError({
                'piece_count': 'Additional piece count is only available for kilogram-based materials.'
            })

        if quantity is not None and price_per_unit is not None:
            attrs['total_price'] = quantity * price_per_unit

        return attrs


class RawMaterialPurchaseSerializer(BaseCurrencySerializerMixin, DataRootSerializer):
    """Serializer for RawMaterialPurchase model."""

    base_currency_amount_fields = ('total_amount',)
    base_currency_date_field = 'purchase_date'

    currency = serializers.ChoiceField(choices=CURRENCY_CHOICES)
    vendor_details = serializers.SerializerMethodField()
    currency_details = serializers.SerializerMethodField()
    items = RawMaterialPurchaseItemSerializer(many=True, read_only=True)
    subtotal_amount = serializers.SerializerMethodField()
    final_total = serializers.SerializerMethodField()
    remaining_amount = serializers.SerializerMethodField()

    class Meta:
        model = RawMaterialPurchase
        fields = "__all__"
        read_only_fields = (
            'invoice_number', 'exchange_rate', 'total_amount_base',
            'paid_amount_base',
            'subtotal_amount', 'final_total', 'remaining_amount',
        )

    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])

        if self.instance and self.instance.status == 'received':
            currency = attrs.get('currency', self.instance.currency)
            purchase_date = attrs.get(
                'purchase_date', self.instance.purchase_date)
            if (
                int(currency) == self.instance.currency
                and purchase_date.date() == self.instance.purchase_date.date()
            ):
                attrs['exchange_rate'] = self.instance.exchange_rate
                amount = attrs.get('total_amount', self.instance.total_amount)
                attrs['total_amount_base'] = quantize_order_amount(
                    amount * self.instance.exchange_rate
                )

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

        return attrs

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

    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_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)


class RawMaterialPurchaseListSerializer(RawMaterialPurchaseSerializer):
    """Lightweight list serializer for raw material purchases."""
    class Meta(RawMaterialPurchaseSerializer.Meta):
        fields = (
            'id',
            'invoice_number',
            'bill_number',
            'purchase_date',
            'status',
            'vendor',
            'vendor_details',
            'currency',
            'currency_details',
            'total_amount',
            'total_amount_base',
            'exchange_rate',
            'paid_amount',
            'final_total',
            'remaining_amount',
            'notes',
        )


class RawMaterialStockSerializer(DataRootSerializer):
    """Serializer for RawMaterialStock model."""
    raw_material_details = serializers.SerializerMethodField()
    cost_layers = serializers.SerializerMethodField()

    class Meta:
        model = RawMaterialStock
        fields = "__all__"
        read_only_fields = ['last_purchase_price', 'last_purchase_date']

    def get_raw_material_details(self, obj):
        if obj.raw_material:
            return {
                "id": obj.raw_material.id,
                "name": obj.raw_material.name,
                "unit": obj.raw_material.unit,
                "material_type": obj.raw_material.material_type,
                "capacity_liters": str(obj.raw_material.capacity_liters) if obj.raw_material.capacity_liters is not None else None,
                "capacity_display_unit": obj.raw_material.capacity_display_unit,
                "bottles_per_carton": obj.raw_material.bottles_per_carton,
            }
        return None

    def get_cost_layers(self, obj):
        items = getattr(obj.raw_material, 'received_purchase_items', None)
        if items is None:
            items = RawMaterialPurchaseItem.objects.filter(
                raw_material=obj.raw_material,
                purchase__status='received',
                purchase__deleted_at__isnull=True,
                quantity__gt=0,
            ).select_related('purchase').order_by(
                'purchase__purchase_date', 'id'
            )

        return [
            {
                'id': item.id,
                'purchase_id': item.purchase_id,
                'invoice_number': item.purchase.invoice_number,
                'bill_number': item.purchase.bill_number or '',
                'purchase_date': item.purchase.purchase_date,
                'currency': item.purchase.currency,
                'currency_details': _currency_details_fn(item.purchase.currency),
                'quantity': item.quantity,
                'piece_count': item.piece_count,
                'price_per_unit': item.price_per_unit,
                'total_price': item.total_price,
                'price_per_unit_base': item.price_per_unit_base,
                'total_price_base': item.total_price_base,
            }
            for item in items
        ]


class RawMaterialStockUpdateSerializer(DataRootSerializer):
    """Serializer for updating raw material stock."""
    quantity_change = serializers.DecimalField(
        max_digits=12,
        decimal_places=4,
        required=True,
        help_text='Amount to add (positive) or subtract (negative) from stock'
    )
    price_per_unit = serializers.DecimalField(
        max_digits=12,
        decimal_places=4,
        required=False,
        help_text=(
            'New unit price in AFN (optional). Raw material stock always stores '
            'last_purchase_price in AFN — convert foreign-currency prices before sending.'
        ),
    )
    notes = serializers.CharField(required=False, allow_blank=True)

    class Meta:
        model = RawMaterialStock
        fields = ['quantity_change', 'price_per_unit', 'notes']

    def validate_quantity_change(self, value):
        if value == 0:
            raise serializers.ValidationError(
                'Quantity change cannot be zero.')
        return value
