"""
Printing API 序列化器
"""
import json
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import serializers
from rest_framework.fields import empty

from api_v1.models import UploadedFile
from api_v1.utils.media import build_public_media_url
from api_v1.views.shipment.serializers import SalesItemSerializer
from printing import models
from .services import PrintingOrderService, PrintingJobService
from basic_info.models import Customer, Employee


def _include_sales_order_bound(context) -> bool:
    request = context.get('request') if context else None
    if not request:
        return True
    raw_value = request.query_params.get('include_sales_order_bound')
    return str(raw_value).lower() not in {'0', 'false', 'no'}


def _get_is_sales_order_bound(obj) -> bool:
    annotated_value = getattr(obj, 'annotated_is_sales_order_bound', None)
    if annotated_value is not None:
        return bool(annotated_value)
    return bool(obj.is_sales_order_bound)


def _build_absolute_media_url(url: str | None, request):
    return build_public_media_url(url, request=request)


def _serialize_plate_images(raw_value, request):
    if not raw_value:
        return []
    serialized = []
    if isinstance(raw_value, list):
        iterable = raw_value
    elif isinstance(raw_value, dict):
        iterable = [raw_value]
    elif isinstance(raw_value, str):
        iterable = [{'url': raw_value}]
    else:
        iterable = []

    for entry in iterable:
        if isinstance(entry, str):
            data = {'url': entry}
        elif isinstance(entry, dict):
            data = dict(entry)
        else:
            continue
        url = data.get('url') or data.get('path')
        data['url'] = _build_absolute_media_url(url, request)
        if 'path' not in data and isinstance(url, str):
            data['path'] = url
        serialized.append(data)
    return serialized


def _build_plate_image_payload(items, request_user):
    if items is None:
        return None
    if not items:
        return []
    file_ids = [item['file_id'] for item in items if item.get('file_id') is not None]
    if not file_ids:
        return []

    queryset = UploadedFile.objects.filter(id__in=file_ids, is_deleted=False)

    files_map = {file.id: file for file in queryset}
    missing = [str(fid) for fid in file_ids if fid not in files_map]
    if missing:
        raise serializers.ValidationError({'plate_image': f'以下文件不存在或已删除: {", ".join(missing)}'})

    payload = []
    for item in items:
        file = files_map[item['file_id']]
        name = item.get('name') or file.original_filename or file.path.name
        payload.append({
            'file_id': file.id,
            'name': name,
            'path': file.path.name,
            'url': file.file_url,
            'size': file.file_size,
            'content_type': file.content_type,
            'uploaded_at': file.created_at.isoformat(),
        })
    return payload


class PlateImageInputListSerializer(serializers.ListSerializer):
    """
    兼容 plate_image 的多种入参形态（尤其是 multipart/form-data 场景）：
    - JSON：直接传 array
    - multipart：常见会把数组作为 JSON 字符串传入（例如 '[{"file_id": 1}]'）
    """

    def to_internal_value(self, data):
        # allow_null=True 时，None 会先到这里
        if data is None:
            return []

        # multipart/form-data 下，前端常把数组序列化成字符串
        if isinstance(data, str):
            raw = data.strip()
            if not raw:
                return []
            try:
                data = json.loads(raw)
            except json.JSONDecodeError:
                raise serializers.ValidationError('plate_image 必须是 JSON 数组或可解析为数组的 JSON 字符串')

        # 兼容单个对象
        if isinstance(data, dict):
            data = [data]

        return super().to_internal_value(data)

    def get_value(self, dictionary):
        """
        DRF 在 multipart/form-data 下会优先用“HTML list”解析（期望 plate_image[0][file_id] 这类键）。
        但前端常见做法是直接传一个字段 plate_image='[{"file_id":1}]'（JSON 字符串）。
        这里做一次兜底：若 HTML list 未解析到值，则回退读取原始键值。
        """
        value = super().get_value(dictionary)
        if value is empty and hasattr(dictionary, 'get'):
            raw = dictionary.get(self.field_name, empty)
            if raw is not empty:
                return raw
        return value


class PlateImageInputSerializer(serializers.Serializer):
    file_id = serializers.IntegerField(min_value=1, help_text='上传文件的 ID')
    name = serializers.CharField(
        required=False,
        allow_blank=True,
        allow_null=True,
        help_text='可选的图片名称，默认使用文件原始名称'
    )

    class Meta:
        list_serializer_class = PlateImageInputListSerializer


