"""
Stateflow业务逻辑服务层
"""
import copy
import logging
from typing import Iterable, List, Optional, Tuple
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from django.db.models import Exists, OuterRef, QuerySet
from . import models

logger = logging.getLogger(__name__)
User = get_user_model()

def _can_relink_business_object(business_object: 'models.BusinessObject') -> bool:
    """
    内部判断：该 BusinessObject 是否允许“换流程并重建”（relink）。

    规则（与 printing.models.PlateOrder/PrintingJob.has_started 保持一致）：
    - 只要存在未撤销（is_cancelled=False）的状态记录，就视为已开始，不允许 relink
    """
    if not business_object:
        return True
    return not business_object.state_logs.filter(is_cancelled=False).exists()


def relink_business_object_for_instance(
    *,
    instance,
    new_process: 'models.Process',
    default_name: str | None = None,
    default_description: str | None = None,
) -> Optional['models.BusinessObject']:
    """
    当业务对象的“流程”发生变更时，重建并重新绑定一个新的 BusinessObject。

    设计目标：
    - stateflow 层提供统一逻辑（不依赖 printing/shipment 等业务模块）
    - 仅在“没有未撤销进度”的情况下允许重建
    - 旧 BusinessObject 不删除（未来可通过“无反向引用”视为悬空）

    返回：
    - BusinessObject 实例：relink 成功后的新 BO
    - None：不允许 relink（例如已存在未撤销进度），调用方应将其转为 400

    注意：
    - 若 instance 原本没有 business_object，本函数仍会创建并绑定新的 BO
    - 本函数只负责 BO 的创建与绑定，不负责写入业务对象自身的 process 字段
    """
    if instance is None or getattr(instance, 'pk', None) is None:
        return None
    if new_process is None:
        return None

    old_bo = getattr(instance, 'business_object', None)
    if old_bo and not _can_relink_business_object(old_bo):
        return None

    ct = ContentType.objects.get_for_model(instance.__class__)
    bo = models.BusinessObject.objects.create(
        name=(default_name or f"{instance.__class__.__name__}-{instance.pk}"),
        process=new_process,
        description=(default_description or ''),
        content_type=ct,
        object_id=instance.pk,
    )

    # 绑定到业务对象（业务对象侧通常为 OneToOneField）
    setattr(instance, 'business_object', bo)
    instance.save(update_fields=['business_object'])
    return bo


def ensure_business_object_bound_to_instance(
    business_object: 'models.BusinessObject',
    instance,
    *,
    default_name: str | None = None,
) -> bool:
    """
    确保 BusinessObject 的 GenericForeignKey 绑定到指定实例。

    说明：
    - 这是一个“自愈”辅助函数，用于修复历史数据/非标准创建路径导致的 content_type/object_id 为空或不一致。
    - 仅在调用方明确知道该 BusinessObject 应当绑定到 instance 时使用（例如 PrintingJob.business_object 一对一）。

    返回：
    - True：本次发生了更新并保存
    - False：无需更新或参数不足（business_object/instance/instance.pk 为空）
    """
    if not business_object or instance is None:
        return False

    instance_id = getattr(instance, 'pk', None)
    if not instance_id:
        return False

    ct = ContentType.objects.get_for_model(instance.__class__)
    update_fields: list[str] = []

    if business_object.content_type_id != ct.id:
        business_object.content_type = ct
        update_fields.append('content_type')

    if business_object.object_id != instance_id:
        business_object.object_id = instance_id
        update_fields.append('object_id')

    if default_name is not None and not (business_object.name or '').strip():
        business_object.name = default_name
        update_fields.append('name')

    if not update_fields:
        return False

    business_object.save(update_fields=update_fields)
    return True


def get_last_completed_state(business_object: 'models.BusinessObject') -> Optional['models.State']:
    """
    获取最后完成的状态
    
    返回：最后一个已完成的状态节点，如果没有则返回 None
    
    应用场景：
    - 显示"刚完成了什么"
    - 查询最后完成状态的参数
    - 审计日志
    """
    last_log = business_object.state_logs.filter(
        is_cancelled=False
    ).order_by('-completed_at', '-id').first()
    
    return last_log.state if last_log else None


