from decimal import Decimal

from rest_framework import status, views, serializers, pagination
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from django.db.models import Sum
from django.utils import dateparse, timezone

from api_v1.views.stock_change_views.mixins import StockChangeViewMixin
from business import models as business_models
from business import pre_order_services
from business import allocation_services
from stock import models as stock_models


class AllocationRecordSerializer(serializers.ModelSerializer):
    stock_ids = serializers.SerializerMethodField(read_only=True)
    stock_details = serializers.SerializerMethodField(read_only=True)
    scanned_by_name = serializers.CharField(source='scanned_by.name', read_only=True)

    class Meta:
        model = business_models.AllocationRecord
        fields = [
            'id',
            'pre_sales_order_item',
            'stock_ids',
            'stock_details',
            'quantity',
            'unit',
            'status',
            'scanned_by',
            'scanned_by_name',
            'scanned_at',
            'remarks',
            'created_at',
        ]
        read_only_fields = [
            'id',
            'pre_sales_order_item',
            'scanned_by',
            'scanned_by_name',
            'scanned_at',
            'created_at',
        ]

    def get_stock_ids(self, obj: business_models.AllocationRecord):
        return obj.stock_id_list

    def get_stock_details(self, obj: business_models.AllocationRecord):
        stock_ids = obj.stock_id_list
        if not stock_ids:
            return []
        details_by_id = stock_models.StockChangeDetail.objects.select_related(
            'product'
        ).in_bulk(stock_ids)
        return [
            StockChangeDetailShortcutSerializer(details_by_id[stock_id]).data
            for stock_id in stock_ids
            if stock_id in details_by_id
        ]


class StockChangeDetailShortcutSerializer(serializers.ModelSerializer):
    product_name = serializers.CharField(source='product.name', read_only=True)

    class Meta:
        model = stock_models.StockChangeDetail
        fields = [
            'product_id',
            'product_name',
            'quantity',
        ]
        read_only_fields = fields


class PreSalesOrderItemSerializer(serializers.ModelSerializer):
    allocated_quantity = serializers.SerializerMethodField(read_only=True)
    progress = serializers.SerializerMethodField(read_only=True)
    allocation_status = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = business_models.PreSalesOrderItem
        fields = [
            'id',
            'product_id',
            'product_name',
            'color',
            'quantity',
            'unit',
            'spec',
            'remarks',
            'allocated_quantity',
            'progress',
            'allocation_status',
        ]
        read_only_fields = ['id']

    def _get_allocated_quantity(self, obj: business_models.PreSalesOrderItem) -> Decimal:
        result = obj.allocation_records.filter(
            status=business_models.AllocationRecordStatusEnum.ACTIVE
        ).aggregate(total=Sum('quantity'))
        return result.get('total') or Decimal('0')

    def get_allocated_quantity(self, obj: business_models.PreSalesOrderItem) -> str:
        return str(self._get_allocated_quantity(obj))

    def get_progress(self, obj: business_models.PreSalesOrderItem) -> str:
        required = obj.quantity or Decimal('0')
        allocated = self._get_allocated_quantity(obj)
        if required <= 0:
            return '0'
        return str((allocated / required).quantize(Decimal('0.0001')))

    def get_allocation_status(self, obj: business_models.PreSalesOrderItem) -> int:
        required = obj.quantity or Decimal('0')
        allocated = self._get_allocated_quantity(obj)
        if allocated <= 0:
            return 1
        if allocated < required:
            return 2
        return 3


class PreSalesOrderItemDetailSerializer(PreSalesOrderItemSerializer):
    allocation_records = serializers.SerializerMethodField(read_only=True)

    class Meta(PreSalesOrderItemSerializer.Meta):
        fields = PreSalesOrderItemSerializer.Meta.fields + [
            'allocation_records',
        ]

    def get_allocation_records(self, obj: business_models.PreSalesOrderItem):
        """返回非已撤销的配货记录"""
        active_records = obj.allocation_records.exclude(
            status=business_models.AllocationRecordStatusEnum.CANCELLED
        )
        return AllocationRecordSerializer(active_records, many=True).data