class PrintingOrderListSerializer(serializers.ModelSerializer):
    """印染订单列表序列化器"""
    customer_name = serializers.SerializerMethodField()
    customer_phone = serializers.SerializerMethodField()
    process_name = serializers.CharField(source='process.name', read_only=True)
    progress = serializers.IntegerField(read_only=True)
    jobs_status_summary = serializers.SerializerMethodField()
    jobs_last_status_summary = serializers.SerializerMethodField()
    created_by = serializers.IntegerField(source='created_by_id', read_only=True)
    created_by_name = serializers.SerializerMethodField()
    merchant_id = serializers.IntegerField(source='merchant.id', read_only=True, allow_null=True)
    external_order_id = serializers.CharField(read_only=True)
    external_customer_id = serializers.CharField(read_only=True)
    external_customer_name = serializers.CharField(read_only=True)
    external_employee_name = serializers.CharField(read_only=True)
    
    class Meta:
        model = models.PrintingOrder
        fields = [
            'id', 'human_id', 'merchant_id', 'customer', 'customer_name', 'customer_phone',
            'fabric', 'width', 'is_urgent', 'area', 'address', 'curve',
            'is_fabric_received', 'outgoing_date', 'is_invalid', 'new_curve',
            'process', 'process_name', 'progress', 'position', 'print_count',
            'jobs_status_summary', 'jobs_last_status_summary',
            'external_order_id', 'external_customer_id', 'external_customer_name', 'external_employee_name',
            'created_by', 'created_by_name',
            'created_at', 'updated_at',
        ]
        read_only_fields = ['id', 'human_id', 'created_at', 'updated_at', 'progress', 'print_count', 'merchant_id']

    def get_customer_name(self, obj):
        if hasattr(obj, 'customer_name'):
            return obj.customer_name
        try:
            customer = getattr(obj, 'customer', None)
        except ObjectDoesNotExist:
            return None
        return getattr(customer, 'name', None)

    def get_customer_phone(self, obj):
        if hasattr(obj, 'customer_phone'):
            return obj.customer_phone
        try:
            customer = getattr(obj, 'customer', None)
        except ObjectDoesNotExist:
            return None
        return getattr(customer, 'mobile', None)

    def get_jobs_status_summary(self, obj):
        """
        返回订单下所有 PrintingJob 按当前状态分组的数量汇总
        
        返回格式: [{"state_name": "进度一", "state_id": 1, "count": 3}, ...]
        - state_name: 状态名称（已完成的返回"已完成"）
        - state_id: 状态ID（已完成的返回 None）
        - count: 该状态下的 job 数量
        """
        # 使用预取的 printing_jobs（避免 N+1）
        jobs = getattr(obj, '_prefetched_objects_cache', {}).get('printing_jobs') or obj.printing_jobs.all()
        
        from collections import Counter
        status_counter = Counter()
        
        for job in jobs:
            # 获取 job 的当前状态显示名称
            status_name = job.status  # 调用 @property，返回状态名或"已完成"
            status_id = job.status_id  # 状态ID，已完成时为 None
            status_counter[(status_name, status_id)] += 1
        
        # 转换为列表格式
        return [
            {'state_name': name, 'state_id': sid, 'count': count}
            for (name, sid), count in status_counter.items()
        ]

    def get_jobs_last_status_summary(self, obj):
        """
        返回订单下所有 PrintingJob 按最后完成状态分组的数量汇总
        
        返回格式: [{"state_name": "进度一", "count": 3}, ...]
        - state_name: 最后完成的状态名称
        - count: 该状态下的 job 数量
        
        注意: 未开始的 job（没有完成任何状态）不计入统计
        """
        # 使用预取的 printing_jobs（避免 N+1）
        jobs = getattr(obj, '_prefetched_objects_cache', {}).get('printing_jobs') or obj.printing_jobs.all()
        
        from collections import Counter
        status_counter = Counter()
        
        for job in jobs:
            # 获取 job 的最后完成状态名称
            last_completed = job.last_completed_state  # 调用 @property，未开始时返回空字符串
            # 只统计有已完成状态的 job
            if last_completed:
                status_counter[last_completed] += 1
        
        # 转换为列表格式
        return [
            {'state_name': name, 'count': count}
            for name, count in status_counter.items()
        ]

    def get_created_by_name(self, obj):
        """获取创建人名称（员工姓名）"""
        user = getattr(obj, 'created_by', None)
        if not user:
            return None
        emp = getattr(user, 'employee', None)
        return getattr(emp, 'name', None)