def get_next_pending_state_simple(business_object: 'models.BusinessObject') -> Optional['models.State']:
    """
    获取下一个待执行的状态（简化版，只返回State对象）
    
    返回：下一个待执行的状态节点，如果没有则返回 None
    
    应用场景：
    - 显示"接下来要做什么"
    - 获取下一步需要的参数
    - 流程推进前的检查
    """
    next_pending = get_next_pending_state(business_object, include_parameters=False)
    return next_pending['state'] if next_pending else None


def get_business_object_current_state(business_object: 'models.BusinessObject') -> Optional['models.State']:
    """
    获取业务对象的当前状态（可配置模式）
    
    根据 settings.STATEFLOW_CURRENT_STATE_MODE 决定返回：
    - 'NEXT' (默认): 下一个待执行的节点
    - 'LAST': 最后完成的节点
    
    注意：此函数保持原有接口不变，但行为可配置
    推荐：在新代码中直接使用 get_last_completed_state() 或 get_next_pending_state_simple() 以明确语义
    
    NEXT模式的规则：
    - 返回下一个待执行的节点（第一个未完成的节点）
    - 如果所有节点都已完成，返回 None
    - 配合进度可以判断具体状态：
      - 进度 0% + current_state 非空 → 显示"未开始"
      - 进度 > 0% + current_state 非空 → 显示 current_state.name（正在进行）
      - current_state = None → 显示"已完成"
    
    LAST模式的规则：
    - 返回最后一个已完成的节点
    - 如果没有完成任何节点，返回 None
    """
    from django.conf import settings
    mode = getattr(settings, 'STATEFLOW_CURRENT_STATE_MODE', 'NEXT')
    
    if mode == 'LAST':
        return get_last_completed_state(business_object)
    else:  # 默认 'NEXT'
        return get_next_pending_state_simple(business_object)


def get_business_object_state_status(business_object: 'models.BusinessObject', state: 'models.State') -> str:
    """
    获取业务对象中某个状态的状态
    
    返回值:
    - 'not_started': 未开始
    - 'completed': 已完成
    
    注意：由于 current_state 现在表示"最后完成的状态"，不再有 'in_progress' 概念
    """
    # 检查是否已完成且未被撤销
    is_completed = business_object.state_logs.filter(state=state, is_cancelled=False).exists()
    if is_completed:
        return 'completed'
    
    return 'not_started'


def get_completed_node_ids(business_object: 'models.BusinessObject') -> List[int]:
    """获取订单已完成且未撤销的节点ID列表（按完成顺序）"""
    return list(
        business_object.state_logs.filter(is_cancelled=False)
        .order_by('completed_at')
        .values_list('state_id', flat=True)
    )


def get_progress_percentage(business_object: 'models.BusinessObject') -> float:
    """计算订单进度百分比"""
    total_nodes = business_object.process.process_nodes.count()
    if total_nodes == 0:
        return 0.0
    
    completed_count = business_object.state_logs.filter(is_cancelled=False).count()
    return (completed_count / total_nodes) * 100


