"""
Shipment API ViewSet
"""

from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from flower.viewsets import LimitedLimitOffsetPagination
from shipment.models import Shipment, ShipmentDelivery

from .serializers import (
    SalesItemDetailSerializer,
    SalesItemRebuildSerializer,
    SalesItemSerializer,
    SalesItemUpdateSerializer,
    ShipmentStatusUpdateSerializer,
    ShipmentDeliveryBindShipmentsSerializer,
    ShipmentDeliveryCreateSerializer,
    ShipmentDeliverySerializer,
    ShipmentDeliveryStatusUpdateSerializer,
    ShipmentDeliveryUpdateSerializer,
    ShipmentSerializer,
    ShipmentCreateNormalSerializer,
    ShipmentCreateExternalSerializer,
    ShipmentSalesItemCustomerSerializer,
    ShipmentUpdateSerializer,
)


def _build_sales_item_serializer_context(items):
    customer_ids = {item.customer_id for item in items if item.customer_id}
    printing_job_ids = {item.printing_job_id for item in items if item.printing_job_id}

    customer_name_map = {}
    if customer_ids:
        from basic_info.models import Customer

        customer_name_map = dict(
            Customer.objects.filter(id__in=customer_ids).values_list("id", "name")
        )

    printing_order_map = {}
    external_order_id_map = {}
    product_image_map = {}
    if printing_job_ids:
        from printing.models import PrintingJob

        printing_jobs = list(
            PrintingJob.objects.filter(id__in=printing_job_ids)
            .select_related("printing_order", "product")
        )
        printing_order_map = {
            job.id: job.printing_order_id for job in printing_jobs
        }
        external_order_id_map = {
            job.id: getattr(job.printing_order, "external_order_id", None)
            for job in printing_jobs
        }
        for job in printing_jobs:
            if not getattr(job, "product", None):
                product_image_map[job.id] = None
                continue
            primary_url = job.product.get_primary_image_url()
            if primary_url:
                product_image_map[job.id] = primary_url
            elif job.product.image:
                product_image_map[job.id] = job.product.image.url
            else:
                product_image_map[job.id] = None

    return {
        "customer_name_map": customer_name_map,
        "printing_order_map": printing_order_map,
        "external_order_id_map": external_order_id_map,
        "product_image_map": product_image_map,
    }


