"""
Stateflow业务逻辑服务层
"""
import copy
from typing import List, Optional, Tuple
from django.contrib.auth import get_user_model
from django.db import transaction
from . import models

User = get_user_model()


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: 创建的状态日志（成功时）
    """
    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)
        if after_advance is None:
            # 所有状态都已完成
            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 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 一致，否则拒绝克隆
    """
    if source is None:
        raise ValueError("source 不能为空")

    # 重新加载 source，确保拿到完整关系（避免调用方未预取导致 N+1）
    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)
    )

    # 校验 content_type 一致性（不做额外校验，仅比对 id）
    if source.content_type_id != expected_content_type_id:
        raise ValueError("content_type 与源对象不一致，拒绝克隆")

    # 如果源对象未绑定 content_type，则拒绝克隆
    if source.content_type_id is None:
        return ValueError("源对象未绑定 content_type，拒绝克隆")
    else:
        if new_object_id is None:
            raise ValueError("必须提供新的 object_id")
        if source.object_id == new_object_id:
            raise ValueError("object_id 必须与源对象不同")
    
    content_type = models.ContentType.objects.get(id=source.content_type_id)
    actual_object = content_type.model_class().objects.get(id=new_object_id)
    if actual_object is None:
        raise ValueError("实际对象不存在，拒绝克隆")

    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,
        )

        # 同步 BusinessObject 的时间戳字段（保持一致性）
        models.BusinessObject.objects.filter(id=cloned.id).update(
            created_at=source.created_at,
            updated_at=source.updated_at,
        )

        # 复制 state_logs（按完成时间排序，保证克隆后的序列稳定）
        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,
            )

            # 同步 StateFlowRecord 的时间戳字段（含 completed_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,
            )

            # 复制参数记录（按 created_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,
                )

        # 重新加载，确保返回对象的字段与 DB 一致（尤其是时间戳）
        cloned.refresh_from_db()
        actual_object.refresh_from_db()

        actual_object.business_object_id = cloned.id
        actual_object.save()
        return cloned