def advance_to_next_state(business_object: 'models.BusinessObject', user, **parameters) -> Tuple[bool, str, Optional['models.StateFlowRecord']]:
    """
    将业务对象推进到下一个状态（完成下一个未完成的节点）
    
    参数:
        business_object: BusinessObject 实例
        user: 操作用户
        **parameters: 状态参数（可选）
    
    返回: 
        tuple: (success: bool, message: str, state_log: StateFlowRecord|None)
        - success: 是否成功
        - message: 提示信息
        - state_log: 创建的状态日志（成功时）
    """
    # 业务约束：BusinessObject 必须能追溯到真实业务对象，否则状态流转记录没有业务意义。
    # DB 允许为空仅为历史兼容；但实际运行中存在“先创建 BO、后绑定到业务模型”的非标准路径。
    # 为避免这类数据导致推进失败，这里做一次自愈：
    # - 若 BO 未绑定 content_type/object_id（或 content_object 解析不到实例）
    # - 则尝试通过常见的一对一反向关系（如 printing_job/plate_order）推断真实业务对象并补齐绑定
    if business_object.content_type_id is None or business_object.object_id is None or business_object.content_object is None:
        inferred_instance = None
        inferred_default_name = None

        # PrintingJob.business_object -> related_name='printing_job'
        try:
            inferred_instance = getattr(business_object, 'printing_job', None)
            if inferred_instance is not None:
                inferred_default_name = f"PrintingJob-{getattr(inferred_instance, 'pk', '')}"
        except (ObjectDoesNotExist, AttributeError):
            inferred_instance = None

        # PlateOrder.business_object -> related_name='plate_order'
        if inferred_instance is None:
            try:
                inferred_instance = getattr(business_object, 'plate_order', None)
                if inferred_instance is not None:
                    inferred_default_name = f"PlateOrder-{getattr(inferred_instance, 'pk', '')}"
            except (ObjectDoesNotExist, AttributeError):
                inferred_instance = None

        if inferred_instance is not None:
            ensure_business_object_bound_to_instance(
                business_object,
                inferred_instance,
                default_name=inferred_default_name,
            )
            # GenericForeignKey 可能缓存了旧值，刷新以确保后续 content_object 判断正确
            business_object.refresh_from_db(fields=['content_type', 'object_id', 'name'])

    if business_object.content_type_id is None or business_object.object_id is None:
        return False, "业务对象未绑定关联对象（content_type/object_id），禁止推进", None
    if business_object.content_object is None:
        return (
            False,
            f"业务对象绑定的关联对象不存在：{business_object.content_type.app_label}.{business_object.content_type.model} #{business_object.object_id}",
            None,
        )

    can_advance, reason = can_advance_to_next_state(business_object)
    if not can_advance:
        return False, reason, None

    with transaction.atomic():
        # 获取下一个待执行的节点
        next_pending = get_next_pending_state(business_object, include_parameters=False)
        
        if not next_pending:
            return False, "没有待执行的节点", None
        
        next_state = next_pending['state']
        
        # 验证必填参数
        if parameters:
            is_valid, missing_params = validate_required_parameters(next_state, **parameters)
            if not is_valid:
                return False, f"缺失必填参数: {', '.join(missing_params)}", None
        else:
            # 检查是否有必填参数
            required_params = get_required_parameters(next_state)
            if required_params.exists():
                required_keys = list(required_params.values_list('key', flat=True))
                return False, f"缺失必填参数: {', '.join(required_keys)}", None
        
        # 创建状态流转记录
        state_log = models.StateFlowRecord.objects.create(
            business_object=business_object,
            state=next_state,
            completed_by=user
        )
        
        # 如果提供了参数，创建参数记录
        if parameters:
            create_parameter_record(state_log, **parameters)
        
        # 检查是否所有状态都已完成
        after_advance = get_next_pending_state(business_object, include_parameters=False)
        is_process_completed = (after_advance is None)
        
        # 发送状态推进信号（在事务内，确保数据已持久化）
        # sender 使用 content_object 的类，以便监听者按类型过滤
        from . import signals
        content_object = business_object.content_object
        sender_class = content_object.__class__ if content_object else None
        
        logger.info(
            f'[stateflow] 发送 state_advanced 信号: '
            f'sender={sender_class} (module={sender_class.__module__ if sender_class else None}), '
            f'business_object_id={business_object.id}, '
            f'state={next_state.name}, is_process_completed={is_process_completed}'
        )
        
        signals.state_advanced.send(
            sender=sender_class,
            process_id=business_object.process_id,
            business_object_id=business_object.id,
            business_object=business_object,
            content_object=content_object,
            state=next_state,
            state_log=state_log,
            completed_by=user,
            is_process_completed=is_process_completed,
            last_completed_state_id=next_state.id,
            last_completed_by=user,
        )
        
        if is_process_completed:
            # 流程全部完成，发送流程完成信号
            logger.info(
                f'[stateflow] 发送 process_completed 信号: '
                f'sender={sender_class}, business_object_id={business_object.id}'
            )
            signals.process_completed.send(
                sender=sender_class,
                process_id=business_object.process_id,
                business_object_id=business_object.id,
                business_object=business_object,
                content_object=content_object,
                completed_by=user,
                last_completed_state_id=next_state.id,
                last_completed_by=user,
            )
            return True, f"流程已完成，最后状态: {next_state.name}", state_log
        
        return True, f"已完成状态: {next_state.name}", state_log