class ShipmentListCreateView(ListModelMixin, GenericAPIView):
    """
    出货单：查询列表 / 创建

    - GET /api/v1/shipment/shipments/
    - POST /api/v1/shipment/shipments/

    请求体:
    {
        "customer": 1,
        "shipment_date": "2026-01-14",
        "remark": "备注信息（可选）",
        "sales_items": [1, 2, 3]
    }

    返回:
    {
        "id": 1,
        "customer": 1,
        "customer_name": "客户A",
        "shipment_date": "2026-01-14",
        "remark": "备注信息",
        "items_count": 3,
        "created_by_id": 1,
        "created_by_name": "张三",
        "created_at": "2026-01-14T10:00:00Z",
        "updated_at": "2026-01-14T10:00:00Z"
    }
    """

    permission_classes = [IsAuthenticated]
    serializer_class = ShipmentSerializer
    pagination_class = LimitedLimitOffsetPagination

    def get_queryset(self):
        """
        仅返回当前用户所属商户的出货单（merchant 隔离）。

        支持过滤参数（可选）：
        - customer: 客户ID
        - status: 状态（1=草稿, 2=已发布, 3=已取消, 4=已驳回, 5=已审核）
        - delivery_id: 送货单ID（仅当传入具体ID时过滤；null/空值不触发过滤）
        - delivery_isnull: 是否仅查询未绑定/已绑定送货单的出货单（true/false）
        - external_id: 外部订单号（精确匹配）
        - shipment_date_from: 出货日期起始（YYYY-MM-DD）
        - shipment_date_to: 出货日期结束（YYYY-MM-DD，包含整天）
        """
        qs = (
            Shipment.objects.all()
            .select_related("merchant", "customer", "created_by", "cancelled_by")
            .prefetch_related(
                "items",
                "external_finished_products",
            )
        )

        user = self.request.user
        if not getattr(user, "is_superuser", False):
            emp = getattr(user, "employee", None)
            merchant = getattr(emp, "merchant", None) if emp else None
            if not merchant:
                return Shipment.objects.none()
            qs = qs.filter(merchant=merchant)

        # optional filters
        customer_id = self.request.query_params.get("customer")
        if customer_id:
            qs = qs.filter(customer_id=customer_id)

        status_val = self.request.query_params.get("status")
        if status_val:
            qs = qs.filter(status=status_val)

        delivery_id = self.request.query_params.get("delivery_id")
        if delivery_id is not None:
            normalized_delivery_id = delivery_id.strip()
            if normalized_delivery_id and normalized_delivery_id.lower() not in {"null", "none"}:
                qs = qs.filter(delivery_id=normalized_delivery_id)

        delivery_isnull = self.request.query_params.get("delivery_isnull")
        if delivery_isnull is not None:
            normalized_delivery_isnull = delivery_isnull.strip().lower()
            if normalized_delivery_isnull in {"1", "true", "yes"}:
                qs = qs.filter(delivery_id__isnull=True)
            elif normalized_delivery_isnull in {"0", "false", "no"}:
                qs = qs.filter(delivery_id__isnull=False)

        external_id = self.request.query_params.get("external_id")
        if external_id:
            qs = qs.filter(external_id=external_id)

        date_from = self.request.query_params.get("shipment_date_from")
        if date_from:
            qs = qs.filter(shipment_date__gte=date_from)

        date_to = self.request.query_params.get("shipment_date_to")
        if date_to:
            # 通过 <= 过滤日期
            qs = qs.filter(shipment_date__lte=date_to)

        only_address_null = self.request.query_params.get("only_address_null")
        if only_address_null is not None:
            normalized = only_address_null.strip().lower()
            if normalized in {"1", "true", "yes"}:
                qs = qs.filter(address="")
            elif normalized in {"0", "false", "no"}:
                qs = qs.exclude(address="")

        return qs.order_by("-created_at", "-id")

    def get(self, request):
        return self.list(request)

    def post(self, request):
        # 验证请求数据
        serializer = ShipmentCreateNormalSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        data = serializer.validated_data

        # 调用业务逻辑
        from shipment.services import create_shipment

        try:
            shipment = create_shipment(
                customer_id=data["customer"],
                shipment_date=data["shipment_date"],
                sales_item_ids=data["sales_items"],
                created_by=request.user,
                address=data.get("address", ""),
                contact_name=data.get("contact_name", ""),
                contact_phone=data.get("contact_phone", ""),
                remark=data.get("remark", ""),
                area=data.get("area", ""),
            )
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        # 返回创建的出货单
        response_serializer = ShipmentSerializer(shipment)
        return Response(response_serializer.data, status=status.HTTP_201_CREATED)


class ShipmentDetailView(RetrieveModelMixin, GenericAPIView):
    """
    出货单详情

    GET /api/v1/shipment/shipments/<id>/
    """

    permission_classes = [IsAuthenticated]
    serializer_class = ShipmentSerializer

    def get_queryset(self):
        qs = (
            Shipment.objects.all()
            .select_related("merchant", "customer", "created_by", "cancelled_by")
            .prefetch_related(
                "items",
                "external_finished_products",
            )
        )

        user = self.request.user
        if getattr(user, "is_superuser", False):
            return qs

        emp = getattr(user, "employee", None)
        merchant = getattr(emp, "merchant", None) if emp else None
        if not merchant:
            return Shipment.objects.none()
        qs = qs.filter(merchant=merchant)

        status_val = self.request.query_params.get("status")
        if status_val:
            qs = qs.filter(status=status_val)

        return qs

    def get(self, request, pk: int):
        return self.retrieve(request, pk=pk)

    def patch(self, request, pk: int):
        """
        更新出货单（部分更新）
        PATCH /api/v1/shipment/shipments/<id>/
        """
        shipment = self.get_object()
        serializer = ShipmentUpdateSerializer(data=request.data, partial=True)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        data = serializer.validated_data

        from shipment.services import update_shipment

        try:
            shipment = update_shipment(
                shipment,
                customer_id=data.get("customer"),
                shipment_date=data.get("shipment_date"),
                address=data.get("address"),
                contact_name=data.get("contact_name"),
                contact_phone=data.get("contact_phone"),
                area=data.get("area"),
                remark=data.get("remark"),
                external_id=data.get("external_id"),
            )
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        return Response(ShipmentSerializer(shipment).data, status=status.HTTP_200_OK)

    def put(self, request, pk: int):
        """
        更新出货单（全量更新，当前实现允许缺省字段，等同于 PATCH）
        PUT /api/v1/shipment/shipments/<id>/
        """
        return self.patch(request, pk=pk)