class PrintingOrderDetailSerializer(serializers.ModelSerializer):
    """印染订单详情序列化器"""
    customer_name = serializers.CharField(source='customer.name', read_only=True)
    customer_phone = serializers.CharField(source='customer.mobile', read_only=True)
    customer_area = serializers.CharField(source='customer.area', read_only=True)
    process_name = serializers.CharField(source='process.name', read_only=True)
    created_by_name = serializers.SerializerMethodField()
    progress = serializers.IntegerField(read_only=True)
    merchant_id = serializers.IntegerField(source='merchant.id', read_only=True, allow_null=True)
    external_order_id = serializers.CharField(read_only=True)
    external_customer_id = serializers.CharField(read_only=True)
    external_customer_name = serializers.CharField(read_only=True)
    external_employee_name = serializers.CharField(read_only=True)
    
    class Meta:
        model = models.PrintingOrder
        fields = [
            'id', 'human_id', 'merchant_id', 'customer', 'customer_name', 'customer_phone', 'customer_area',
            'fabric', 'width', 'is_urgent', 'area', 'address', 'fabric_source',
            'is_fabric_received', 'craft', 'description', 'outgoing_date',
            'curve', 'new_curve', 'position', 'created_by_name',
            'printing_warn', 'rolling_warn', 'production_warn',
            'external_order_id', 'external_customer_id', 'external_customer_name', 'external_employee_name',
            'is_invalid', 'process', 'process_name', 'progress', 'print_count',
            'created_at', 'updated_at'
        ]
        read_only_fields = ['id', 'human_id', 'created_at', 'updated_at', 'progress', 'print_count', 'merchant_id']

    def get_created_by_name(self, obj):
        """获取创建人名称（员工姓名）"""
        user = getattr(obj, 'created_by', None)
        if not user:
            return None
        emp = getattr(user, 'employee', None)
        return getattr(emp, 'name', None)


class PrintingOrderCreateUpdateSerializer(serializers.ModelSerializer):
    """印染订单创建/更新序列化器"""

    # 兼容前端只传日期（YYYY-MM-DD）：会解析为 00:00:00
    # 同时也接受 ISO8601 datetime（含时区）。
    outgoing_date = serializers.DateTimeField(
        required=False,
        allow_null=True,
        input_formats=[
            "iso-8601",
            "%Y-%m-%d",
            "%Y-%m-%d %H:%M:%S",
            "%Y-%m-%d %H:%M",
        ],
        help_text="出货日期时间（可仅传 YYYY-MM-DD，将自动视为 00:00:00）",
    )
    
    class Meta:
        model = models.PrintingOrder
        fields = [
            'id', 'customer', 'fabric', 'width', 'is_urgent', 'area', 'address',
            'fabric_source', 'is_fabric_received', 'craft', 'description',
            'outgoing_date', 'curve', 'new_curve', 'position',
            'printing_warn', 'rolling_warn', 'production_warn', 'is_invalid',
            'process', 'external_order_id',
        ]
        read_only_fields = ['id']
    
    def validate_customer(self, value):
        """验证客户是否存在"""
        if not value:
            raise serializers.ValidationError("客户不能为空")
        return value
    
    def validate(self, attrs):
        """验证流程修改权限"""
        # 更新时验证 process 修改权限
        if self.instance and 'process' in attrs:
            new_process = attrs['process']
            old_process = self.instance.process
            
            if new_process != old_process:
                if not PrintingOrderService.can_change_process(self.instance):
                    raise serializers.ValidationError({
                        'process': '存在已开始的印染任务，无法修改流程'
                    })
        
        return attrs
    
    def create(self, validated_data):
        """创建订单，使用 service 层"""
        user = self.context['request'].user
        return PrintingOrderService.create_printing_order(validated_data, user)
    
    def update(self, instance, validated_data):
        """更新订单，使用 service 层"""
        user = self.context['request'].user
        success, message, updated_instance = PrintingOrderService.update_printing_order(
            instance, validated_data, user
        )
        if not success:
            raise serializers.ValidationError(message)
        return updated_instance