def reset_business_object_progress(business_object: 'models.BusinessObject') -> None:
    """
    重置业务对象进度
    
    注意：不删除历史记录，而是标记所有记录为已撤销，并记录撤销时间
    这样可以保留完整的操作历史，支持审计需求
    """
    from django.utils import timezone
    
    business_object.state_logs.filter(is_cancelled=False).update(
        is_cancelled=True,
        cancelled_at=timezone.now()
    )


# 向后兼容的别名
reset_order_progress = reset_business_object_progress


def step_back_one_state(business_object: 'models.BusinessObject', user) -> Tuple[bool, str]:
    """
    回退一步（撤销最后一次完成的状态）
    
    这是 advance_to_next_state 的逆向操作
    
    返回: (是否成功, 消息)
    """
    from django.utils import timezone
    
    with transaction.atomic():
        # 获取最后一条有效的状态流转记录（按完成时间倒序）
        last_record = (
            business_object.state_logs
            .filter(is_cancelled=False)
            .order_by('-completed_at', '-id')
            .first()
        )
        
        # 如果没有任何有效记录，说明当前没有任何状态流转，无需回退
        if not last_record:
            return False, "当前没有任何状态流转记录，无法回退"
        
        # 标记最后一条记录为已撤销
        last_record.is_cancelled = True
        last_record.cancelled_at = timezone.now()
        last_record.save(update_fields=['is_cancelled', 'cancelled_at'])
        
        return True, f"已回退状态: {last_record.state.name}"


def get_current_state_parameters(business_object: 'models.BusinessObject') -> List['models.StateParameter']:
    """获取订单当前状态的参数列表"""
    current_state = get_business_object_current_state(business_object)
    if current_state:
        return list(current_state.parameters.all())
    return []


def get_display_status(business_object: 'models.BusinessObject') -> str:
    """
    获取用于显示的状态文本
    
    规则：
    - 如果没有 business_object：返回第一个节点名称（如果有流程），否则返回空字符串
    - 如果 current_state 为 None：返回"已完成"
    - 否则：返回 current_state 的名称
    
    注意：此函数废除了"未开始"的概念，直接显示节点名称
    
    返回值: 状态显示文本（字符串）
    """
    if not business_object:
        return ''
    
    current_state = get_business_object_current_state(business_object)
    
    if current_state is None:
        # 没有待执行的节点，已完成
        return '已完成'
    
    # 直接返回当前状态名称，不管进度如何
    return current_state.name


def get_overall_status(business_object: 'models.BusinessObject') -> str:
    """
    获取业务对象的整体状态
    
    返回值:
    - 'in_progress': 进行中（有部分状态已完成，但未完成所有）
    - 'completed': 已完成（所有状态都已完成）
    
    注意：废除了 'not_started' 状态，进度为0时也返回 'in_progress'
    """
    # 检查是否有下一个待执行节点
    next_pending = get_next_pending_state(business_object, include_parameters=False)
    
    if next_pending is None:
        # 没有待执行节点，说明所有状态都已完成
        return 'completed'
    
    return 'in_progress'


def can_advance_to_next_state(business_object: 'models.BusinessObject') -> Tuple[bool, str]:
    """
    检查业务对象是否可以推进到下一个状态
    
    返回: (是否可以推进, 原因)
    """
    # 检查流程是否有节点
    first_node = business_object.process.process_nodes.order_by('order', 'id').first()
    if not first_node:
        return False, "流程没有任何节点"
    
    # 获取下一个待执行节点
    next_pending = get_next_pending_state(business_object, include_parameters=False)
    
    if next_pending is None:
        # 没有待执行节点，流程已完成
        return False, "流程已完成，无法继续推进"
    
    next_state = next_pending['state']
    return True, f"可以推进到: {next_state.name}"