class ShipmentStatusUpdateView(APIView):
    """
    更新出货单状态

    PATCH /api/v1/shipment/shipments/<id>/status/
    PUT /api/v1/shipment/shipments/<id>/status/
    """

    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        qs = Shipment.objects.all().select_related(
            "merchant",
            "customer",
            "created_by",
            "cancelled_by",
            "approved_by",
        ).prefetch_related(
            "items",
            "external_finished_products",
        )

        user = self.request.user
        if getattr(user, "is_superuser", False):
            return qs

        emp = getattr(user, "employee", None)
        merchant = getattr(emp, "merchant", None) if emp else None
        if not merchant:
            return Shipment.objects.none()
        return qs.filter(merchant=merchant)

    def patch(self, request, pk: int):
        shipment = self.get_queryset().filter(id=pk).first()
        if shipment is None:
            return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

        serializer = ShipmentStatusUpdateSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        from shipment.models import ShipmentStatus
        from shipment.services import modify_status

        target_status = serializer.validated_data["status"]
        approved_by = request.user if target_status == ShipmentStatus.APPROVED else None

        try:
            shipment = modify_status(
                shipment,
                target_status=target_status,
                operator=request.user,
                approved_by=approved_by,
            )
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        return Response(ShipmentSerializer(shipment).data, status=status.HTTP_200_OK)

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


class ShipmentDeliveryListCreateView(ListModelMixin, GenericAPIView):
    """
    送货单：查询列表 / 创建

    - GET /api/v1/shipment/deliveries/
    - POST /api/v1/shipment/deliveries/
    """

    permission_classes = [IsAuthenticated]
    serializer_class = ShipmentDeliverySerializer
    pagination_class = LimitedLimitOffsetPagination

    def get_queryset(self):
        qs = ShipmentDelivery.objects.all().select_related(
            "merchant", "created_by", "operator", "cancelled_by"
        ).prefetch_related(
            "shipments",
            "shipments__customer",
        )

        user = self.request.user
        if not getattr(user, "is_superuser", False):
            emp = getattr(user, "employee", None)
            merchant = getattr(emp, "merchant", None) if emp else None
            if not merchant:
                return ShipmentDelivery.objects.none()
            qs = qs.filter(merchant=merchant)

        status_val = self.request.query_params.get("status")
        if status_val:
            qs = qs.filter(status=status_val)

        driver_name = (self.request.query_params.get("driver_name") or "").strip()
        if driver_name:
            qs = qs.filter(driver_name__icontains=driver_name)

        vehicle_trip = (self.request.query_params.get("vehicle_trip") or "").strip()
        if vehicle_trip:
            qs = qs.filter(vehicle_trip__icontains=vehicle_trip)

        return qs.order_by("-created_at", "-id")

    def get(self, request):
        return self.list(request)

    def post(self, request):
        serializer = ShipmentDeliveryCreateSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        from shipment.services import create_shipment_delivery

        try:
            delivery = create_shipment_delivery(
                driver_name=serializer.validated_data["driver_name"],
                vehicle_trip=serializer.validated_data["vehicle_trip"],
                contact_phone=serializer.validated_data.get("contact_phone", ""),
                vehicle_capacity=serializer.validated_data.get("vehicle_capacity", ""),
                shipment_ids=serializer.validated_data.get("shipments", []),
                shipment_order_ids=serializer.validated_data.get("shipment_order_ids", []),
                created_by=request.user,
                remark=serializer.validated_data.get("remark", ""),
                internal_remark=serializer.validated_data.get("internal_remark", ""),
            )
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        delivery = ShipmentDelivery.objects.select_related(
            "merchant", "created_by", "operator", "cancelled_by"
        ).prefetch_related(
            "shipments",
            "shipments__customer",
        ).get(id=delivery.id)

        return Response(
            ShipmentDeliverySerializer(delivery).data,
            status=status.HTTP_201_CREATED,
        )