class PrintingJobListSerializer(serializers.ModelSerializer):
    """印染款式明细列表序列化器"""

    printing_order_id = serializers.CharField(source='printing_order.human_id', read_only=True)
    external_order_id = serializers.CharField(source='printing_order.external_order_id', read_only=True)
    customer_name = serializers.CharField(source='printing_order.customer.name', read_only=True, allow_null=True)
    product_name = serializers.CharField(source='product.name', read_only=True)
    product_image_url = serializers.SerializerMethodField()
    status = serializers.CharField(read_only=True)
    work_state_display = serializers.SerializerMethodField()
    has_started = serializers.BooleanField(read_only=True)
    is_completed = serializers.BooleanField(read_only=True)
    is_production_completed = serializers.BooleanField(read_only=True)
    progress_percentage = serializers.FloatField(read_only=True)
    last_completed_state = serializers.CharField(read_only=True)
    business_object_id = serializers.SerializerMethodField()
    batch_advance_records = serializers.SerializerMethodField()
    saleitems = serializers.SerializerMethodField()
    is_sales_order_bound = serializers.SerializerMethodField()
    billed_quantity = serializers.DecimalField(max_digits=18, decimal_places=2, read_only=True)
    merchant_id = serializers.IntegerField(source='merchant.id', read_only=True, allow_null=True)
    fabric = serializers.SerializerMethodField()
    curve = serializers.CharField(source='printing_order.curve', read_only=True, allow_null=True)
    new_curve = serializers.CharField(source='printing_order.new_curve', read_only=True, allow_null=True)
    
    class Meta:
        model = models.PrintingJob
        fields = [
            'id', 'original_id', 'sub_id', 'merchant_id', 'printing_order', 'printing_order_id', 'external_order_id',
            'customer_name', 'product', 'product_name',
            'product_image_url', 'has_started',
            'quantity', 'billed_quantity', 'unit', 'size', 'pieces', 'description',
            'work_state', 'work_state_display',
            'status', 'is_completed', 'is_production_completed', 'progress_percentage', 'last_completed_state',
            'business_object_id',
            'is_sales_order_bound',
            'batch_advance_records',
            'saleitems',
            'fabric',
            'curve',
            'new_curve',
            'created_at', 'updated_at'
        ]
        read_only_fields = [
            'id', 'created_at', 'updated_at',
            'status', 'is_completed', 'is_production_completed', 'progress_percentage', 'last_completed_state',
            'business_object_id', 'is_sales_order_bound', 'billed_quantity', 'merchant_id'
        ]

    def to_representation(self, instance):
        data = super().to_representation(instance)
        if not _include_sales_order_bound(self.context):
            data.pop('is_sales_order_bound', None)
        return data
    
    def get_business_object_id(self, obj):
        """安全地获取 business_object_id"""
        return obj.business_object.id if obj.business_object else None

    def get_product_image_url(self, obj):
        """
        获取产品图片 URL
        
        优先级：
        1. mdy_image_url（明道云图片）
        2. description JSON 中的图片 URL
        3. image 字段（本地上传的图片）
        """
        if not obj.product:
            return None
        
        # 先尝试 get_primary_image_url（mdy_image_url 或 description JSON）
        primary_url = obj.product.get_primary_image_url()
        if primary_url:
            return primary_url
        
        # fallback 到本地上传的 image 字段
        if obj.product.image:
            request = self.context.get('request')
            if request:
                return request.build_absolute_uri(obj.product.image.url)
            return obj.product.image.url
        
        return None

    def get_work_state_display(self, obj):
        return obj.get_work_state_display()

    def get_batch_advance_records(self, obj):
        """
        批量推进记录（稳定输出 key）
        
        - 如果没有批量记录：返回 []
        - 仅返回与该 job 相关的批量推进审计记录（按 created_at 倒序）
        """
        # 注意：这里不要链式调用 select_related/order_by，否则会绕开 viewset 的 prefetch 缓存，导致 N+1 查询。
        records = list(obj.batch_advance_records.all())
        return PrintingJobBatchAdvanceRecordSerializer(records, many=True).data

    def get_saleitems(self, obj):
        items = getattr(obj, '_saleitems_cache', None)
        if items is None:
            from shipment.services import get_active_sales_items_queryset
            items = list(
                get_active_sales_items_queryset().select_related('shipment', 'created_by')
                .filter(printing_job_id=obj.id)
                .order_by('id')
            )
        return SalesItemSerializer(items, many=True).data

    def get_is_sales_order_bound(self, obj):
        return _get_is_sales_order_bound(obj)

    def get_fabric(self, obj):
        return obj.printing_order.fabric