class PreSalesOrderSerializer(serializers.ModelSerializer):
    human_id = serializers.CharField(read_only=True)
    customer_name = serializers.CharField(source='customer.name', read_only=True)
    warehouse_name = serializers.CharField(source='warehouse.name', read_only=True)
    operator_name = serializers.CharField(source='operator.name', read_only=True)
    items = PreSalesOrderItemSerializer(many=True, read_only=True)
    allocated_quantity = serializers.SerializerMethodField(read_only=True)
    progress = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = business_models.PreSalesOrder
        fields = [
            'id',
            'human_id',
            'merchant',
            'customer',
            'customer_name',
            'warehouse',
            'warehouse_name',
            'operator',
            'operator_name',
            'kind',
            'remarks',
            'created_at',
            'items',
            'allocated_quantity',
            'progress',
        ]
        read_only_fields = [
            'id',
            'human_id',
            'created_at',
            'customer_name',
            'warehouse_name',
            'operator_name',
            'items',
            'allocated_quantity',
            'progress',
        ]

    def get_allocated_quantity(self, obj: business_models.PreSalesOrder) -> str:
        """计算整单已配货总量"""
        total = Decimal('0')
        for item in obj.items.all():
            result = item.allocation_records.filter(
                status=business_models.AllocationRecordStatusEnum.ACTIVE
            ).aggregate(total=Sum('quantity'))
            total += result.get('total') or Decimal('0')
        return str(total)

    def get_progress(self, obj: business_models.PreSalesOrder) -> str:
        """计算整单配货进度（已配货总量/总需求量）"""
        required_total = Decimal('0')
        allocated_total = Decimal('0')
        for item in obj.items.all():
            required_total += item.quantity or Decimal('0')
            result = item.allocation_records.filter(
                status=business_models.AllocationRecordStatusEnum.ACTIVE
            ).aggregate(total=Sum('quantity'))
            allocated_total += result.get('total') or Decimal('0')
        if required_total <= 0:
            return '0'
        return str((allocated_total / required_total).quantize(Decimal('0.0001')))


class PreSalesOrderDetailSerializer(PreSalesOrderSerializer):
    items = PreSalesOrderItemDetailSerializer(many=True, read_only=True)


class PreSalesOrderPagination(pagination.LimitOffsetPagination):
    default_limit = 20
    max_limit = 100


class PreSalesOrderView(StockChangeViewMixin, views.APIView):
    """预销售单查询与创建"""

    permission_classes = [IsAuthenticated]
    pagination_class = PreSalesOrderPagination

    def get(self, request):
        if not self.check_employee_permission(request):
            return self.permission_error_response('无权限访问')

        merchant = request.user.employee.merchant
        queryset = pre_order_services.list_pre_sales_orders(merchant=merchant)

        customer_id = request.query_params.get('customer') or request.query_params.get('customer_id')
        warehouse_id = request.query_params.get('warehouse') or request.query_params.get('warehouse_id')
        kind = request.query_params.get('kind')
        human_id_contains = request.query_params.get('human_id__icontains')
        created_from = request.query_params.get('created_at_from')
        created_to = request.query_params.get('created_at_to')

        try:
            if customer_id:
                queryset = queryset.filter(customer_id=int(customer_id))
            if warehouse_id:
                queryset = queryset.filter(warehouse_id=int(warehouse_id))
            if kind:
                queryset = queryset.filter(kind=int(kind))
        except (TypeError, ValueError):
            return Response({'error': '筛选参数必须为整数'}, status=status.HTTP_400_BAD_REQUEST)

        if human_id_contains:
            queryset = queryset.filter(human_id__icontains=str(human_id_contains).strip())

        def _parse_datetime(value: str):
            dt = dateparse.parse_datetime(value)
            if dt is None:
                d = dateparse.parse_date(value)
                if d is None:
                    return None
                dt = timezone.datetime.combine(d, timezone.datetime.min.time())
            if timezone.is_naive(dt):
                dt = timezone.make_aware(dt, timezone.get_current_timezone())
            return dt

        if created_from:
            dt_from = _parse_datetime(created_from)
            if dt_from is None:
                return Response({'error': 'created_at_from 格式不正确'}, status=status.HTTP_400_BAD_REQUEST)
            queryset = queryset.filter(created_at__gte=dt_from)

        if created_to:
            dt_to = _parse_datetime(created_to)
            if dt_to is None:
                return Response({'error': 'created_at_to 格式不正确'}, status=status.HTTP_400_BAD_REQUEST)
            queryset = queryset.filter(created_at__lte=dt_to)

        paginator = self.pagination_class()
        page = paginator.paginate_queryset(queryset.order_by('-created_at'), request, view=self)
        serializer = PreSalesOrderSerializer(page, many=True)
        return paginator.get_paginated_response(serializer.data)

    def post(self, request):
        if not self.check_employee_permission(request):
            return self.permission_error_response('无权限访问')

        merchant = request.user.employee.merchant
        employee = request.user.employee
        data = request.data or {}

        try:
            pre_sales_order = pre_order_services.create_pre_sales_order(
                merchant=merchant,
                customer_id=data.get('customer') or data.get('customer_id'),
                warehouse_id=data.get('warehouse') or data.get('warehouse_id'),
                operator=employee,
                created_by=request.user,
                kind=data.get('kind'),
                items=data.get('items'),
                remarks=data.get('remarks', ''),
            )
        except ValueError as exc:
            return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)

        return Response(PreSalesOrderSerializer(pre_sales_order).data, status=status.HTTP_201_CREATED)