class ShipmentDeliveryDetailView(RetrieveModelMixin, GenericAPIView):
    """
    送货单详情 / 修改 / 删除

    - GET /api/v1/shipment/deliveries/<id>/
    - PATCH /api/v1/shipment/deliveries/<id>/
    - PUT /api/v1/shipment/deliveries/<id>/
    - DELETE /api/v1/shipment/deliveries/<id>/
    """

    permission_classes = [IsAuthenticated]
    serializer_class = ShipmentDeliverySerializer

    def get_queryset(self):
        qs = ShipmentDelivery.objects.all().select_related(
            "merchant", "created_by", "operator", "cancelled_by"
        ).prefetch_related(
            "shipments",
            "shipments__customer",
        )

        user = self.request.user
        if getattr(user, "is_superuser", False):
            return qs

        emp = getattr(user, "employee", None)
        merchant = getattr(emp, "merchant", None) if emp else None
        if not merchant:
            return ShipmentDelivery.objects.none()
        return qs.filter(merchant=merchant)

    def get(self, request, pk: int):
        return self.retrieve(request, pk=pk)

    def patch(self, request, pk: int):
        delivery = self.get_object()
        serializer = ShipmentDeliveryUpdateSerializer(data=request.data, partial=True)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        from shipment.services import UNSET, update_shipment_delivery

        try:
            delivery = update_shipment_delivery(
                delivery,
                driver_name=serializer.validated_data.get("driver_name"),
                vehicle_trip=serializer.validated_data.get("vehicle_trip"),
                contact_phone=serializer.validated_data.get("contact_phone"),
                vehicle_capacity=serializer.validated_data.get("vehicle_capacity"),
                shipment_ids=serializer.validated_data.get("shipments"),
                shipment_order_ids=(
                    serializer.validated_data["shipment_order_ids"]
                    if "shipment_order_ids" in serializer.validated_data
                    else UNSET
                ),
                remark=serializer.validated_data.get("remark"),
                internal_remark=serializer.validated_data.get("internal_remark"),
                operator=request.user,
            )
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        delivery = self.get_queryset().get(id=delivery.id)

        return Response(
            ShipmentDeliverySerializer(delivery).data,
            status=status.HTTP_200_OK,
        )

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

    def delete(self, request, pk: int):
        delivery = self.get_object()

        from shipment.services import delete_shipment_delivery

        delete_shipment_delivery(delivery)
        return Response(status=status.HTTP_204_NO_CONTENT)


class ShipmentDeliveryStatusUpdateView(APIView):
    """
    修改送货单状态

    POST /api/v1/shipment/deliveries/<id>/status/
    """

    permission_classes = [IsAuthenticated]

    def post(self, request, pk: int):
        user = request.user
        queryset = ShipmentDelivery.objects.all()
        if not getattr(user, "is_superuser", False):
            emp = getattr(user, "employee", None)
            merchant = getattr(emp, "merchant", None) if emp else None
            if not merchant:
                return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)
            queryset = queryset.filter(merchant=merchant)

        delivery = queryset.filter(id=pk).first()
        if delivery is None:
            return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)

        serializer = ShipmentDeliveryStatusUpdateSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        from shipment.services import modify_shipment_delivery_status

        try:
            delivery = modify_shipment_delivery_status(
                delivery,
                target_status=serializer.validated_data["status"],
                operator=request.user,
            )
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        delivery = ShipmentDelivery.objects.select_related(
            "merchant", "created_by", "operator", "cancelled_by"
        ).prefetch_related(
            "shipments",
            "shipments__customer",
        ).get(id=delivery.id)

        return Response(
            ShipmentDeliverySerializer(delivery).data,
            status=status.HTTP_200_OK,
        )