class PrintingJobDetailSerializer(serializers.ModelSerializer):
    """印染款式明细详情序列化器"""
    printing_order_id = serializers.CharField(source='printing_order.human_id', read_only=True)
    external_order_id = serializers.CharField(source='printing_order.external_order_id', read_only=True)
    product_name = serializers.CharField(source='product.name', read_only=True)
    product_code = serializers.CharField(source='product.human_id', read_only=True)
    status = serializers.CharField(read_only=True)
    status_id = serializers.IntegerField(read_only=True)
    work_state_display = serializers.SerializerMethodField()
    is_completed = serializers.BooleanField(read_only=True)
    is_production_completed = serializers.BooleanField(read_only=True)
    has_started = serializers.BooleanField(read_only=True)
    progress_percentage = serializers.FloatField(read_only=True)
    last_completed_state = serializers.CharField(read_only=True)
    business_object_id = serializers.SerializerMethodField()
    batch_advance_records = serializers.SerializerMethodField()
    saleitems = serializers.SerializerMethodField()
    is_sales_order_bound = serializers.SerializerMethodField()
    billed_quantity = serializers.DecimalField(max_digits=18, decimal_places=2, read_only=True)
    merchant_id = serializers.IntegerField(source='merchant.id', read_only=True, allow_null=True)
    fabric = serializers.SerializerMethodField()
    curve = serializers.CharField(source='printing_order.curve', read_only=True, allow_null=True)
    new_curve = serializers.CharField(source='printing_order.new_curve', read_only=True, allow_null=True)
    
    class Meta:
        model = models.PrintingJob
        fields = [
            'id', 'original_id', 'sub_id', 'merchant_id', 'printing_order', 'printing_order_id', 'external_order_id', 'product', 'product_name', 'product_code',
            'quantity', 'billed_quantity', 'unit', 'size', 'pieces', 'description',
            'work_state', 'work_state_display',
            'status', 'status_id', 'is_completed', 'is_production_completed', 'has_started',
            'progress_percentage', 'last_completed_state',
            'business_object_id',
            'is_sales_order_bound',
            'batch_advance_records',
            'saleitems',
            'fabric',
            'curve',
            'new_curve',
            'created_at', 'updated_at'
        ]
        read_only_fields = [
            'id', 'created_at', 'updated_at', 
            'status', 'status_id', 'is_completed', 'is_production_completed', 'has_started',
            'progress_percentage', 'last_completed_state',
            'business_object_id', 'is_sales_order_bound', 'billed_quantity', 'merchant_id'
        ]

    def to_representation(self, instance):
        data = super().to_representation(instance)
        if not _include_sales_order_bound(self.context):
            data.pop('is_sales_order_bound', None)
        return data
    
    def get_business_object_id(self, obj):
        """安全地获取 business_object_id"""
        return obj.business_object.id if obj.business_object else None

    def get_work_state_display(self, obj):
        return obj.get_work_state_display()

    def get_batch_advance_records(self, obj):
        """见 list serializer，同样稳定输出 key"""
        records = list(obj.batch_advance_records.all())
        return PrintingJobBatchAdvanceRecordSerializer(records, many=True).data

    def get_saleitems(self, obj):
        items = getattr(obj, '_saleitems_cache', None)
        if items is None:
            from shipment.services import get_active_sales_items_queryset
            items = list(
                get_active_sales_items_queryset().select_related('shipment', 'created_by')
                .filter(printing_job_id=obj.id)
                .order_by('id')
            )
        return SalesItemSerializer(items, many=True).data

    def get_is_sales_order_bound(self, obj):
        return _get_is_sales_order_bound(obj)

    def get_fabric(self, obj):
        return obj.printing_order.fabric


class PrintingJobCreateUpdateSerializer(serializers.ModelSerializer):
    """印染款式明细创建/更新序列化器"""
    external_order_id = serializers.CharField(source='printing_order.external_order_id', read_only=True)
    batch_advance_records = serializers.SerializerMethodField(read_only=True)
    
    class Meta:
        model = models.PrintingJob
        fields = [
            'id', 'original_id', 'sub_id', 'printing_order', 'external_order_id', 'product', 'quantity', 'unit', 'size', 'pieces', 'description',
            'work_state',
            'batch_advance_records',
        ]
        read_only_fields = ['id', 'external_order_id']
        extra_kwargs = {
            'size': {'required': False, 'allow_null': True, 'allow_blank': True},
            'pieces': {'required': False, 'allow_null': True},
            'description': {'required': False, 'allow_null': True, 'allow_blank': True},
            'work_state': {'required': False},
        }
    
    def validate_printing_order(self, value):
        """验证印染订单是否存在"""
        if not value:
            raise serializers.ValidationError("印染订单不能为空")
        return value
    
    def validate_product(self, value):
        """验证产品是否存在"""
        if not value:
            raise serializers.ValidationError("产品不能为空")
        return value
    
    def validate_quantity(self, value):
        """验证数量"""
        if value <= 0:
            raise serializers.ValidationError("数量必须大于0")
        return value
    
    def create(self, validated_data):
        """创建任务，使用 service 层"""
        user = self.context['request'].user
        return PrintingJobService.create_printing_job(validated_data, user)
    
    def update(self, instance, validated_data):
        """更新任务，使用 service 层"""
        # 不允许修改 printing_order 绑定关系（避免业务对象流程不一致）
        if 'printing_order' in validated_data:
            new_order = validated_data.get('printing_order')
            if new_order and new_order.id != getattr(instance, 'printing_order_id', None):
                raise serializers.ValidationError({'printing_order': '不允许修改印染订单绑定关系'})

        user = self.context['request'].user
        success, message, updated_instance = PrintingJobService.update_printing_job(
            instance, validated_data, user
        )
        if not success:
            raise serializers.ValidationError(message)
        return updated_instance

    def get_batch_advance_records(self, obj):
        """创建/更新接口也稳定输出该 key（通常为空）"""
        records = list(obj.batch_advance_records.all())
        return PrintingJobBatchAdvanceRecordSerializer(records, many=True).data


