from decimal import Decimal
from rest_framework import serializers
from rest_framework.exceptions import ParseError
from apps.utils.serializers import CustomModelSerializer
from apps.utils.constants import EXCLUDE_FIELDS
from apps.mpr.models import (
    PurchaseRequisition, PurchaseRequisitionItem,
    WarehouseEntry, WarehouseEntryItem, WarehouseStock,
    MaterialRequisition, MaterialRequisitionItem,
    WareHouse,
)
from apps.wf.serializers import TicketSimpleSerializer


# ========== 库房 ==========

class WareHouseSerializer(CustomModelSerializer):
    class Meta:
        model = WareHouse
        fields = '__all__'
        read_only_fields = ['create_time', 'update_time', 'is_deleted']


# ========== 物资申购单 ==========

class PurchaseRequisitionItemSerializer(CustomModelSerializer):
    class Meta:
        model = PurchaseRequisitionItem
        fields = '__all__'
        read_only_fields = ['create_time', 'update_time', 'is_deleted']


class PurchaseRequisitionListSerializer(CustomModelSerializer):
    create_by_name = serializers.CharField(source='create_by.name', read_only=True)
    belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
    ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)

    class Meta:
        model = PurchaseRequisition
        fields = '__all__'
        read_only_fields = EXCLUDE_FIELDS


class PurchaseRequisitionDetailSerializer(CustomModelSerializer):
    create_by_name = serializers.CharField(source='create_by.name', read_only=True)
    belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
    ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
    items_ = PurchaseRequisitionItemSerializer(source='items', many=True, read_only=True)

    class Meta:
        model = PurchaseRequisition
        fields = '__all__'
        read_only_fields = EXCLUDE_FIELDS


class PurchaseRequisitionCreateSerializer(CustomModelSerializer):
    items = serializers.ListField(child=serializers.DictField(), write_only=True, required=False, default=[])

    class Meta:
        model = PurchaseRequisition
        fields = '__all__'
        read_only_fields = EXCLUDE_FIELDS + ['belong_dept', 'number', 'total_amount']

    def create(self, validated_data):
        items_data = validated_data.pop('items', [])
        validated_data['number'] = PurchaseRequisition.get_a_number()
        instance = super().create(validated_data)
        self._save_items(instance, items_data)
        return instance

    def update(self, instance, validated_data):
        items_data = validated_data.pop('items', None)
        instance = super().update(instance, validated_data)
        if items_data is not None:
            instance.items.all().delete()
            self._save_items(instance, items_data)
        return instance

    def _save_items(self, instance, items_data):
        total = 0
        for item in items_data:
            item.pop('id', None)
            unit_price = float(item.get('unit_price', 0) or 0)
            purchase_quantity = float(item.get('purchase_quantity', 0) or 0)
            item['total_price'] = round(unit_price * purchase_quantity, 2)
            total += item['total_price']
            PurchaseRequisitionItem.objects.create(requisition=instance, **item)
        instance.total_amount = total
        instance.save(update_fields=['total_amount'])


# ========== 仓库入库单 ==========

class WarehouseEntryItemSerializer(CustomModelSerializer):
    class Meta:
        model = WarehouseEntryItem
        fields = '__all__'
        read_only_fields = ['create_time', 'update_time', 'is_deleted']


class WarehouseEntryListSerializer(CustomModelSerializer):
    create_by_name = serializers.CharField(source='create_by.name', read_only=True)
    belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
    warehouse_name = serializers.CharField(source='warehouse.name', read_only=True)
    ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
    entry_type_display = serializers.CharField(source='get_entry_type_display', read_only=True)
    entry_method_display = serializers.CharField(source='get_entry_method_display', read_only=True)

    class Meta:
        model = WarehouseEntry
        fields = '__all__'
        read_only_fields = EXCLUDE_FIELDS


class WarehouseEntryDetailSerializer(CustomModelSerializer):
    create_by_name = serializers.CharField(source='create_by.name', read_only=True)
    belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
    warehouse_name = serializers.CharField(source='warehouse.name', read_only=True)
    ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
    items_ = WarehouseEntryItemSerializer(source='items', many=True, read_only=True)
    entry_type_display = serializers.CharField(source='get_entry_type_display', read_only=True)
    entry_method_display = serializers.CharField(source='get_entry_method_display', read_only=True)

    class Meta:
        model = WarehouseEntry
        fields = '__all__'
        read_only_fields = EXCLUDE_FIELDS


