from rest_framework.exceptions import ParseError
from django.db import transaction

from apps.utils.viewsets import CustomModelViewSet
from apps.wf.mixins import TicketMixin
from apps.wf.models import Ticket
from apps.mpr.models import (
    PurchaseRequisition, PurchaseRequisitionItem,
    WarehouseEntry, WarehouseEntryItem, WarehouseStock,
    MaterialRequisition, MaterialRequisitionItem,
    WareHouse,
)
from apps.mpr.serializers import (
    WareHouseSerializer,
    PurchaseRequisitionListSerializer,
    PurchaseRequisitionDetailSerializer,
    PurchaseRequisitionCreateSerializer,
    PurchaseRequisitionItemSerializer,
    WarehouseEntryListSerializer,
    WarehouseEntryDetailSerializer,
    WarehouseEntryCreateSerializer,
    WarehouseEntryItemSerializer,
    WarehouseStockSerializer,
    MaterialRequisitionListSerializer,
    MaterialRequisitionDetailSerializer,
    MaterialRequisitionCreateSerializer,
    MaterialRequisitionItemSerializer,
)
from apps.mpr.filters import (
    PurchaseRequisitionFilter, WarehouseEntryFilter,
    WarehouseStockFilter, MaterialRequisitionFilter,
)


class WareHouseViewSet(CustomModelViewSet):
    """
    库房管理
    """
    queryset = WareHouse.objects.all()
    serializer_class = WareHouseSerializer
    search_fields = ['number', 'name', 'place']
    ordering = '-create_time'
    perms_map = {
        'get': '*', 'post': 'warehouse.create',
        'put': 'warehouse.update', 'delete': 'warehouse.delete',
    }


class PurchaseRequisitionViewSet(TicketMixin, CustomModelViewSet):
    """
    物资申购单
    """
    queryset = PurchaseRequisition.objects.all()
    serializer_class = PurchaseRequisitionListSerializer
    retrieve_serializer_class = PurchaseRequisitionDetailSerializer
    create_serializer_class = PurchaseRequisitionCreateSerializer
    update_serializer_class = PurchaseRequisitionCreateSerializer
    select_related_fields = ['create_by', 'belong_dept', 'ticket', 'ticket__state']
    search_fields = ['number', 'create_by__name']
    filterset_class = PurchaseRequisitionFilter
    ordering = '-create_time'
    workflow_key = 'wf_mpr'

    def gen_other_ticket_data(self, instance):
        dept_name = instance.belong_dept.name if instance.belong_dept else ''
        return {"dept_name": dept_name}


class PurchaseRequisitionItemViewSet(CustomModelViewSet):
    """
    物资申购明细
    """
    queryset = PurchaseRequisitionItem.objects.all()
    serializer_class = PurchaseRequisitionItemSerializer
    filterset_fields = ['requisition']
    ordering = 'create_time'


class WarehouseEntryViewSet(TicketMixin, CustomModelViewSet):
    """
    仓库入库单
    """
    queryset = WarehouseEntry.objects.all()
    serializer_class = WarehouseEntryListSerializer
    retrieve_serializer_class = WarehouseEntryDetailSerializer
    create_serializer_class = WarehouseEntryCreateSerializer
    update_serializer_class = WarehouseEntryCreateSerializer
    select_related_fields = ['create_by', 'belong_dept', 'warehouse', 'ticket', 'ticket__state']
    search_fields = ['number', 'create_by__name', 'warehouse__name']
    filterset_class = WarehouseEntryFilter
    ordering = '-create_time'
    workflow_key = 'wf_warehouse_entry'

    def gen_other_ticket_data(self, instance):
        return {"warehouse_name": instance.warehouse.name if instance.warehouse else ''}

    @staticmethod
    def approve_entry(ticket: Ticket, transition, new_ticket_data: dict):
        """审批通过后，将入库明细写入物料库存"""
        entry: WarehouseEntry = WarehouseEntry.objects.get(ticket=ticket)
        if WarehouseStock.objects.filter(entry=entry).exists():
            raise ParseError('该入库单已入库，不可重复操作')
        for item in entry.items.all():
            WarehouseStock.objects.create(
                warehouse=entry.warehouse,
                entry=entry,
                entry_number=entry.number,
                entry_date=entry.entry_date,
                entry_type=entry.entry_type,
                entry_method=entry.entry_method,
                name=item.name,
                spec=item.spec,
                unit=item.unit,
                quantity=item.quantity,
                unit_price=item.unit_price,
                amount=item.amount,
                supplier_name=item.supplier_name,
                invoice_received=item.invoice_received,
            )


class WarehouseEntryItemViewSet(CustomModelViewSet):
    """
    入库明细
    """
    queryset = WarehouseEntryItem.objects.all()
    serializer_class = WarehouseEntryItemSerializer
    filterset_fields = ['entry']
    ordering = 'create_time'


class WarehouseStockViewSet(CustomModelViewSet):
    """
    物料库存
    """
    queryset = WarehouseStock.objects.all()
    serializer_class = WarehouseStockSerializer
    select_related_fields = ['warehouse', 'entry']
    search_fields = ['name', 'spec', 'supplier_name', 'entry_number']
    filterset_class = WarehouseStockFilter
    ordering = '-create_time'
    perms_map = {'get': '*', 'post': 'warehouse_stock.create',
                 'put': 'warehouse_stock.update', 'delete': 'warehouse_stock.delete'}


class MaterialRequisitionViewSet(TicketMixin, CustomModelViewSet):
    """
    物资领用单
    """
    queryset = MaterialRequisition.objects.all()
    serializer_class = MaterialRequisitionListSerializer
    retrieve_serializer_class = MaterialRequisitionDetailSerializer
    create_serializer_class = MaterialRequisitionCreateSerializer
    update_serializer_class = MaterialRequisitionCreateSerializer
    select_related_fields = ['create_by', 'belong_dept', 'ticket', 'ticket__state']
    search_fields = ['number', 'create_by__name', 'collector']
    filterset_class = MaterialRequisitionFilter
    ordering = '-create_time'
    workflow_key = 'wf_material_requis'

    def gen_other_ticket_data(self, instance):
        dept_name = instance.belong_dept.name if instance.belong_dept else ''
        return {"dept_name": dept_name, "collector": instance.collector or ''}

    @staticmethod
    def approve_requisition(ticket: Ticket, transition, new_ticket_data: dict):
        """审批通过后，将库存物品状态改为已领用"""
        req = MaterialRequisition.objects.get(ticket=ticket)
        for item in req.items.filter(is_stock_item=True, stock__isnull=False):
            stock = WarehouseStock.objects.select_for_update().get(id=item.stock_id)
            stock.status = 'requisitioned'
            stock.save(update_fields=['status'])

    @staticmethod
    def reject_requisition(ticket: Ticket, transition, new_ticket_data: dict):
        """审批拒绝后，恢复库存数量和状态"""
        from apps.mpr.serializers import MaterialRequisitionCreateSerializer
        req = MaterialRequisition.objects.get(ticket=ticket)
        MaterialRequisitionCreateSerializer._restore_stock(req)


class MaterialRequisitionItemViewSet(CustomModelViewSet):
    """
    物资领用明细
    """
    queryset = MaterialRequisitionItem.objects.all()
    serializer_class = MaterialRequisitionItemSerializer
    filterset_fields = ['requisition']
    ordering = 'create_time'