class PrintingJobBatchAdvanceRecordSerializer(serializers.ModelSerializer):
    """印染任务批量操作记录（用于嵌入 PrintingJob 的序列化结果）"""

    state_id = serializers.IntegerField(source='state.id', read_only=True)
    state_name = serializers.CharField(source='state.name', read_only=True)
    created_by_username = serializers.CharField(source='created_by.username', read_only=True)
    created_by_name = serializers.SerializerMethodField()

    class Meta:
        model = models.PrintingJobBatchAdvanceRecord
        fields = [
            'id',
            'printing_order',
            'state',
            'state_id',
            'state_name',
            'created_by',
            'created_by_username',
            'created_by_name',
            'parameters',
            'only_parameters',
            'created_at',
        ]
        read_only_fields = fields

    def get_created_by_name(self, obj):
        user = getattr(obj, 'created_by', None)
        if not user:
            return None
        emp = getattr(user, 'employee', None)
        return getattr(emp, 'name', None)


class PlateOrderDesignCodeMixin:
    """确保 design_code 为空时使用主键"""

    @staticmethod
    def _normalize_design_code(design_code: str | None, instance_id: int | None) -> str | None:
        if design_code:
            return design_code
        if instance_id:
            return str(instance_id)
        return design_code

    def to_representation(self, instance):
        data = super().to_representation(instance)
        data['design_code'] = self._normalize_design_code(data.get('design_code'), instance.id)
        data['plate_image'] = _serialize_plate_images(getattr(instance, 'plate_image', None), self.context.get('request'))
        return data


class PlateOrderKanbanListSerializer(serializers.ModelSerializer):
    """开版订单看板列表序列化器（精简版，仅包含看板实际使用的字段，消除 N+1 查询）"""
    customer_name = serializers.CharField(source="customer.name", read_only=True)
    salesperson_name = serializers.CharField(source="salesperson.name", read_only=True)
    merchandiser_name = serializers.CharField(source="merchandiser.name", read_only=True)
    created_by_name = serializers.CharField(source="created_by.employee.name", read_only=True, default=None)
    process_name = serializers.CharField(source="annotated_process_name", read_only=True, default=None)
    plate_image = serializers.SerializerMethodField()
    plate_image_url = serializers.SerializerMethodField()
    last_completed_state = serializers.CharField(read_only=True)

    class Meta:
        model = models.PlateOrder
        fields = [
            'id', 'design_code', 'customer_name', 'area',
            'salesperson_name', 'merchandiser_name', 'fabric_source', 'fabric',
            'width', 'plate_type', 'plate_date', 'plate_method',
            'plate_image', 'plate_image_url', 'plate_notes', 'reprint_reason',
            'urgency_level', 'production_method', 'is_mark_frame',
            'drawing_rating', 'color_matching_rating', 'sample_rating',
            'difficulty_rating', 'style_name', 'required_sample_meters',
            'required_completion_date', 'completion_date', 'approval_result',
            'customer_feedback', 'last_completed_state', 'business_object_id',
            'original_id', 'process_name', 'print_count', 'created_at',
            'created_by_name',
        ]

    def _get_cached_plate_images(self, obj):
        cache_attr = '_cached_plate_images'
        images = getattr(obj, cache_attr, None)
        if images is None:
            images = _serialize_plate_images(
                getattr(obj, 'plate_image', None),
                self.context.get('request'),
            )
            setattr(obj, cache_attr, images)
        return images

    def get_plate_image(self, obj):
        return self._get_cached_plate_images(obj)

    def get_plate_image_url(self, obj):
        images = self._get_cached_plate_images(obj)
        return [entry.get('url') for entry in images if entry.get('url')]

    def to_representation(self, instance):
        data = super().to_representation(instance)
        if not data.get('design_code') and instance.id:
            data['design_code'] = str(instance.id)
        return data