def get_business_object_state_timeline(business_object: 'models.BusinessObject') -> List[dict]:
    """
    获取业务对象状态时间线（包括未开始和已完成的状态）
    
    返回格式:
    [
        {
            'state': State对象,
            'status': 'not_started' | 'completed' | 'cancelled',
            'order': 顺序号,
            'completed_at': 完成时间（如果已完成）,
            'completed_by': 完成人（如果已完成）,
            'cancelled_at': 撤销时间（如果已撤销）,
            'is_cancelled': 是否已撤销,
        },
        ...
    ]
    
    状态判断规则：
    - cancelled: 有日志记录且已撤销
    - completed: 有日志记录且未撤销
    - not_started: 没有日志记录（还未完成）
    
    注意：移除了 'in_progress' 状态，因为在当前设计中：
    - current_state 表示"下一个待执行的节点"，它还未开始执行，所以是 not_started
    - 一个节点要么已完成，要么还未开始，没有"进行中"的中间状态
    """
    timeline = []
    process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id')
    
    # 构建状态日志映射（包括已撤销的记录）
    state_logs_map = {
        log.state_id: log 
        for log in business_object.state_logs.select_related('completed_by')
    }
    
    # 获取 current_state（下一个待执行的节点）
    current_state = get_business_object_current_state(business_object)
    current_state_id = current_state.id if current_state else None
    
    # 检查是否有任何完成记录
    has_any_completed = business_object.state_logs.filter(is_cancelled=False).exists()
    
    for node in process_nodes:
        state = node.state
        log = state_logs_map.get(state.id)
        
        if log:
            if log.is_cancelled:
                status = 'cancelled'
            else:
                status = 'completed'
            completed_at = log.completed_at
            completed_by = log.completed_by
            cancelled_at = log.cancelled_at
            is_cancelled = log.is_cancelled
        else:
            # 未开始的节点
            # current_state 是"下一个待执行的节点"，它还未开始，所以状态是 not_started
            status = 'not_started'
            completed_at = None
            completed_by = None
            cancelled_at = None
            is_cancelled = False
        
        timeline.append({
            'state': state,
            'status': status,
            'order': node.order,
            'completed_at': completed_at,
            'completed_by': completed_by,
            'cancelled_at': cancelled_at,
            'is_cancelled': is_cancelled,
        })
    
    return timeline


def get_next_pending_state(
    business_object: 'models.BusinessObject', 
    include_parameters: bool = True
) -> Optional[dict]:
    """
    获取指定业务对象的下一个待执行节点（第一个未完成的节点）
    
    Args:
        business_object: 业务对象
        include_parameters: 是否包含参数列表，默认 True
    
    Returns:
        包含状态信息的字典，如果没有待执行节点则返回 None
        {
            'state': State对象,
            'order': 节点顺序号,
            'parameters': [StateParameter列表] (如果 include_parameters=True)
        }
    """
    # 获取所有节点（按顺序）
    process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id')
    if not process_nodes.exists():
        return None
    
    # 获取已完成且未被撤销的状态ID集合
    completed_state_ids = set(
        business_object.state_logs.filter(is_cancelled=False).values_list('state_id', flat=True)
    )
    
    # 找到第一个未完成的节点
    for node in process_nodes:
        if node.state_id not in completed_state_ids:
            result = {
                'state': node.state,
                'order': node.order,
            }
            if include_parameters:
                result['parameters'] = list(node.state.parameters.all())
            return result
    
    # 所有节点都已完成
    return None


def get_all_pending_states(business_object: 'models.BusinessObject') -> List[dict]:
    """
    获取指定业务对象所有待执行的节点列表（不包含参数）
    
    Args:
        business_object: 业务对象
    
    Returns:
        待执行节点列表，每个元素包含：
        {
            'state': State对象,
            'order': 节点顺序号
        }
    """
    # 获取所有节点
    process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id')
    
    # 获取已完成且未被撤销的状态ID集合
    completed_state_ids = set(
        business_object.state_logs.filter(is_cancelled=False).values_list('state_id', flat=True)
    )
    
    # 找到所有未完成的节点
    pending_nodes = []
    for node in process_nodes:
        if node.state_id not in completed_state_ids:
            pending_nodes.append({
                'state': node.state,
                'order': node.order,
            })
    
    return pending_nodes


def get_state_parameters(
    state: 'models.State', 
    required_only: bool = False
) -> List['models.StateParameter']:
    """
    获取指定节点的所有参数列表
    
    Args:
        state: 状态节点
        required_only: 是否仅包含必填参数，默认 False
    
    Returns:
        参数列表
    """
    return state.get_parameters(required_only=required_only)


def get_required_parameters(state: 'models.State'):
    """
    获取状态的所有必填参数
    
    参数:
        state: State 实例
    
    返回:
        QuerySet: 必填的 StateParameter 对象
    """
    return state.parameters.filter(is_required=True)