class PreSalesOrderDetailView(StockChangeViewMixin, views.APIView):
    permission_classes = [IsAuthenticated]

    def _get_order(self, request, pk: int):
        if not self.check_employee_permission(request):
            return None, self.permission_error_response('无权限访问')

        merchant = request.user.employee.merchant
        try:
            order = pre_order_services.get_pre_sales_order(
                merchant=merchant,
                pre_sales_order_id=pk,
            )
            return order, None
        except ValueError:
            return None, self.not_found_response('预销售单不存在')

    def get(self, request, pk: int):
        order, error_response = self._get_order(request, pk)
        if error_response:
            return error_response
        return Response(PreSalesOrderDetailSerializer(order).data, status=status.HTTP_200_OK)

    def put(self, request, pk: int):
        return self._update(request, pk)

    def patch(self, request, pk: int):
        return self._update(request, pk)

    def _update(self, request, pk: int):
        order, error_response = self._get_order(request, pk)
        if error_response:
            return error_response

        data = request.data or {}

        try:
            updated = pre_order_services.update_pre_sales_order(
                pre_sales_order=order,
                customer_id=data.get('customer') or data.get('customer_id'),
                warehouse_id=data.get('warehouse') or data.get('warehouse_id'),
                kind=data.get('kind'),
                items=data.get('items'),
                remarks=data.get('remarks'),
            )
        except ValueError as exc:
            return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)

        return Response(PreSalesOrderSerializer(updated).data, status=status.HTTP_200_OK)

    def delete(self, request, pk: int):
        order, error_response = self._get_order(request, pk)
        if error_response:
            return error_response

        pre_order_services.delete_pre_sales_order(pre_sales_order=order)
        return Response(status=status.HTTP_204_NO_CONTENT)


class PreSalesOrderConvertToSalesOrderView(StockChangeViewMixin, views.APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request, pk: int):
        if not self.check_employee_permission(request):
            return self.permission_error_response('无权限访问')

        merchant = request.user.employee.merchant
        employee = request.user.employee

        try:
            sales_order = pre_order_services.convert_pre_sales_order_to_sales_order(
                merchant=merchant,
                pre_sales_order_id=pk,
                operator=employee,
                created_by=request.user,
            )
        except PermissionError as exc:
            return Response({'error': str(exc)}, status=status.HTTP_403_FORBIDDEN)
        except business_models.PreSalesOrder.DoesNotExist:
            return self.not_found_response('预销售单不存在')
        except ValueError as exc:
            return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)

        return Response(
            {
                'id': sales_order.id,
                'human_id': sales_order.human_id,
                'status': sales_order.status,
                'message': '预销售单已转换为销售单，等待审批',
            },
            status=status.HTTP_201_CREATED,
        )


class PreSalesOrderItemAllocationView(StockChangeViewMixin, views.APIView):
    permission_classes = [IsAuthenticated]

    def _get_item(self, request, item_id: int):
        if not self.check_employee_permission(request):
            return None, self.permission_error_response('无权限访问')

        merchant = request.user.employee.merchant
        try:
            item = business_models.PreSalesOrderItem.objects.select_related(
                'pre_sales_order',
            ).get(id=item_id, pre_sales_order__merchant=merchant)
            return item, None
        except business_models.PreSalesOrderItem.DoesNotExist:
            return None, self.not_found_response('预销售单明细不存在')

    def post(self, request, item_id: int):
        item, error_response = self._get_item(request, item_id)
        if error_response:
            return error_response

        data = request.data or {}
        try:
            record = allocation_services.create_allocation_record(
                item=item,
                stock_ids=data.get('stock_ids') or [],
                quantity=data.get('quantity'),
                unit=data.get('unit'),
                scanned_by=request.user.employee,
                remarks=data.get('remarks'),
            )
        except ValueError as exc:
            return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)

        return Response(AllocationRecordSerializer(record).data, status=status.HTTP_201_CREATED)


class PreSalesOrderItemAllocationDetailView(StockChangeViewMixin, views.APIView):
    permission_classes = [IsAuthenticated]

    def delete(self, request, item_id: int, pk: int):
        if not self.check_employee_permission(request):
            return self.permission_error_response('无权限访问')

        merchant = request.user.employee.merchant
        try:
            record = business_models.AllocationRecord.objects.select_related(
                'pre_sales_order_item',
                'pre_sales_order_item__pre_sales_order',
            ).get(
                id=pk,
                pre_sales_order_item_id=item_id,
                pre_sales_order_item__pre_sales_order__merchant=merchant,
            )
        except business_models.AllocationRecord.DoesNotExist:
            return self.not_found_response('配货记录不存在')

        updated = allocation_services.cancel_allocation_record_with_unfreeze(
            record=record,
            cancelled_by=request.user,
        )
        return Response(AllocationRecordSerializer(updated).data, status=status.HTTP_200_OK)