class PlateOrderListSerializer(PlateOrderDesignCodeMixin, serializers.ModelSerializer):
    """开版订单列表序列化器"""
    customer_name = serializers.CharField(source="customer.name", read_only=True)
    salesperson_name = serializers.CharField(source="salesperson.name", read_only=True)
    merchandiser_name = serializers.CharField(source="merchandiser.name", read_only=True)
    designer_name = serializers.CharField(source="designer.name", read_only=True)
    created_by = serializers.IntegerField(source='created_by_id', read_only=True)
    status = serializers.CharField(read_only=True)
    progress_percentage = serializers.IntegerField(read_only=True)
    process_name = serializers.SerializerMethodField()
    plate_image_url = serializers.SerializerMethodField()
    last_completed_state = serializers.CharField(read_only=True)
    content_type_id = serializers.SerializerMethodField()
    created_by_name = serializers.SerializerMethodField()
    merchant_id = serializers.IntegerField(source='merchant.id', read_only=True, allow_null=True)
    
    class Meta:
        model = models.PlateOrder

        fields = [
            'id', 'original_id', 'merchant_id', 'design_code', 'plate_type', 'plate_date', 'plate_method',
            'plate_image', 'plate_image_url', 'image_name', 'plate_notes', 'reprint_reason',
            'urgency_level', 'is_invalid',
            'customer', 'customer_name', 'area', 'default_address',
            'salesperson', 'salesperson_name',
            'merchandiser', 'merchandiser_name',
            'designer', 'designer_name',
            'style_name', 'fabric', 'fabric_source', 'width', 'production_method',
            'is_mark_frame', 'drawing_rating', 'color_matching_rating',
            'sample_rating', 'difficulty_rating',
            'sample_meter', 'required_sample_meters',
            'required_completion_date', 'completion_date',
            'approval_result', 'is_ordered', 'customer_feedback', 'print_count',
            'process', 'process_name', 'created_by_name',
            'status', 'status_id', 'is_completed', 'has_started', 'last_completed_state',
            'progress_percentage', 'business_object_id', 'content_type_id',
            'created_by',
            'created_at', 'updated_at'
        ]
        read_only_fields = [
            'id', 'status', 'progress_percentage', 'last_completed_state',
            'created_at', 'updated_at', 'print_count', 'merchant_id'
        ]

    def get_plate_image_url(self, obj):
        images = _serialize_plate_images(getattr(obj, 'plate_image', None), self.context.get('request'))
        return [entry.get('url') for entry in images if entry.get('url')]

    def get_created_by_name(self, obj):
        user = getattr(obj, 'created_by', None)
        if not user:
            return None
        emp = getattr(user, 'employee', None)
        return getattr(emp, 'name', None)
    
    def get_process_name(self, obj) ->str | None:
        """获取流程名称"""
        if obj.process:
            try:
                from stateflow.models import Process
                process = Process.objects.get(id=obj.process)
                return process.name
            except Process.DoesNotExist:
                return None
        return None

    def get_content_type_id(self, obj):
        """
        返回关联流程实例（business_object）的 content_type_id。
        
        - 无 business_object：返回 None
        - business_object 存在但未绑定关联对象：返回 None
        """
        if not getattr(obj, 'business_object_id', None):
            return None
        return obj.business_object.content_type_id


class PlateOrderDetailSerializer(PlateOrderDesignCodeMixin, serializers.ModelSerializer):
    """开版订单详情序列化器"""
    customer_name = serializers.CharField(source="customer.name", read_only=True)
    customer_phone = serializers.CharField(source="customer.mobile", read_only=True)
    salesperson_name = serializers.CharField(source="salesperson.name", read_only=True)
    merchandiser_name = serializers.CharField(source="merchandiser.name", read_only=True)
    designer_name = serializers.CharField(source="designer.name", read_only=True)
    created_by = serializers.IntegerField(source='created_by_id', read_only=True)
    status = serializers.CharField(read_only=True)
    status_id = serializers.IntegerField(read_only=True)
    is_completed = serializers.BooleanField(read_only=True)
    has_started = serializers.BooleanField(read_only=True)
    progress_percentage = serializers.IntegerField(read_only=True)
    business_object_id = serializers.SerializerMethodField()
    plate_image_url = serializers.SerializerMethodField()
    process_name = serializers.SerializerMethodField()
    last_completed_state = serializers.CharField(read_only=True)
    merchant_id = serializers.IntegerField(source='merchant.id', read_only=True, allow_null=True)
    
    class Meta:
        model = models.PlateOrder
        fields = [
            'id', 'original_id', 'merchant_id', 'design_code', 'plate_type', 'plate_date', 'plate_method',
            'plate_image', 'plate_image_url', 'image_name', 'plate_notes', 'reprint_reason',
            'urgency_level', 'is_invalid',
            'customer', 'customer_name', 'customer_phone', 'area', 'default_address',
            'salesperson', 'salesperson_name',
            'merchandiser', 'merchandiser_name',
            'designer', 'designer_name',
            'style_name', 'fabric', 'fabric_source', 'width', 'production_method',
            'is_mark_frame', 'drawing_rating', 'color_matching_rating',
            'sample_rating', 'difficulty_rating',
            'sample_meter', 'required_sample_meters',
            'required_completion_date', 'completion_date',
            'approval_result', 'is_ordered', 'customer_feedback', 'print_count',
            'process', 'process_name',
            'status', 'status_id', 'is_completed', 'has_started',
            'progress_percentage', 'business_object_id', 'last_completed_state',
            'created_by',
            'created_at', 'updated_at'
        ]
        read_only_fields = [
            'id', 'status', 'status_id', 'is_completed', 'has_started',
            'progress_percentage', 'business_object_id', 'last_completed_state',
            'created_at', 'updated_at', 'print_count', 'merchant_id'
        ]
    
    def get_business_object_id(self, obj):
        """安全地获取 business_object_id"""
        return obj.business_object.id if obj.business_object else None
    
    def get_plate_image_url(self, obj):
        images = _serialize_plate_images(getattr(obj, 'plate_image', None), self.context.get('request'))
        return [entry.get('url') for entry in images if entry.get('url')]
    
    def get_process_name(self, obj):
        """获取流程名称"""
        if obj.process:
            try:
                from stateflow.models import Process
                process = Process.objects.get(id=obj.process)
                return process.name
            except Process.DoesNotExist:
                return None
        return None