class ShipmentDeliveryCancelView(APIView):
    """
    取消送货单

    POST /api/v1/shipment/deliveries/<id>/cancel/
    """

    permission_classes = [IsAuthenticated]

    def post(self, request, pk: int):
        if not request.user.has_perm("shipment.cancel_shipmentdelivery"):
            return Response(
                {"detail": "没有权限取消送货单"},
                status=status.HTTP_403_FORBIDDEN,
            )

        user = request.user
        queryset = ShipmentDelivery.objects.all()
        if not getattr(user, "is_superuser", False):
            emp = getattr(user, "employee", None)
            merchant = getattr(emp, "merchant", None) if emp else None
            if not merchant:
                return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)
            queryset = queryset.filter(merchant=merchant)

        delivery = queryset.filter(id=pk).first()
        if delivery is None:
            return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)

        from shipment.services import cancel_shipment_delivery

        delivery = cancel_shipment_delivery(
            delivery,
            cancelled_by=request.user,
            operator=request.user,
        )

        delivery = ShipmentDelivery.objects.select_related(
            "merchant", "created_by", "operator", "cancelled_by"
        ).prefetch_related(
            "shipments",
            "shipments__customer",
        ).get(id=delivery.id)

        return Response(
            ShipmentDeliverySerializer(delivery).data,
            status=status.HTTP_200_OK,
        )


class ShipmentDeliveryBindShipmentsView(APIView):
    """
    追加绑定出货单到现有送货单

    POST /api/v1/shipment/deliveries/<id>/bind-shipments/
    """

    permission_classes = [IsAuthenticated]

    def post(self, request, pk: int):
        user = request.user
        queryset = ShipmentDelivery.objects.all()
        if not getattr(user, "is_superuser", False):
            emp = getattr(user, "employee", None)
            merchant = getattr(emp, "merchant", None) if emp else None
            if not merchant:
                return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)
            queryset = queryset.filter(merchant=merchant)

        delivery = queryset.filter(id=pk).first()
        if delivery is None:
            return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)

        serializer = ShipmentDeliveryBindShipmentsSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        from shipment.services import bind_shipments_to_delivery

        try:
            delivery = bind_shipments_to_delivery(
                delivery,
                shipment_ids=serializer.validated_data["shipments"],
                operator=request.user,
            )
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        delivery = ShipmentDelivery.objects.select_related(
            "merchant", "created_by", "operator", "cancelled_by"
        ).prefetch_related(
            "shipments",
            "shipments__customer",
        ).get(id=delivery.id)

        return Response(
            ShipmentDeliverySerializer(delivery).data,
            status=status.HTTP_200_OK,
        )