class WarehouseEntryCreateSerializer(CustomModelSerializer):
    items = serializers.ListField(child=serializers.DictField(), write_only=True, required=False, default=[])

    class Meta:
        model = WarehouseEntry
        fields = '__all__'
        read_only_fields = EXCLUDE_FIELDS + ['belong_dept', 'number', 'total_amount']

    def create(self, validated_data):
        items_data = validated_data.pop('items', [])
        validated_data['number'] = WarehouseEntry.get_a_number()
        instance = super().create(validated_data)
        self._save_items(instance, items_data)
        return instance

    def update(self, instance, validated_data):
        items_data = validated_data.pop('items', None)
        instance = super().update(instance, validated_data)
        if items_data is not None:
            instance.items.all().delete()
            self._save_items(instance, items_data)
        return instance

    def _save_items(self, instance, items_data):
        total = 0
        for item in items_data:
            item.pop('id', None)
            unit_price = float(item.get('unit_price', 0) or 0)
            quantity = float(item.get('quantity', 0) or 0)
            item['amount'] = round(unit_price * quantity, 2)
            total += item['amount']
            WarehouseEntryItem.objects.create(entry=instance, **item)
        instance.total_amount = total
        instance.save(update_fields=['total_amount'])


# ========== 物料库存 ==========

class WarehouseStockSerializer(CustomModelSerializer):
    warehouse_name = serializers.CharField(source='warehouse.name', read_only=True)
    entry_type_display = serializers.CharField(source='get_entry_type_display', read_only=True)
    entry_method_display = serializers.CharField(source='get_entry_method_display', read_only=True)
    status_display = serializers.SerializerMethodField()

    class Meta:
        model = WarehouseStock
        fields = '__all__'
        read_only_fields = ['create_time', 'update_time', 'is_deleted']

    def get_status_display(self, obj):
        if obj.quantity <= 0:
            return '已领完'
        return obj.get_status_display()


# ========== 物资领用单 ==========

class MaterialRequisitionItemSerializer(CustomModelSerializer):
    stock_name = serializers.CharField(source='stock.name', read_only=True, default='')

    class Meta:
        model = MaterialRequisitionItem
        fields = '__all__'
        read_only_fields = ['create_time', 'update_time', 'is_deleted']


class MaterialRequisitionListSerializer(CustomModelSerializer):
    create_by_name = serializers.CharField(source='create_by.name', read_only=True)
    belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
    ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)

    class Meta:
        model = MaterialRequisition
        fields = '__all__'
        read_only_fields = EXCLUDE_FIELDS


class MaterialRequisitionDetailSerializer(CustomModelSerializer):
    create_by_name = serializers.CharField(source='create_by.name', read_only=True)
    belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
    ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
    items_ = MaterialRequisitionItemSerializer(source='items', many=True, read_only=True)

    class Meta:
        model = MaterialRequisition
        fields = '__all__'
        read_only_fields = EXCLUDE_FIELDS


class MaterialRequisitionCreateSerializer(CustomModelSerializer):
    items = serializers.ListField(child=serializers.DictField(), write_only=True, required=False, default=[])

    class Meta:
        model = MaterialRequisition
        fields = '__all__'
        read_only_fields = EXCLUDE_FIELDS + ['belong_dept', 'number']

    def create(self, validated_data):
        items_data = validated_data.pop('items', [])
        validated_data['number'] = MaterialRequisition.get_a_number()
        instance = super().create(validated_data)
        self._save_items(instance, items_data)
        return instance

    def update(self, instance, validated_data):
        items_data = validated_data.pop('items', None)
        instance = super().update(instance, validated_data)
        if items_data is not None:
            self._restore_stock(instance)
            instance.items.all().delete()
            self._save_items(instance, items_data)
        return instance

    def _save_items(self, instance, items_data):
        for item in items_data:
            item.pop('id', None)
            is_stock_item = item.get('is_stock_item', True)
            stock_id = item.pop('stock', None) or item.pop('stock_id', None)
            quantity = Decimal(str(item.get('quantity', 0) or 0))

            stock_obj = None
            if is_stock_item and stock_id:
                try:
                    stock_obj = WarehouseStock.objects.select_for_update().get(id=stock_id)
                except WarehouseStock.DoesNotExist:
                    raise ParseError(f"库存记录不存在: {stock_id}")
                if stock_obj.quantity < quantity:
                    raise ParseError(f"库存不足: {stock_obj.name} 库存{stock_obj.quantity} < 领用{quantity}")
                stock_obj.quantity -= quantity
                stock_obj.status = 'in_requisition'
                stock_obj.save(update_fields=['quantity', 'status'])

            MaterialRequisitionItem.objects.create(
                requisition=instance,
                is_stock_item=is_stock_item,
                stock=stock_obj,
                req_type=item.get('req_type', ''),
                name=item.get('name', ''),
                spec=item.get('spec', ''),
                unit=item.get('unit', ''),
                quantity=quantity,
                note=item.get('note', ''),
            )

    @staticmethod
    def _restore_stock(instance):
        """恢复库存（用于编辑或拒绝时）"""
        for item in instance.items.filter(is_stock_item=True, stock__isnull=False):
            stock = WarehouseStock.objects.select_for_update().get(id=item.stock_id)
            stock.quantity += item.quantity
            stock.status = 'idle'
            stock.save(update_fields=['quantity', 'status'])