def validate_required_parameters(state: 'models.State', **kwargs) -> Tuple[bool, List[str]]:
    """
    验证必填参数是否全部提供
    
    参数:
        state: State 实例
        **kwargs: 用户提供的参数字典
    
    返回:
        tuple: (is_valid: bool, missing_params: list)
        - is_valid: 是否通过验证
        - missing_params: 缺失的必填参数 key 列表
    """
    required_params = get_required_parameters(state)
    required_keys = list(required_params.values_list('key', flat=True))
    provided_keys = set(kwargs.keys())
    missing_keys = [key for key in required_keys if key not in provided_keys]
    
    is_valid = len(missing_keys) == 0
    return is_valid, missing_keys


def create_parameter_record(state_log: 'models.StateFlowRecord', remark: str = '', **parameters) -> 'models.StateLogParameterRecord':
    """
    为状态流转记录创建参数记录
    
    参数:
        state_log: StateFlowRecord 实例
        remark: 备注
        **parameters: 参数字典（直接保存，不做验证）
    
    返回:
        StateLogParameterRecord: 创建的参数记录
    """
    record = models.StateLogParameterRecord.objects.create(
        state_log=state_log,
        parameters=parameters,  # 直接保存为 JSON
        remark=remark
    )
    
    return record


def add_parameters_to_state_log(state_log: 'models.StateFlowRecord', remark: str = '', **parameters) -> 'models.StateLogParameterRecord':
    """
    为已有的状态流转记录补充参数（支持重复key）
    
    参数:
        state_log: StateFlowRecord 实例
        remark: 备注
        **parameters: 参数字典
    
    返回:
        StateLogParameterRecord: 创建的参数记录
    """
    return create_parameter_record(state_log, remark, **parameters)


def get_process_nodes(business_object: 'models.BusinessObject') -> List[dict]:
    """
    获取业务对象所属流程的所有状态节点列表
    
    参数:
        business_object: BusinessObject 实例
    
    返回:
        节点列表，每个元素包含：
        {
            'id': ProcessNode ID,
            'state': State对象,
            'state_id': State ID,
            'state_name': State 名称,
            'order': 节点顺序号
        }
    """
    process_nodes = business_object.process.process_nodes.select_related('state').order_by('order', 'id')
    
    return [
        {
            'id': node.id,
            'state': node.state,
            'state_id': node.state_id,
            'state_name': node.state.name,
            'order': node.order,
        }
        for node in process_nodes
    ]


def query_business_objects_by_state_status(
    *,
    state_ids: Iterable[int] | None = None,
    status: str = 'completed',
    process_id: int | None = None,
    base_queryset: QuerySet | None = None,
) -> QuerySet:
    """
    按节点状态过滤 BusinessObject 查询集

    Args:
        state_ids: 目标节点集合（为空时表示任意节点）
        status: 过滤状态，支持 completed/not_started/cancelled/in_progress
        process_id: 限定流程 ID
        base_queryset: 可复用的基础查询集

    Returns:
        过滤后的 QuerySet
    """
    if status not in {'completed', 'not_started', 'cancelled', 'in_progress'}:
        raise ValueError('unsupported status value')

    qs = base_queryset if base_queryset is not None else models.BusinessObject.objects.all()
    state_id_list = list(state_ids) if state_ids is not None else None

    if process_id is not None:
        qs = qs.filter(process_id=process_id)

    if state_id_list:
        qs = qs.filter(process__process_nodes__state_id__in=state_id_list)

    state_logs = models.StateFlowRecord.objects.filter(business_object_id=OuterRef('pk'))
    if state_id_list:
        state_logs = state_logs.filter(state_id__in=state_id_list)

    qs = qs.annotate(
        has_state_log=Exists(state_logs),
        has_state_completed=Exists(state_logs.filter(is_cancelled=False)),
        has_state_cancelled=Exists(state_logs.filter(is_cancelled=True)),
    )

    if status == 'completed':
        qs = qs.filter(has_state_completed=True)
    elif status == 'cancelled':
        qs = qs.filter(has_state_cancelled=True)
    elif status == 'not_started':
        qs = qs.filter(has_state_log=False)
    elif status == 'in_progress':
        qs = qs.filter(has_state_log=True, has_state_completed=False, has_state_cancelled=False)

    return qs.distinct()