class ShipmentExternalCreateView(APIView):
    """
    创建出货单（external 版）

    POST /api/v1/shipment/shipments/external/

    特点：
    - external_id 必填
    - external_finished_products 必填（数组）
    - 不绑定任何销售品
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        serializer = ShipmentCreateExternalSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        data = serializer.validated_data

        from shipment.services import create_external_shipment

        try:
            shipment = create_external_shipment(
                customer_id=data["customer"],
                shipment_date=data["shipment_date"],
                external_id=data["external_id"],
                external_finished_products=data["external_finished_products"],
                created_by=request.user,
                address=data.get("address", ""),
                contact_name=data.get("contact_name", ""),
                contact_phone=data.get("contact_phone", ""),
                remark=data.get("remark", ""),
                area=data.get("area", ""),
            )
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        response_serializer = ShipmentSerializer(shipment)
        return Response(response_serializer.data, status=status.HTTP_201_CREATED)


class ShipmentSalesItemCustomerListView(GenericAPIView):
    """
    从未出货销售品反推出“当前可出货客户”列表。

    GET /api/v1/shipment/sales-items/customers/
    """

    permission_classes = [IsAuthenticated]
    serializer_class = ShipmentSalesItemCustomerSerializer
    pagination_class = LimitedLimitOffsetPagination

    def get_queryset(self):
        user = self.request.user
        if getattr(user, "is_superuser", False):
            from basic_info.models import Customer, Merchant

            merchant_id = self.request.query_params.get("merchant")
            if not merchant_id:
                return Customer.objects.none()
            try:
                merchant = Merchant.objects.get(id=merchant_id)
            except Merchant.DoesNotExist:
                return Customer.objects.none()
        else:
            from basic_info.models import Customer

            emp = getattr(user, "employee", None)
            merchant = getattr(emp, "merchant", None) if emp else None
            if not merchant:
                return Customer.objects.none()

        from shipment.services import get_customers_with_unshipped_sales_items

        return get_customers_with_unshipped_sales_items(merchant=merchant)

    def get(self, request):
        queryset = self.get_queryset()
        page = self.paginate_queryset(queryset)
        serializer = self.get_serializer(page if page is not None else queryset, many=True)
        if page is not None:
            return self.get_paginated_response(serializer.data)
        return Response(serializer.data)


class SalesItemByPrintingOrderView(APIView):
    """
    通过生产订单查询销售品

    GET /api/v1/shipment/sales-items/by-printing-order/<printing_order_id>/

    返回与该 PrintingOrder 下所有 PrintingJob 关联的 SalesItem 列表。
    路径参数既支持内部 ID，也支持 external_order_id。

    查询参数:
    - include_already_has_shipment: 是否包含已关联出货单的销售品（true/false），默认 false

    返回:
    {
        "count": 5,
        "results": [
            {
                "id": 1,
                "name": "产品A",
                "quantity": "100.00",
                "unit": 1,
                "unit_display": "米",
                "position": "A1-01",
                "remark": "",
                "printing_job_id": 123,
                "customer_id": null,
                "shipment_id": null,
                "shipment_date": null,
                "created_at": "2026-01-14T10:00:00Z",
                "created_by_id": 1,
                "created_by_name": "张三"
            },
            ...
        ]
    }
    """

    permission_classes = [IsAuthenticated]

    def get(self, request, printing_order_id):
        from printing.models import PrintingOrder

        user = request.user
        if getattr(user, "is_superuser", False):
            base_queryset = PrintingOrder.objects.all()
        else:
            emp = getattr(user, "employee", None)
            merchant = getattr(emp, "merchant", None) if emp else None
            if not merchant:
                return Response(
                    {"detail": f"生产订单 {printing_order_id} 不存在"},
                    status=status.HTTP_404_NOT_FOUND,
                )
            base_queryset = PrintingOrder.objects.filter(merchant=merchant)

        printing_order = None
        if printing_order_id.isdigit():
            printing_order = base_queryset.filter(id=int(printing_order_id)).first()

        if printing_order is None:
            matched_orders = list(
                base_queryset.filter(external_order_id=printing_order_id).only("id")[:2]
            )
            if len(matched_orders) > 1:
                return Response(
                    {
                        "detail": (
                            f"external_order_id {printing_order_id} 匹配到多个生产订单，"
                            "请改用内部ID查询"
                        )
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )
            if len(matched_orders) == 1:
                printing_order = matched_orders[0]

        if printing_order is None:
            return Response(
                {"detail": f"生产订单 {printing_order_id} 不存在"},
                status=status.HTTP_404_NOT_FOUND,
            )

        # 获取查询参数
        include_already_has_shipment = (
            request.query_params.get("include_already_has_shipment", "false").lower()
            == "true"
        )

        # 调用 shipment 业务逻辑
        from shipment.services import get_sales_items_by_printing_order

        sales_items = get_sales_items_by_printing_order(
            printing_order_id=printing_order_id,
            include_already_has_shipment=include_already_has_shipment,
            merchant=printing_order.merchant,
        )

        # 序列化返回
        items = list(sales_items)
        serializer = SalesItemSerializer(
            items,
            many=True,
            context=_build_sales_item_serializer_context(items),
        )

        return Response({"count": len(serializer.data), "results": serializer.data})


class SalesItemByCustomerView(GenericAPIView):
    """
    通过客户查询销售品。

    GET /api/v1/shipment/sales-items/by-customer/<customer_id>/
    """

    permission_classes = [IsAuthenticated]
    serializer_class = SalesItemSerializer
    pagination_class = LimitedLimitOffsetPagination

    def get(self, request, customer_id: int):
        from basic_info.models import Customer

        user = request.user
        if getattr(user, "is_superuser", False):
            customer = Customer.objects.filter(id=customer_id).first()
        else:
            emp = getattr(user, "employee", None)
            merchant = getattr(emp, "merchant", None) if emp else None
            if not merchant:
                return Response(
                    {"detail": f"客户 {customer_id} 不存在"},
                    status=status.HTTP_404_NOT_FOUND,
                )
            customer = Customer.objects.filter(id=customer_id, merchant=merchant).first()

        if customer is None:
            return Response(
                {"detail": f"客户 {customer_id} 不存在"},
                status=status.HTTP_404_NOT_FOUND,
            )

        include_already_has_shipment = (
            request.query_params.get("include_already_has_shipment", "false").lower()
            == "true"
        )
        external_order_id = (request.query_params.get("external_order_id") or "").strip()

        from shipment.services import get_sales_items_by_customer

        queryset = get_sales_items_by_customer(
            merchant=customer.merchant,
            customer_id=customer.id,
            include_already_has_shipment=include_already_has_shipment,
            external_order_id=external_order_id or None,
        )
        page = self.paginate_queryset(queryset)
        items = list(page) if page is not None else list(queryset)
        serializer = self.get_serializer(
            items,
            many=True,
            context=_build_sales_item_serializer_context(items),
        )
        if page is not None:
            return self.get_paginated_response(serializer.data)
        return Response(serializer.data)


class SalesItemCreateView(APIView):
    """
    手动创建销售品

    POST /api/v1/shipment/sales-items/

    当自动转化销售品开关关闭时，通过此接口手动创建销售品。

    请求体:
    {
        "printing_job_id": 123,
        "name": "产品名称",
        "quantity": "100.50",
        "unit": 1,
        "customer_id": 456,
        "remark": "备注信息",
        "position": "A1-01"
    }

    参数说明:
    - printing_job_id: 生产任务ID（必填）
    - name: 销售品名称（必填）
    - quantity: 数量（必填，支持小数）
    - unit: 单位（必填）：1=米, 2=件, 3=码, 4=个
    - customer_id: 客户ID（可选，默认从生产订单获取）
    - remark: 备注（可选）
    - position: 货位（可选）

    返回:
    {
        "id": 1,
        "name": "产品名称",
        "quantity": "100.50",
        "unit": 1,
        "unit_display": "米",
        "position": "A1-01",
        "remark": "备注信息",
        "printing_job_id": 123,
        "customer_id": 456,
        "shipment_id": null,
        "shipment_date": null,
        "created_at": "2026-01-14T10:00:00Z",
        "created_by_id": 1,
        "created_by_name": "张三"
    }
    """

    permission_classes = [IsAuthenticated]

    def post(self, request):
        from .serializers import SalesItemCreateSerializer

        serializer = SalesItemCreateSerializer(
            data=request.data, context={"request": request}
        )

        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        try:
            sales_item = serializer.save()
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        # 返回创建的销售品
        response_serializer = SalesItemSerializer(
            sales_item,
            context=_build_sales_item_serializer_context([sales_item]),
        )
        return Response(response_serializer.data, status=status.HTTP_201_CREATED)


class SalesItemDetailView(GenericAPIView):
    """
    销售品详情。

    GET /api/v1/shipment/sales-items/<id>/
    PATCH /api/v1/shipment/sales-items/<id>/
    """

    permission_classes = [IsAuthenticated]
    serializer_class = SalesItemDetailSerializer

    def get_queryset(self):
        from shipment.models import SalesItem
        from shipment.services import get_active_sales_items_queryset

        qs = (
            get_active_sales_items_queryset()
            .select_related("shipment", "created_by", "delete_by")
            .order_by("id")
        )

        user = self.request.user
        if getattr(user, "is_superuser", False):
            return qs

        emp = getattr(user, "employee", None)
        merchant = getattr(emp, "merchant", None) if emp else None
        if not merchant:
            return qs.none()
        return qs.filter(merchant=merchant)

    def get(self, request, pk: int):
        sales_item = self.get_queryset().filter(id=pk).first()
        if sales_item is None:
            return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

        serializer = self.get_serializer(
            sales_item,
            context={
                **_build_sales_item_serializer_context([sales_item]),
                "request": request,
            },
        )
        return Response(serializer.data)

    def patch(self, request, pk: int):
        sales_item = self.get_queryset().filter(id=pk).first()
        if sales_item is None:
            return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

        allowed_fields = {"quantity", "remark", "position"}
        unexpected_fields = sorted(set(request.data.keys()) - allowed_fields)
        if unexpected_fields:
            return Response(
                {
                    "detail": (
                        "销售品仅允许修改以下字段: "
                        f"{', '.join(sorted(allowed_fields))}；"
                        f"不支持字段: {', '.join(unexpected_fields)}"
                    )
                },
                status=status.HTTP_400_BAD_REQUEST,
            )

        serializer = SalesItemUpdateSerializer(data=request.data, partial=True)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        from shipment.services import update_sales_item

        try:
            sales_item = update_sales_item(
                sales_item,
                quantity=serializer.validated_data.get("quantity"),
                remark=serializer.validated_data.get("remark"),
                position=serializer.validated_data.get("position"),
                operator=request.user,
            )
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        response_serializer = self.get_serializer(
            sales_item,
            context={
                **_build_sales_item_serializer_context([sales_item]),
                "request": request,
            },
        )
        return Response(response_serializer.data, status=status.HTTP_200_OK)

    def delete(self, request, pk: int):
        if not request.user.has_perm("shipment.soft_delete_salesitem"):
            return Response(
                {"detail": "没有权限删除销售品"},
                status=status.HTTP_403_FORBIDDEN,
            )

        sales_item = self.get_queryset().filter(id=pk).first()
        if sales_item is None:
            return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

        from shipment.services import delete_sales_item

        try:
            delete_sales_item(sales_item, deleted_by=request.user)
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        return Response({"detail": "销售品已标记为删除"}, status=status.HTTP_200_OK)


class SalesItemRebuildView(GenericAPIView):
    """
    销售品重建。

    POST /api/v1/shipment/sales-items/<id>/rebuild/
    """

    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        from shipment.services import get_active_sales_items_queryset

        qs = (
            get_active_sales_items_queryset()
            .select_related("shipment", "created_by")
            .order_by("id")
        )

        user = self.request.user
        if getattr(user, "is_superuser", False):
            return qs

        emp = getattr(user, "employee", None)
        merchant = getattr(emp, "merchant", None) if emp else None
        if not merchant:
            return qs.none()
        return qs.filter(merchant=merchant)

    def post(self, request, pk: int):
        sales_item = self.get_queryset().filter(id=pk).first()
        if sales_item is None:
            return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

        if sales_item.created_by_id != request.user.id:
            return Response(
                {"detail": "只有销售品创建者才能执行重建"},
                status=status.HTTP_403_FORBIDDEN,
            )

        serializer = SalesItemRebuildSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        from shipment.models import SalesItemRebuildRecord
        from shipment.services import rebuild_sales_item

        try:
            new_sales_item = rebuild_sales_item(
                sales_item,
                new_printing_job_id=serializer.validated_data["new_printing_job_id"],
                operator=request.user,
                quantity=serializer.validated_data.get("quantity"),
            )
        except ValueError as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        rebuild_record = SalesItemRebuildRecord.objects.filter(
            new_sales_item_id=new_sales_item.id
        ).order_by("-id").first()
        response_serializer = SalesItemDetailSerializer(
            new_sales_item,
            context={
                **_build_sales_item_serializer_context([new_sales_item]),
                "request": request,
            },
        )
        return Response(
            {
                "detail": "销售品已重建",
                "old_sales_item_id": sales_item.id,
                "new_sales_item_id": new_sales_item.id,
                "rebuild_record_id": rebuild_record.id if rebuild_record else None,
                "data": response_serializer.data,
            },
            status=status.HTTP_201_CREATED,
        )