class PlateOrderCreateUpdateSerializer(serializers.ModelSerializer):
    """开版订单创建/更新序列化器"""
    plate_image = PlateImageInputSerializer(
        many=True,
        required=False,
        allow_null=True,
        help_text="开版图片列表，需提供已上传的 file_id，可选 name 字段"
    )
    created_by = serializers.IntegerField(source='created_by_id', read_only=True)

    class Meta:
        model = models.PlateOrder
        fields = [
            "id", "original_id", "design_code", "plate_type", "plate_date", "plate_method",
            "plate_image", "image_name", "plate_notes", "reprint_reason",
            "urgency_level", "is_invalid",
            "customer", "area", "default_address",
            "salesperson", "merchandiser", "designer",
            "style_name", "fabric", "fabric_source", "width", "production_method",
            "is_mark_frame", "drawing_rating", "color_matching_rating",
            "sample_rating", "difficulty_rating",
            "sample_meter", "required_sample_meters",
            "required_completion_date", "completion_date",
            "approval_result", "is_ordered", "customer_feedback",
            "process",
            "created_by",
        ]
        read_only_fields = ["id", "created_by"]

    def validate_plate_image(self, value):
        return value or []

    def validate_customer(self, value):
        """验证客户是否存在"""
        if not value:
            raise serializers.ValidationError("客户不能为空")
        return value
    
    def validate_salesperson(self, value):
        """验证业务员是否存在"""
        if value and not Employee.objects.filter(id=value.id).exists():
            raise serializers.ValidationError("业务员不存在")
        return value
    
    def validate_merchandiser(self, value):
        """验证跟单员是否存在"""
        if value and not Employee.objects.filter(id=value.id).exists():
            raise serializers.ValidationError("跟单员不存在")
        return value
    
    def validate_designer(self, value):
        """验证设计师是否存在"""
        if value and not Employee.objects.filter(id=value.id).exists():
            raise serializers.ValidationError("设计师不存在")
        return value
    
    def validate_sample_meter(self, value):
        """验证样品米数"""
        if value is not None and value < 0:
            raise serializers.ValidationError("样品米数不能为负数")
        return value
    
    def validate_required_sample_meters(self, value):
        """验证所需样品米数"""
        if value is not None and value < 0:
            raise serializers.ValidationError("所需样品米数不能为负数")
        return value
    
    def validate_process(self, value):
        """验证流程是否存在"""
        if value:
            from stateflow.models import Process
            if not Process.objects.filter(id=value).exists():
                raise serializers.ValidationError("流程不存在")
        return value
    
    def validate(self, attrs):
        """交叉验证"""
        return attrs
    
    def create(self, validated_data):
        plate_images = validated_data.pop('plate_image', None)
        if plate_images is not None:
            validated_data['plate_image'] = _build_plate_image_payload(plate_images, self._request_user)
        return super().create(validated_data)
    
    def update(self, instance, validated_data):
        # 若更新涉及 process 变更，需要重建并重新绑定 BusinessObject（若有未撤销进度则拒绝）
        new_process_id = validated_data.get('process', None)
        if new_process_id is not None and new_process_id != getattr(instance, 'process', None):
            from stateflow.models import Process
            from stateflow.services import relink_business_object_for_instance

            try:
                new_process = Process.objects.get(id=new_process_id)
            except Process.DoesNotExist:
                # 理论上 validate_process 已校验；这里兜底
                raise serializers.ValidationError({'process': '流程不存在'})

            relinked = relink_business_object_for_instance(
                instance=instance,
                new_process=new_process,
                default_name=f"PlateOrder-{instance.pk}",
                default_description=f"开版订单 {instance.pk} 的流程实例",
            )
            if relinked is None:
                raise serializers.ValidationError({'process': '该订单流程已存在有效进度，无法修改流程'})

        plate_images = validated_data.pop('plate_image', None)
        if plate_images is not None:
            validated_data['plate_image'] = _build_plate_image_payload(plate_images, self._request_user)
        return super().update(instance, validated_data)

    @property
    def _request_user(self):
        request = self.context.get('request')
        return getattr(request, 'user', None)