def clone_business_object(
    source: 'models.BusinessObject',
    *,
    new_object_id: int,
    expected_content_type_id: int,
) -> 'models.BusinessObject':
    """
    克隆一个 BusinessObject（含所有状态流转记录与工艺参数记录）

    目标：
    - 不影响原对象
    - 克隆出“业务数据完全一致”的新对象（process/描述/关联对象/日志/参数等）
    - 返回新的 BusinessObject 实例

    说明：
    - 会克隆 source.state_logs 的所有记录（含已撤销记录）
    - 会克隆每条 StateFlowRecord 下的所有 StateLogParameterRecord（含参数 JSON 与 remark）
    - 会尽量保持 completed_at / cancelled_at / 参数记录 created_at 与原对象一致
    - created_at/updated_at 等 ModelBase 字段也会被同步（使用 update 绕过 auto_now*）
    - **重要**：克隆必须提供新的 object_id（当 source.content_type 非空时），否则克隆与业务绑定无差异，容易造成误用
    - expected_content_type_id 仅用于校验调用方意图：必须与 source.content_type_id 一致，否则拒绝克隆

    2026-01-13 曾短期禁用；目前恢复实现以保证 API/测试一致性。
    """
    if source is None:
        raise ValueError("source 不能为空")
    if source.content_type_id is None or source.object_id is None:
        raise ValueError("源对象未绑定关联对象（content_type/object_id），禁止克隆")
    if expected_content_type_id is None:
        raise ValueError("必须提供 expected_content_type_id")

    source = (
        models.BusinessObject.objects
        .select_related('process', 'content_type')
        .prefetch_related(
            'state_logs__state',
            'state_logs__completed_by',
            'state_logs__parameter_records',
        )
        .get(id=source.id)
    )

    if source.content_type_id != expected_content_type_id:
        raise ValueError("content_type 与源对象不一致，拒绝克隆")

    if new_object_id is None:
        raise ValueError("必须提供新的 object_id")
    if source.object_id == new_object_id:
        raise ValueError("object_id 必须与源对象不同")

    # 校验目标关联对象存在
    try:
        source.content_type.get_object_for_this_type(pk=new_object_id)
    except ObjectDoesNotExist:
        raise ValueError(
            f"目标关联对象不存在：{source.content_type.app_label}.{source.content_type.model} #{new_object_id}"
        )

    with transaction.atomic():
        cloned = models.BusinessObject.objects.create(
            name=source.name,
            process=source.process,
            description=source.description,
            content_type=source.content_type,
            object_id=new_object_id,
        )
        models.BusinessObject.objects.filter(id=cloned.id).update(
            created_at=source.created_at,
            updated_at=source.updated_at,
        )

        source_logs = sorted(
            list(source.state_logs.all()),
            key=lambda log: (log.completed_at, log.id),
        )
        for src_log in source_logs:
            new_log = models.StateFlowRecord.objects.create(
                business_object=cloned,
                state=src_log.state,
                completed_by=src_log.completed_by,
                is_cancelled=src_log.is_cancelled,
                cancelled_at=src_log.cancelled_at,
            )
            models.StateFlowRecord.objects.filter(id=new_log.id).update(
                completed_at=src_log.completed_at,
                created_at=src_log.created_at,
                updated_at=src_log.updated_at,
                cancelled_at=src_log.cancelled_at,
            )
            src_param_records = sorted(
                list(src_log.parameter_records.all()),
                key=lambda rec: (rec.created_at, rec.id),
            )
            for src_rec in src_param_records:
                new_rec = models.StateLogParameterRecord.objects.create(
                    state_log=new_log,
                    parameters=copy.deepcopy(src_rec.parameters),
                    remark=src_rec.remark,
                )
                models.StateLogParameterRecord.objects.filter(id=new_rec.id).update(
                    created_at=src_rec.created_at,
                    updated_at=src_rec.updated_at,
                )

        return models.BusinessObject.objects.get(id=cloned.id)
    #             models.StateLogParameterRecord.objects.filter(id=new_rec.id).update(
    #                 created_at=src_rec.created_at,
    #                 updated_at=src_rec.updated_at,
    #             )
    #     cloned.refresh_from_db()
    #     return cloned
