import { Injectable, Logger, NotFoundException, ForbiddenException, BadRequestException, HttpStatus } from '@nestjs/common';
import { BusinessException } from '@common/exceptions/business.exception';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { TemporalService } from './temporal/temporal.service';  // 使用审批引擎专用的 TemporalService
import { BusinessTypeRegistry } from './business-type-registry.service';
import { ApprovalTaskStatus, InstanceStatus, ApprovalTaskAction, NodeStatus, Prisma } from '@prisma/client';
import { SkipAssertAccess } from '@common/decorators/skip-assert-access.decorator';
import {
  StartApprovalDto,
  ApproveDto,
  RejectDto,
  ReturnDto,
  ForwardDto,
  WithdrawDto,
  ApproverWithdrawDto,
  AddSignDto,
  ClaimDto,
  UnclaimDto,
  ExecuteDto,
  PendingTasksQueryDto,
  InitiatedQueryDto,
  ProcessedQueryDto,
  CcQueryDto,
  ProcessSearchDto,
  RemindDto,
  BatchRemindDto,
} from './dto/approval.dto';
import {
  StartApprovalResponse,
  ApprovalActionResponse,
  WithdrawResponse,
  ApprovalTaskItem,
  PaginatedTasksResponse,
  TaskDetailResponse,
  ProcessDetailResponse,
  ProcessStatusResponse,
  ApprovalHistoryResponse,
  DiagramResponse,
  RemindResponse,
  BatchRemindResponse,
  TaskStatsResponse,
  UserInfo,
  ReturnableNode,
  AvailableAction,
} from './dto/approval-response.dto';

@Injectable()
export class ApprovalService {
  private readonly logger = new Logger(ApprovalService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly temporalService: TemporalService,
    private readonly businessTypeRegistry: BusinessTypeRegistry,
  ) {}

  // ==================== 辅助方法 ====================

  /**
   * 映射前端审批模式到后端工作流引擎格式
   * 前端: 'AND' | 'OR' | 'SEQUENTIAL' | 'SINGLE'
   * 后端: 'COUNTERSIGN' | 'OR_SIGN' | 'SEQUENTIAL'
   */
  private mapApprovalModeForWorkflow(frontendMode: string | undefined): string {
    const mode = frontendMode || 'OR';
    const mappedMode = (() => {
      switch (mode) {
        case 'AND':
          return 'COUNTERSIGN';  // 会签：所有人都要通过
        case 'OR':
          return 'OR_SIGN';      // 或签：任一人通过即可
        case 'SEQUENTIAL':
          return 'SEQUENTIAL';   // 依次审批
        case 'SINGLE':
          return 'OR_SIGN';      // 单人审批（相当于或签）
        default:
          return 'OR_SIGN';      // 默认或签
      }
    })();
    
    // ⭐ 调试日志（提交新流程时会打印）
    if (frontendMode !== mappedMode) {
      this.logger.log(`[ApprovalMode映射] ${frontendMode} → ${mappedMode}`);
    }
    
    return mappedMode;
  }

  /**
   * 标准化审批模式（处理历史数据中的前端格式）
   * 任何格式 → 后端标准格式
   * ⭐ 用于API返回时统一格式，避免前端匹配失败
   */
  private normalizeApprovalMode(mode: string | undefined | null): string | undefined {
    if (!mode) return undefined;
    
    // 已经是后端格式，直接返回
    if (mode === 'COUNTERSIGN' || mode === 'OR_SIGN' || mode === 'SEQUENTIAL') {
      return mode;
    }
    
    // 前端格式 → 后端格式（处理历史数据）
    return this.mapApprovalModeForWorkflow(mode);
  }

  // ==================== 流程启动 ====================

  /**
   * @SkipAssertAccess: 流程启动是系统侧调用（form-management.submitInstance / 业务方手工 API）。
   * IDOR 防护在调用方做：form-management 已校验 createdBy=userId；ApprovalInstance 是新建/重置
   * 自身的状态，不存在跨租户访问。
   */
  @SkipAssertAccess('系统侧流程启动：调用方已校验业务单据归属（form-management.submitInstance 已 createdBy 校验），重提路径复用旧 ApprovalInstance ID 也是该业务单据自己的，无 IDOR 风险')
  async startApproval(dto: StartApprovalDto, initiatorId: string): Promise<StartApprovalResponse> {
    this.logger.log(`Starting approval process: ${dto.processDefinitionKey} for business ${dto.businessType}:${dto.businessId}`);

    // 检查是否已存在相同业务的审批流程
    // (businessType, businessId) 上有 unique 约束，因此每个业务单据只能有一个 ApprovalInstance。
    // 重提（resubmit）场景：业务单据状态已是 REJECTED/WITHDRAWN，旧 ApprovalInstance 也是终态，
    // 此时**复用 ID + 重置状态 + 起新 workflow**，而非新建（否则会撞 unique 约束 → silent failure，
    // form 层状态变 PENDING_APPROVAL 但 approval engine 没新流程 → 审批人待办为空）。
    // 详见 .learnings/ERRORS/ERR-20260501-003.md。
    const existingInstance = await this.prisma.approvalInstance.findFirst({
      where: {
        businessType: dto.businessType,
        businessId: dto.businessId,
      },
    });

    if (existingInstance && existingInstance.status === InstanceStatus.RUNNING) {
      throw new BusinessException(
        '该业务单据已有运行中的审批流程',
        'PROCESS_ALREADY_EXISTS',
        HttpStatus.CONFLICT,
        {
          existingInstanceId: existingInstance.id,
          existingStatus: existingInstance.status,
          createdAt: existingInstance.startTime.toISOString(),
        },
      );
    }
    const isResubmit = !!existingInstance;

    // 获取流程定义和版本
    const definition = await this.prisma.approvalDefinition.findUnique({
      where: { key: dto.processDefinitionKey },
      include: {
        versions: {
          where: dto.version
            ? { version: dto.version }
            : { isDefault: true },
          take: 1,
        },
        },
      });
      
    if (!definition) {
      throw new BusinessException(
        `流程定义不存在: ${dto.processDefinitionKey}`,
        'PROCESS_DEFINITION_NOT_FOUND',
        HttpStatus.NOT_FOUND,
      );
    }

    if (definition.versions.length === 0) {
      throw new NotFoundException(`流程版本不存在`);
    }

    const version = definition.versions[0];
    let processModel = version.processModel as any;

    // ⭐ 调试日志：打印原始 processModel
    this.logger.log(`[启动工作流-调试] processModel.nodes数量: ${processModel?.nodes?.length || 0}`);
    if (processModel?.nodes) {
      const userTaskNodes = processModel.nodes.filter((n: any) => n.type === 'USER_TASK');
      this.logger.log(`[启动工作流-调试] USER_TASK节点数量: ${userTaskNodes.length}`);
      userTaskNodes.forEach((node: any) => {
        this.logger.log(
          `[启动工作流-调试] 节点"${node.name}" - approvalMode: ${node.approvalMode || '(未设置)'}, config.approvalMode: ${node.config?.approvalMode || '(未设置)'}`
        );
      });
    }

    // ⭐ 映射前端审批模式到后端工作流引擎格式
    // 前端: 'AND' → 后端: 'COUNTERSIGN'
    // 前端: 'OR' → 后端: 'OR_SIGN'
    if (processModel?.nodes) {
      let mappedCount = 0;
      
      processModel = {
        ...processModel,
        nodes: processModel.nodes.map((node: any) => {
          if (node.type === 'USER_TASK') {
            const topLevelMode = node.approvalMode;
            const configMode = node.config?.approvalMode;
            
            // ⭐ 关键：检查两个位置的approvalMode
            if (topLevelMode || configMode) {
              const sourceMode = topLevelMode || configMode;
              const mappedMode = this.mapApprovalModeForWorkflow(sourceMode);
              
              this.logger.log(
                `[启动工作流-映射] 节点"${node.name}" approvalMode: ${sourceMode} → ${mappedMode} (来源: ${topLevelMode ? 'node.approvalMode' : 'node.config.approvalMode'})`
              );
              
              mappedCount++;
              
              // ⭐ 同时更新两个位置
              return {
                ...node,
                approvalMode: mappedMode,  // 顶层
                config: node.config ? {
                  ...node.config,
                  approvalMode: mappedMode,  // config内部
                } : undefined,
              };
            }
          }
          return node;
        }),
      };
      
      if (mappedCount > 0) {
        this.logger.log(
          `[启动工作流-映射] ✅ 共映射 ${mappedCount} 个审批节点的approvalMode`
        );
      } else {
        this.logger.warn(
          `[启动工作流-映射] ⚠️ 没有找到需要映射的审批节点！`
        );
      }
    }

    // 生成 workflowId（每次重启都用新 workflowId，避免 Temporal workflow 重名冲突）
    const workflowId = `approval-${dto.businessType}-${dto.businessId}-${Date.now()}`;

    // 创建（或重置）流程实例
    // 重提：复用旧 ID + 清空终态字段；新提交：直接 create
    const instance = isResubmit
      ? await this.prisma.approvalInstance.update({
          where: { id: existingInstance!.id },
          data: {
            versionId: version.id,
            title: dto.title,
            workflowId,
            workflowRunId: '',
            initiatorId,
            status: InstanceStatus.RUNNING,
            currentNodeId: null,
            currentNode: null,
            variables: dto.variables || {},
            priority: dto.priority || 0,
            dueDate: dto.dueDate ? new Date(dto.dueDate) : null,
            startTime: new Date(),
            endTime: null,
            endReason: null,
            endComment: null,
            totalNodeExecutions: 0,
          },
        })
      : await this.prisma.approvalInstance.create({
          data: {
            version: { connect: { id: version.id } },
            businessType: dto.businessType,
            businessId: dto.businessId,
            businessKey: dto.businessId,        // 业务标识符（如 formInstance.id）
            title: dto.title,                   // 用户友好标题（如 "工时记录表 - 2025_0002"）
            workflowId,
            workflowRunId: '', // 由 Temporal 返回
            initiator: { connect: { id: initiatorId } },
            status: InstanceStatus.RUNNING,
            variables: dto.variables || {},
            priority: dto.priority || 0,
            dueDate: dto.dueDate ? new Date(dto.dueDate) : null,
          },
        });

    // 启动 Temporal 工作流
    let workflowRunId = '';
    try {
      const workflowResult = await this.temporalService.startApprovalWorkflow({
        instanceId: instance.id,
        workflowId,  // 传递已生成的 workflowId
        processModel,
        settings: version.settings as any || {},
        variables: dto.variables || {},
        initiatorId,
        businessType: dto.businessType,
        businessId: dto.businessId,
        callbackUrl: dto.callbackUrl,
      });
      workflowRunId = workflowResult.runId;

      // 更新 workflowRunId
      await this.prisma.approvalInstance.update({
        where: { id: instance.id },
        data: { workflowRunId },
      });
    } catch (error) {
      this.logger.error(`Failed to start Temporal workflow: ${error}`);
      // 继续执行，使用空的 runId
    }

    // 获取当前节点
    const currentNode = await this.getCurrentNodeInfo(instance.id);

      return {
        instanceId: instance.id,
        workflowId,
      workflowRunId,
      status: InstanceStatus.RUNNING,
      currentNodeId: currentNode?.nodeId,
      startTime: instance.startTime.toISOString(),
    };
  }

  // ==================== 审批操作 ====================

  async approve(dto: ApproveDto, operatorId: string): Promise<ApprovalActionResponse> {
    const task = await this.getTaskWithValidation(dto.taskId, operatorId);
    
    // 检查任务状态
    this.validateTaskForAction(task, 'APPROVE');

    // ⭐ 零技术债方案：立即更新任务状态 + 错误处理
    try {
      // 1. 立即更新任务状态（确保前端能查询到最新状态）
      await this.prisma.approvalTask.update({
        where: { id: dto.taskId },
        data: {
          status: ApprovalTaskStatus.COMPLETED,
          endTime: new Date(),
        },
      });

      // 2. 发送 Signal 到 Temporal
      await this.temporalService.sendSignal(task.instance.workflowId, 'approve', {
            taskId: dto.taskId,
        operatorId,
            comment: dto.comment,
        formData: dto.formData,
        attachments: dto.attachments,
      });
    } catch (error) {
      // ⭐ 错误处理：如果 sendSignal 失败，回滚任务状态
      this.logger.error(`Failed to send approve signal: ${error}`);
      
      try {
        await this.prisma.approvalTask.update({
          where: { id: dto.taskId },
          data: {
            status: ApprovalTaskStatus.PENDING,  // 回滚
            endTime: null,
          },
        });
      } catch (rollbackError) {
        this.logger.error(`Failed to rollback task status: ${rollbackError}`);
        // 记录到错误表，需要人工介入
      }
      
      throw new BadRequestException('审批操作失败，请重试');
    }

    // 注意：操作日志由 Temporal Workflow 自动创建，无需在这里重复记录

    // ⭐ 获取更新后的完整流程状态
    const updatedInstance = await this.prisma.approvalInstance.findUnique({
      where: { id: task.instanceId },
      include: { 
        nodeInstances: { 
          where: { status: 'ACTIVE' }, 
          take: 1,
          orderBy: { startTime: 'desc' }
        } 
      },
    });

    const isProcessCompleted = updatedInstance 
      ? ['APPROVED', 'REJECTED', 'WITHDRAWN', 'TERMINATED', 'FAILED'].includes(updatedInstance.status)
      : false;

    return {
      success: true,
      taskId: dto.taskId,
      action: 'APPROVE',
      nextNodeId: updatedInstance?.nodeInstances[0]?.nodeId,
      isProcessCompleted,
      // ⭐ 返回流程最新状态
      instance: updatedInstance ? {
        id: updatedInstance.id,
        status: updatedInstance.status as any,
        currentNodeId: updatedInstance.currentNodeId ?? undefined,
        currentNodeName: updatedInstance.nodeInstances[0]?.nodeName ?? undefined,
        endTime: updatedInstance.endTime?.toISOString(),
        endReason: updatedInstance.endReason ?? undefined,
      } : undefined,
      // ⭐ 流程完成时需要刷新列表
      shouldRefreshList: isProcessCompleted,
    };
  }

  async reject(dto: RejectDto, operatorId: string): Promise<ApprovalActionResponse> {
    const task = await this.getTaskWithValidation(dto.taskId, operatorId);
    
    this.validateTaskForAction(task, 'REJECT');

    // ⭐ 零技术债方案：立即更新任务状态 + 错误处理
    try {
      // 1. 立即更新任务状态
      await this.prisma.approvalTask.update({
        where: { id: dto.taskId },
        data: {
          status: ApprovalTaskStatus.COMPLETED,
          endTime: new Date(),
        },
      });

      // 2. 发送 Signal 到 Temporal
      await this.temporalService.sendSignal(task.instance.workflowId, 'reject', {
            taskId: dto.taskId,
        operatorId,
            comment: dto.comment,
        rejectCode: dto.rejectCode,
        attachments: dto.attachments,
      });
    } catch (error) {
      // ⭐ 错误处理：回滚任务状态
      this.logger.error(`Failed to send reject signal: ${error}`);
      
      try {
        await this.prisma.approvalTask.update({
          where: { id: dto.taskId },
          data: {
            status: ApprovalTaskStatus.PENDING,
            endTime: null,
          },
        });
      } catch (rollbackError) {
        this.logger.error(`Failed to rollback task status: ${rollbackError}`);
      }
      
      throw new BadRequestException('审批操作失败，请重试');
    }

    // 注意：操作日志由 Temporal Workflow 自动创建，无需在这里重复记录

    // ⭐ 获取更新后的完整流程状态
    const updatedInstance = await this.prisma.approvalInstance.findUnique({
      where: { id: task.instanceId },
    });

    return {
      success: true,
      taskId: dto.taskId,
      action: 'REJECT',
      isProcessCompleted: true,
      // ⭐ 返回流程最新状态
      instance: updatedInstance ? {
        id: updatedInstance.id,
        status: updatedInstance.status as any,
        currentNodeId: updatedInstance.currentNodeId ?? undefined,
        endTime: updatedInstance.endTime?.toISOString(),
        endReason: updatedInstance.endReason ?? undefined,
      } : undefined,
      // ⭐ 拒绝后流程结束，需要刷新列表
      shouldRefreshList: true,
    };
  }

  async return(dto: ReturnDto, operatorId: string): Promise<ApprovalActionResponse> {
    const task = await this.getTaskWithValidation(dto.taskId, operatorId);
    
    this.validateTaskForAction(task, 'RETURN');

    // 验证目标节点是否可退回
    const returnableNodes = await this.getReturnableNodes(task.instanceId, dto.taskId);
    if (!returnableNodes.find(n => n.nodeId === dto.targetNodeId)) {
      throw new BadRequestException('目标节点不可退回');
    }

    await this.temporalService.sendSignal(task.instance.workflowId, 'return', {
      taskId: dto.taskId,
      operatorId,
      targetNodeId: dto.targetNodeId,
      comment: dto.comment,
    });

    await this.createActionLog(dto.taskId, task.instanceId, 'RETURN', operatorId, {
      comment: dto.comment,
      targetNodeId: dto.targetNodeId,
    });

    return {
      success: true,
      taskId: dto.taskId,
      action: 'RETURN',
      nextNodeId: dto.targetNodeId,
      isProcessCompleted: false,
    };
  }

  async forward(dto: ForwardDto, operatorId: string): Promise<ApprovalActionResponse> {
    const task = await this.getTaskWithValidation(dto.taskId, operatorId);
    
    this.validateTaskForAction(task, 'FORWARD');

    // ⭐ 只发送信号给 Temporal workflow，由 workflow 统一创建 actionLog
    // 避免重复创建（workflow 的 handleForward 中也会创建）
    await this.temporalService.sendSignal(task.instance.workflowId, 'forward', {
        taskId: dto.taskId,
      operatorId,
      toUserId: dto.toUserId,
      comment: dto.comment,
    });

    return {
      success: true,
      taskId: dto.taskId,
      action: 'FORWARD',
      isProcessCompleted: false,
    };
  }

  async withdraw(instanceId: string, dto: WithdrawDto, operatorId: string): Promise<WithdrawResponse> {
    const instance = await this.prisma.approvalInstance.findUnique({
      where: { id: instanceId },
    });

    if (!instance) {
      throw new NotFoundException('流程实例不存在');
    }

    // 只有发起人可以撤回
    if (instance.initiatorId !== operatorId) {
      throw new ForbiddenException('只有发起人可以撤回');
    }

    // 检查流程状态
    if (instance.status !== InstanceStatus.RUNNING) {
      throw new BadRequestException('只有运行中的流程可以撤回');
    }

    await this.temporalService.sendSignal(instance.workflowId, 'withdraw', {
      operatorId,
      reason: dto.reason,
    });

    // ⭐ 创建流程操作节点实例（撤回作为独立的流程级操作）
    const now = new Date();
    const processActionNode = await this.prisma.approvalNodeInstance.create({
      data: {
        instanceId,
        nodeId: 'WITHDRAW_ACTION',           // 虚拟节点ID
        nodeName: '撤回',                     // 节点名称
        nodeType: 'PROCESS_ACTION',          // 流程操作类型
        status: 'COMPLETED',                 // 立即完成
        assignees: [operatorId],
        approvalMode: null,
        executionCount: 1,
        startTime: now,
        endTime: now,
      },
    });

    // 创建流程操作任务
    const processActionTask = await this.prisma.approvalTask.create({
      data: {
        instanceId,
        nodeInstanceId: processActionNode.id,
        name: '撤回申请',
        type: 'USER_TASK',
        assignee: operatorId,                // 操作人是发起人
        candidateUsers: [],
        candidateGroups: [],
        status: ApprovalTaskStatus.COMPLETED,
        createTime: now,
        endTime: now,
      },
    });

    // 记录操作日志
    await this.createActionLog(processActionTask.id, instanceId, 'WITHDRAW', operatorId, {
      comment: dto.reason,
    });

    return {
      success: true,
      instanceId,
      status: 'WITHDRAWN',  // ⭐ 修复：撤回应该返回 WITHDRAWN 状态
      endReason: 'WITHDRAWN',
    };
  }

  async approverWithdraw(instanceId: string, dto: ApproverWithdrawDto, operatorId: string): Promise<ApprovalActionResponse> {
    const task = await this.getTaskWithValidation(dto.taskId, operatorId);
    
    // 检查是否是审批人撤回（已完成的任务）
    const completedTask = await this.prisma.approvalTask.findFirst({
      where: {
        instanceId,
        assignee: operatorId,
        status: ApprovalTaskStatus.COMPLETED,
      },
      orderBy: { endTime: 'desc' },
    });

    if (!completedTask) {
      throw new ForbiddenException('没有可撤回的审批操作');
    }

    await this.temporalService.sendSignal(task.instance.workflowId, 'approverWithdraw', {
      taskId: dto.taskId,
      operatorId,
      reason: dto.reason,
    });

    await this.createActionLog(dto.taskId, instanceId, 'APPROVER_WITHDRAW', operatorId, {
      comment: dto.reason,
    });

    return {
      success: true,
      taskId: dto.taskId,
      action: 'APPROVER_WITHDRAW',
      isProcessCompleted: false,
    };
  }

  async addSign(instanceId: string, dto: AddSignDto, operatorId: string): Promise<ApprovalActionResponse> {
    const task = await this.getTaskWithValidation(dto.taskId, operatorId);
    
    this.validateTaskForAction(task, 'ADD_SIGN');

    await this.temporalService.sendSignal(task.instance.workflowId, 'addSign', {
          taskId: dto.taskId,
      operatorId,
      userIds: dto.userIds,
      type: dto.type,
          comment: dto.comment,
    });

    await this.createActionLog(dto.taskId, instanceId, 'ADD_SIGN', operatorId, {
      comment: dto.comment,
      addSignUsers: dto.userIds,
    });

    return {
      success: true,
        taskId: dto.taskId,
        action: 'ADD_SIGN',
      isProcessCompleted: false,
    };
  }

  async claim(instanceId: string, dto: ClaimDto, operatorId: string): Promise<ApprovalActionResponse> {
    const task = await this.prisma.approvalTask.findUnique({
      where: { id: dto.taskId },
      include: { instance: true },
    });

    if (!task) {
      throw new NotFoundException('任务不存在');
    }

    // 检查是否在候选人列表中
    if (!task.candidateUsers.includes(operatorId) && !task.candidateGroups.length) {
      throw new ForbiddenException('您不在候选人列表中');
    }

    // 检查任务状态
    if (task.status !== ApprovalTaskStatus.PENDING && task.status !== ApprovalTaskStatus.CREATED) {
      throw new BadRequestException('任务已被认领');
    }

    await this.temporalService.sendSignal(task.instance.workflowId, 'claim', {
          taskId: dto.taskId,
      operatorId,
    });

    // 更新任务状态
    await this.prisma.approvalTask.update({
      where: { id: dto.taskId },
      data: {
        assignee: operatorId,
        status: ApprovalTaskStatus.CLAIMED,
        claimTime: new Date(),
      },
    });

    await this.createActionLog(dto.taskId, instanceId, 'CLAIM', operatorId, {});

    return {
      success: true,
      taskId: dto.taskId,
      action: 'CLAIM',
      isProcessCompleted: false,
    };
  }

  async unclaim(instanceId: string, dto: UnclaimDto, operatorId: string): Promise<ApprovalActionResponse> {
    const task = await this.getTaskWithValidation(dto.taskId, operatorId);

    if (task.status !== ApprovalTaskStatus.CLAIMED) {
      throw new BadRequestException('任务未被认领');
    }

    // 更新任务状态
    await this.prisma.approvalTask.update({
      where: { id: dto.taskId },
      data: {
        assignee: null,
        status: ApprovalTaskStatus.PENDING,
        claimTime: null,
      },
    });

    await this.createActionLog(dto.taskId, instanceId, 'UNCLAIM', operatorId, {});

    return {
      success: true,
      taskId: dto.taskId,
      action: 'UNCLAIM',
      isProcessCompleted: false,
    };
  }

  async execute(instanceId: string, dto: ExecuteDto, operatorId: string): Promise<ApprovalActionResponse> {
    const task = await this.getTaskWithValidation(dto.taskId, operatorId);
    
    this.validateTaskForAction(task, 'EXECUTE');

    await this.temporalService.sendSignal(task.instance.workflowId, 'execute', {
      taskId: dto.taskId,
      operatorId,
      formData: dto.formData,
      comment: dto.comment,
      attachments: dto.attachments,
    });

    await this.createActionLog(dto.taskId, instanceId, 'EXECUTE', operatorId, {
      comment: dto.comment,
      formData: dto.formData,
      attachments: dto.attachments,
    });

    return {
      success: true,
      taskId: dto.taskId,
      action: 'EXECUTE',
      isProcessCompleted: false,
    };
  }

  // ==================== 任务查询 ====================

  async getMyPendingTasks(userId: string, query: PendingTasksQueryDto): Promise<PaginatedTasksResponse> {
    if (!userId) {
      throw new BadRequestException('用户 ID 不能为空');
    }

    // ⭐ 构建 instance 查询条件
    const instanceWhere: Prisma.ApprovalInstanceWhereInput = {
      status: InstanceStatus.RUNNING, // 只查询正在运行的流程
    };

    if (query.businessType) {
      instanceWhere.businessType = query.businessType;
    }

    const where: Prisma.ApprovalTaskWhereInput = {
      OR: [
        { assignee: userId },
        ...(userId ? [{ candidateUsers: { has: userId } }] : []),
      ],
      status: { in: [ApprovalTaskStatus.PENDING, ApprovalTaskStatus.CLAIMED, ApprovalTaskStatus.ASSIGNED] },
      instance: instanceWhere, // ⭐ 修复：只查询正在运行的流程实例的任务
    };

    if (query.status) {
      where.status = query.status;
    }

    if (query.priority !== undefined) {
      where.priority = query.priority;
    }

    if (query.isOverdue) {
      where.dueDate = { lt: new Date() };
    }

    if (query.isDelegated !== undefined) {
      where.isDelegated = query.isDelegated;
    }

    // 排序
    let orderBy: Prisma.ApprovalTaskOrderByWithRelationInput = { createTime: 'desc' };
    if (query.sortBy) {
      switch (query.sortBy) {
        case 'dueDate':
          orderBy = { dueDate: query.sortOrder || 'asc' };
          break;
        case 'priority':
          orderBy = { priority: query.sortOrder || 'desc' };
          break;
        case 'lastReminderTime':
          orderBy = { lastReminderAt: query.sortOrder || 'desc' };
          break;
        case 'remainingHours':
          orderBy = { dueDate: 'asc' }; // 按截止时间升序
          break;
        default:
          orderBy = { createTime: query.sortOrder || 'desc' };
      }
    }

    const [tasks, total] = await Promise.all([
      this.prisma.approvalTask.findMany({
        where,
        include: {
          instance: {
            include: {
              initiator: true,
            },
          },
          nodeInstance: true,
        },
        orderBy,
        skip: ((query.page || 1) - 1) * (query.limit || 20),
        take: query.limit || 20,
      }),
      this.prisma.approvalTask.count({ where }),
    ]);

    const items: ApprovalTaskItem[] = await Promise.all(
      tasks.map(async (task) => {
        const delegatedFromUser = task.delegatedFrom
          ? (await this.getUserInfo(task.delegatedFrom)) ?? undefined
          : undefined;

        // 从 variables 中提取表单相关字段
        const variables = task.instance.variables as Record<string, any> || {};
        const formKey = variables.formKey;
        const formDefinitionId = variables.formDefinitionId;
        const formVersionId = variables.formVersionId;

    return {
          taskId: task.id,
          instanceId: task.instanceId,
          businessType: task.instance.businessType,
          businessId: task.instance.businessId,
          businessKey: task.instance.businessKey,
          title: task.instance.title || task.instance.businessKey,  // ⭐ 修复：使用实例标题而不是任务名称
          status: task.status as any,
          priority: task.priority,
          initiator: this.mapUserInfo(task.instance.initiator),
          nodeName: task.nodeInstance.nodeName,
          nodeType: task.nodeInstance.nodeType,
          approvalMode: this.normalizeApprovalMode(task.nodeInstance.approvalMode) as any,  // ⭐ 标准化审批模式
          createTime: task.createTime.toISOString(),
          dueDate: task.dueDate?.toISOString(),
          isOverdue: task.dueDate ? task.dueDate < new Date() : false,
          remainingHours: task.dueDate
            ? Math.max(0, (task.dueDate.getTime() - Date.now()) / (1000 * 60 * 60))
            : undefined,
          isDelegated: task.isDelegated,
          delegatedFrom: delegatedFromUser,
          delegatedAt: task.delegatedAt?.toISOString(),
          delegationReason: task.delegationReason || undefined,
          lastReminderTime: task.lastReminderAt?.toISOString(),
          reminderCount: task.reminderCount,
          // 表单相关字段（仅表单类型审批有值）
          formKey,
          formDefinitionId,
          formVersionId,
        };
      }),
    );

    const totalPages = Math.ceil(total / (query.limit || 20));

    return {
      items,
      total,
      page: query.page || 1,
      limit: query.limit || 20,
      totalPages,
      hasNext: (query.page || 1) < totalPages,
      hasPrev: (query.page || 1) > 1,
    };
  }

  async getMyInitiated(userId: string | undefined, query: InitiatedQueryDto): Promise<PaginatedTasksResponse> {
    const where: Prisma.ApprovalInstanceWhereInput = {};
    
    // 如果指定了 userId，只查询该用户的流程；否则查询所有流程（管理员）
    if (userId) {
      where.initiatorId = userId;
    }

    if (query.businessType) {
      where.businessType = query.businessType;
    }

    if (query.status) {
      where.status = query.status as any;
    }

    if (query.startTimeFrom || query.startTimeTo) {
      where.startTime = {};
      if (query.startTimeFrom) {
        where.startTime.gte = new Date(query.startTimeFrom);
      }
      if (query.startTimeTo) {
        where.startTime.lte = new Date(query.startTimeTo);
      }
    }

    const [instances, total] = await Promise.all([
      this.prisma.approvalInstance.findMany({
        where,
        include: {
          initiator: true,
          nodeInstances: {
            orderBy: { startTime: 'desc' },
            // ⭐ 不限制数量，查询所有nodeInstance，因为pending task可能在任何一个节点
            include: {
              approvalTasks: {
                where: {
                  status: { in: ['PENDING', 'ASSIGNED', 'CLAIMED'] }, // 查询所有待处理的任务
                },
                take: 1, // 每个节点只取1个pending task即可
              },
            },
          },
        },
        orderBy: { startTime: 'desc' },
        skip: ((query.page || 1) - 1) * (query.limit || 20),
        take: query.limit || 20,
      }),
      this.prisma.approvalInstance.count({ where }),
    ]);

    // 批量获取业务摘要
    const businessSummaries = await this.businessTypeRegistry.getBatchSummaries(
      instances.map((i) => ({ businessType: i.businessType, businessId: i.businessId })),
    );

    const items: ApprovalTaskItem[] = instances.map((instance, index) => {
      const { summary, metadata } = businessSummaries[index];

      console.log('[getMyInitiated] Processing instance:', {
        id: instance.id,
        title: instance.title,
        status: instance.status,
        nodeInstancesCount: instance.nodeInstances?.length || 0,
      });

      // 获取当前节点名称：优先从 currentNode（JSON字段），否则从最新的 nodeInstance
      let nodeName = '';
      let nodeType = '';
      if (instance.currentNode && typeof instance.currentNode === 'object') {
        const currentNodeData = instance.currentNode as any;
        nodeName = currentNodeData.name || currentNodeData.nodeName || '';
        nodeType = currentNodeData.type || currentNodeData.nodeType || '';
      } else if (instance.nodeInstances && instance.nodeInstances.length > 0) {
        nodeName = instance.nodeInstances[0]?.nodeName || '';
        nodeType = instance.nodeInstances[0]?.nodeType || '';
      }

      // 从 variables 中提取表单相关字段
      const variables = instance.variables as Record<string, any> || {};
      const formKey = variables.formKey;
      const formDefinitionId = variables.formDefinitionId;
      const formVersionId = variables.formVersionId;

      // 获取当前pending的taskId（从所有nodeInstance中找）
      let currentTaskId = '';
      if (instance.nodeInstances && instance.nodeInstances.length > 0) {
        // 遍历所有nodeInstance，找到第一个有pending task的
        for (const nodeInstance of instance.nodeInstances) {
          console.log('[getMyInitiated] Checking nodeInstance:', {
            nodeId: nodeInstance.nodeId,
            nodeName: nodeInstance.nodeName,
            nodeStatus: nodeInstance.status,
            hasApprovalTasks: !!nodeInstance.approvalTasks,
            approvalTasksCount: nodeInstance.approvalTasks?.length || 0,
          });
          
          if (nodeInstance.approvalTasks && nodeInstance.approvalTasks.length > 0) {
            currentTaskId = nodeInstance.approvalTasks[0].id;
            console.log('[getMyInitiated] ✅ Found pending taskId:', currentTaskId, 'in node:', nodeInstance.nodeName);
            break; // 找到第一个pending task就退出
          }
        }
        
        if (!currentTaskId) {
          console.log('[getMyInitiated] ⚠️ No pending tasks found for instance:', instance.id);
        }
      } else {
        console.log('[getMyInitiated] ⚠️ No nodeInstances found for instance:', instance.id);
      }

      return {
        taskId: currentTaskId, // ⭐ 修复：返回当前pending的taskId
        instanceId: instance.id,
        businessType: instance.businessType,
        businessId: instance.businessId,
        businessKey: instance.businessKey,
        title: instance.title || summary?.title || instance.businessKey,  // ⭐ 修复：优先使用instance.title
        status: instance.status as any,
        priority: instance.priority,
        initiator: this.mapUserInfo(instance.initiator),
        nodeName,
        nodeType,
        createTime: instance.startTime.toISOString(),
        dueDate: instance.dueDate?.toISOString(),
        isOverdue: instance.dueDate ? instance.dueDate < new Date() : false,
        isDelegated: false,

        // 表单相关字段（仅表单类型审批有值）
        formKey,
        formDefinitionId,
        formVersionId,

        // 业务类型元数据
        businessTypeMeta: metadata
          ? {
              displayName: metadata.displayName,
              icon: metadata.icon,
              color: metadata.color,
            }
          : undefined,

        // 业务摘要
        businessSummary: summary || undefined,
      } as any;
    });

    const totalPages = Math.ceil(total / (query.limit || 20));

    return {
      items,
      total,
      page: query.page || 1,
      limit: query.limit || 20,
      totalPages,
      hasNext: (query.page || 1) < totalPages,
      hasPrev: (query.page || 1) > 1,
    };
  }

  async getMyProcessed(userId: string, query: ProcessedQueryDto): Promise<PaginatedTasksResponse> {
    const where: Prisma.ApprovalTaskLogWhereInput = {
        operatorId: userId,
      action: {
        // ⭐ 修复：转交（FORWARD）不算"已审批"，只有真正的审批操作才算
        // ⭐ 新增：包含管理员代审批操作（ADMIN_APPROVE, ADMIN_REJECT）
        in: ['APPROVE', 'REJECT', 'RETURN', 'ADMIN_APPROVE', 'ADMIN_REJECT'],
      },
    };

    if (query.action) {
      where.action = query.action as any;
    }

    if (query.actionTimeFrom || query.actionTimeTo) {
      where.actionTime = {};
      if (query.actionTimeFrom) {
        where.actionTime.gte = new Date(query.actionTimeFrom);
      }
      if (query.actionTimeTo) {
        where.actionTime.lte = new Date(query.actionTimeTo);
      }
    }

      const [logs, total] = await Promise.all([
        this.prisma.approvalTaskLog.findMany({
          where,
          include: {
            task: {
              include: {
                instance: {
                  include: {
                  initiator: true,
                  },
                },
                nodeInstance: true,
              },
            },
          },
        orderBy: { actionTime: 'desc' },
        skip: ((query.page || 1) - 1) * (query.limit || 20),
        take: query.limit || 20,
        }),
        this.prisma.approvalTaskLog.count({ where }),
      ]);

    // 按 taskId 去重，保留每个任务的最后一次操作
    const taskMap = new Map<string, typeof logs[0]>();
    for (const log of logs) {
      if (!taskMap.has(log.taskId)) {
        taskMap.set(log.taskId, log);
      }
    }
    const uniqueLogs = Array.from(taskMap.values());

    const items: ApprovalTaskItem[] = uniqueLogs.map((log) => {
      // 从 variables 中提取表单相关字段
      const variables = log.task.instance.variables as Record<string, any> || {};
      const formKey = variables.formKey;
      const formDefinitionId = variables.formDefinitionId;
      const formVersionId = variables.formVersionId;

      return {
        taskId: log.taskId,
        instanceId: log.instanceId,
        businessType: log.task.instance.businessType,
        businessId: log.task.instance.businessId,
        businessKey: log.task.instance.businessKey,
        title: log.task.instance.title || log.task.instance.businessKey,  // ⭐ 修复：使用实例标题
        status: log.task.instance.status as any,  // ⭐ 修复：使用流程状态而不是任务状态
        priority: log.task.priority,
        initiator: this.mapUserInfo(log.task.instance.initiator),
        nodeName: log.task.nodeInstance.nodeName,
        nodeType: log.task.nodeInstance.nodeType,
        createTime: log.task.createTime.toISOString(),
        isOverdue: false,
        isDelegated: log.task.isDelegated,
        // 表单相关字段（仅表单类型审批有值）
        formKey,
        formDefinitionId,
        formVersionId,
      };
    });

    const totalPages = Math.ceil(total / (query.limit || 20));

        return {
      items,
      total,
      page: query.page || 1,
      limit: query.limit || 20,
      totalPages,
      hasNext: (query.page || 1) < totalPages,
      hasPrev: (query.page || 1) > 1,
    };
  }

  async getMyCc(userId: string, query: CcQueryDto): Promise<PaginatedTasksResponse> {
    // CC 任务暂时使用特殊任务类型查询
    const where: Prisma.ApprovalTaskWhereInput = {
      type: 'CC',
      candidateUsers: { has: userId },
    };

    if (query.businessType) {
      where.instance = { businessType: query.businessType };
    }

    const [tasks, total] = await Promise.all([
      this.prisma.approvalTask.findMany({
        where,
      include: {
          instance: {
          include: {
              initiator: true,
            },
          },
          nodeInstance: true,
        },
        orderBy: { createTime: 'desc' },
        skip: ((query.page || 1) - 1) * (query.limit || 20),
        take: query.limit || 20,
      }),
      this.prisma.approvalTask.count({ where }),
    ]);

    const items: ApprovalTaskItem[] = tasks.map((task) => {
      // 从 variables 中提取表单相关字段
      const variables = task.instance.variables as Record<string, any> || {};
      const formKey = variables.formKey;
      const formDefinitionId = variables.formDefinitionId;
      const formVersionId = variables.formVersionId;

      return {
        taskId: task.id,
        instanceId: task.instanceId,
        businessType: task.instance.businessType,
        businessId: task.instance.businessId,
        businessKey: task.instance.businessKey,
        title: task.instance.title || task.instance.businessKey,  // ⭐ 修复：使用实例标题
        status: task.status as any,
        priority: task.priority,
        initiator: this.mapUserInfo(task.instance.initiator),
        nodeName: task.nodeInstance.nodeName,
        nodeType: task.nodeInstance.nodeType,
        createTime: task.createTime.toISOString(),
        isOverdue: false,
        isDelegated: false,
        // 表单相关字段（仅表单类型审批有值）
        formKey,
        formDefinitionId,
        formVersionId,
      };
    });

    const totalPages = Math.ceil(total / (query.limit || 20));

        return {
      items,
      total,
      page: query.page || 1,
      limit: query.limit || 20,
      totalPages,
      hasNext: (query.page || 1) < totalPages,
      hasPrev: (query.page || 1) > 1,
    };
  }

  async getMyStats(userId: string): Promise<TaskStatsResponse> {
    if (!userId) {
      return {
        pendingCount: 0,
        urgentCount: 0,
        overdueCount: 0,
        initiatedCount: 0,
        processedToday: 0,
        processedThisWeek: 0,
        ccUnreadCount: 0,
      };
    }

    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const weekAgo = new Date(today);
    weekAgo.setDate(weekAgo.getDate() - 7);
    
    // 紧急任务：24小时内到期
    const urgentDeadline = new Date();
    urgentDeadline.setHours(urgentDeadline.getHours() + 24);

    const [pendingCount, urgentCount, overdueCount, initiatedCount, processedToday, processedThisWeek, ccUnreadCount] = await Promise.all([
      // 待我处理
      this.prisma.approvalTask.count({
        where: {
          OR: [
            { assignee: userId },
            { candidateUsers: { has: userId } },
          ],
          status: { in: [ApprovalTaskStatus.PENDING, ApprovalTaskStatus.CLAIMED, ApprovalTaskStatus.ASSIGNED] },
        },
      }),
      // 紧急待办（24小时内到期）
      this.prisma.approvalTask.count({
        where: {
          OR: [
            { assignee: userId },
            { candidateUsers: { has: userId } },
          ],
          status: { in: [ApprovalTaskStatus.PENDING, ApprovalTaskStatus.CLAIMED, ApprovalTaskStatus.ASSIGNED] },
          dueDate: { lte: urgentDeadline, gt: new Date() },
        },
      }),
      // 已超时
      this.prisma.approvalTask.count({
        where: {
          OR: [
            { assignee: userId },
            { candidateUsers: { has: userId } },
          ],
          status: { in: [ApprovalTaskStatus.PENDING, ApprovalTaskStatus.CLAIMED, ApprovalTaskStatus.ASSIGNED] },
          dueDate: { lt: new Date() },
        },
      }),
      // 我发起的（所有状态）
      this.prisma.approvalInstance.count({
        where: {
          initiatorId: userId,
          // 不限制状态，统计所有发起的审批
        },
      }),
      // 今日已处理
      this.prisma.approvalTaskLog.count({
        where: {
          operatorId: userId,
          // ⭐ 修复：包含管理员代审批操作
          action: { in: ['APPROVE', 'REJECT', 'ADMIN_APPROVE', 'ADMIN_REJECT'] },
          actionTime: { gte: today },
        },
      }),
      // 本周已处理
      this.prisma.approvalTaskLog.count({
        where: {
          operatorId: userId,
          // ⭐ 修复：包含管理员代审批操作
          action: { in: ['APPROVE', 'REJECT', 'ADMIN_APPROVE', 'ADMIN_REJECT'] },
          actionTime: { gte: weekAgo },
        },
      }),
      // 抄送未读（暂时返回0，待实现抄送功能）
      Promise.resolve(0),
    ]);

    return {
      pendingCount,
      urgentCount,
      overdueCount,
      initiatedCount,
      processedToday,
      processedThisWeek,
      ccUnreadCount,
    };
  }

  async getTaskDetail(taskId: string, userId: string): Promise<TaskDetailResponse> {
    // 验证 UUID 格式
    if (!this.isValidUUID(taskId)) {
      throw new NotFoundException('任务不存在');
    }

    const task = await this.prisma.approvalTask.findUnique({
      where: { id: taskId },
      include: {
        instance: {
          include: {
            initiator: true,
            version: true,
          },
        },
        nodeInstance: true,
      },
    });

    if (!task) {
      throw new NotFoundException('任务不存在');
    }

    // 检查权限
    const hasAccess =
      task.assignee === userId ||
      task.candidateUsers.includes(userId) ||
      task.instance.initiatorId === userId;

    if (!hasAccess) {
      throw new ForbiddenException('无权访问此任务');
    }

    const assigneeInfo = task.assignee
      ? await this.getUserInfo(task.assignee)
      : undefined;

    const candidateUsersInfo = await Promise.all(
      task.candidateUsers.map((id) => this.getUserInfo(id)),
    );

    const delegatedFromInfo = task.delegatedFrom
      ? await this.getUserInfo(task.delegatedFrom)
      : undefined;

    const returnableNodes = await this.getReturnableNodes(task.instanceId, taskId);
    const availableActions = this.getAvailableActions(task, userId);

    // 获取操作日志
    const actionLogs = await this.prisma.approvalTaskLog.findMany({
      where: { taskId },
      include: { operator: true },
      orderBy: { actionTime: 'asc' },
    });

          return {
      id: task.id,
      name: task.name,
      description: task.description || undefined,
      type: task.type as any,
      status: task.status as any,
      assignee: assigneeInfo || undefined,
      candidateUsers: candidateUsersInfo.filter(Boolean) as UserInfo[],
      candidateGroups: task.candidateGroups,
      isDelegated: task.isDelegated,
      delegatedFrom: delegatedFromInfo || undefined,
      delegatedAt: task.delegatedAt?.toISOString(),
      delegationReason: task.delegationReason || undefined,
      delegationType: task.delegationType as any,
      createTime: task.createTime.toISOString(),
      claimTime: task.claimTime?.toISOString(),
      dueDate: task.dueDate?.toISOString(),
      endTime: task.endTime?.toISOString(),
      lastReminderTime: task.lastReminderAt?.toISOString(),
      isOverdue: task.dueDate ? task.dueDate < new Date() : false,
      remainingHours: task.dueDate
        ? Math.round((task.dueDate.getTime() - Date.now()) / 3600000)
        : undefined,
      reminderCount: task.reminderCount,
      version: task.version,
      editableFields: undefined,
      requiredFields: undefined,
      allowedActions: availableActions.filter(a => a.enabled).map(a => a.action),
      returnTargets: returnableNodes,
      instance: {
        id: task.instanceId,
        businessType: task.instance.businessType,
        businessId: task.instance.businessId,
        businessKey: task.instance.businessKey,
        title: task.instance.businessKey,
        status: task.instance.status as any,
        initiator: this.mapUserInfo(task.instance.initiator),
        startTime: task.instance.startTime.toISOString(),
      },
      nodeInstance: {
        id: task.nodeInstance.id,
        nodeId: task.nodeInstance.nodeId,
        nodeName: task.nodeInstance.nodeName,
        nodeType: task.nodeInstance.nodeType as any,
      },
      actionLogs: actionLogs.map(log => ({
        id: log.id,
        action: log.action as any,
        operator: this.mapUserInfo(log.operator),
        comment: log.comment || undefined,
        actionTime: log.actionTime.toISOString(),
        formDataChanges: log.formDataChanges as any,
      })),
    };
  }

  async getReturnableNodes(instanceId: string, taskId: string): Promise<ReturnableNode[]> {
    const nodeInstances = await this.prisma.approvalNodeInstance.findMany({
      where: {
        instanceId,
        status: ApprovalTaskStatus.COMPLETED,
        nodeType: 'USER_TASK',
      },
      orderBy: { startTime: 'asc' },
    });

    return nodeInstances.map((node) => ({
      nodeId: node.nodeId,
      nodeName: node.nodeName,
      nodeType: node.nodeType,
      executedAt: node.endTime?.toISOString(),
    }));
  }

  // ==================== 流程查询 ====================

  async getProcessDetail(instanceId: string): Promise<ProcessDetailResponse> {
    // 验证 UUID 格式
    if (!this.isValidUUID(instanceId)) {
      throw new NotFoundException('流程实例不存在');
    }

    const instance = await this.prisma.approvalInstance.findUnique({
      where: { id: instanceId },
      include: {
        initiator: true,
        version: true,
        nodeInstances: {
          where: { status: 'ACTIVE' },
          take: 1,
        },
      },
    });

    if (!instance) {
      throw new NotFoundException('流程实例不存在');
    }

    // 获取流程定义信息
    const definition = await this.prisma.approvalDefinition.findFirst({
      where: { versions: { some: { id: instance.versionId } } },
    });

    // 获取节点实例
    const nodeInstances = await this.prisma.approvalNodeInstance.findMany({
      where: { instanceId },
      include: {
        approvalTasks: true,
      },
      orderBy: { startTime: 'asc' },
    });

    // 获取待处理任务
    const pendingTasks = await this.prisma.approvalTask.findMany({
      where: {
        instanceId,
        status: { in: ['PENDING', 'CLAIMED', 'ASSIGNED'] },
      },
      include: {
        instance: { include: { initiator: true } },
        nodeInstance: true,
      },
    });

    // 获取审批人信息
    const assigneeIds = [...new Set(nodeInstances.flatMap(ni => ni.approvalTasks.map(t => t.assignee).filter(Boolean)))];
    const assigneeUsers = await this.prisma.user.findMany({
      where: { id: { in: assigneeIds as string[] } },
    });
    const assigneeMap = new Map(assigneeUsers.map(u => [u.id, u]));

            return {
      id: instance.id,
      businessType: instance.businessType,
      businessId: instance.businessId,
      businessKey: instance.businessKey,
      title: instance.businessKey,
      processDefinition: {
        id: definition?.id || '',
        key: definition?.key || '',
        name: definition?.name || '',
        category: definition?.category || '',
      },
      processVersion: {
        id: instance.version.id,
        version: instance.version.version,
        name: instance.version.name,
      },
      workflowId: instance.workflowId,
      workflowRunId: instance.workflowRunId,
      initiator: this.mapUserInfo(instance.initiator),
      status: instance.status as any,
      currentNodeId: instance.nodeInstances[0]?.nodeId,
      currentNodeName: instance.nodeInstances[0]?.nodeName,
      variables: instance.variables as any,
      totalNodeExecutions: instance.totalNodeExecutions,
      startTime: instance.startTime.toISOString(),
      endTime: instance.endTime?.toISOString(),
      endReason: instance.endReason || undefined,
      endComment: instance.endComment || undefined,
      priority: instance.priority,
      dueDate: instance.dueDate?.toISOString(),
      nodeInstances: nodeInstances.map(ni => ({
        id: ni.id,
        nodeId: ni.nodeId,
        nodeName: ni.nodeName,
        nodeType: ni.nodeType as any,
        status: ni.status as any,
        assignees: ni.approvalTasks
          .map(t => t.assignee ? this.mapUserInfo(assigneeMap.get(t.assignee)) : null)
          .filter(Boolean) as UserInfo[],
        approvalMode: ni.approvalMode as any,
        executionCount: ni.executionCount,
        result: ni.result as string | undefined,
        startTime: ni.startTime.toISOString(),
        endTime: ni.endTime?.toISOString(),
        tasks: [],
      })),
      pendingTasks: pendingTasks.map(t => this.mapTaskToItemFromDb(t)),
    };
  }

  private mapTaskToItemFromDb(task: any): ApprovalTaskItem {
    // 从 variables 中提取表单相关字段
    const variables = task.instance?.variables as Record<string, any> || {};
    const formKey = variables.formKey;
    const formDefinitionId = variables.formDefinitionId;
    const formVersionId = variables.formVersionId;

    return {
      taskId: task.id,
      instanceId: task.instanceId,
      businessType: task.instance?.businessType || '',
      businessId: task.instance?.businessId || '',
      businessKey: task.instance?.businessKey || '',
      title: task.instance?.title || task.instance?.businessKey || '',  // 优先使用 title 字段
              status: task.status,
      priority: task.instance?.priority || 0,
      initiator: task.instance?.initiator ? this.mapUserInfo(task.instance.initiator) : { id: '', name: '' },
      nodeName: task.nodeInstance?.nodeName || task.name || '',
      nodeType: task.nodeInstance?.nodeType || 'USER_TASK',
      approvalMode: this.normalizeApprovalMode(task.nodeInstance?.approvalMode) as any,  // ⭐ 标准化审批模式
      createTime: task.createTime.toISOString(),
      dueDate: task.dueDate?.toISOString(),
      isOverdue: task.dueDate ? new Date() > task.dueDate : false,
      remainingHours: task.dueDate ? Math.round((task.dueDate.getTime() - Date.now()) / 3600000) : undefined,
      isDelegated: !!task.delegatedFrom,
      delegatedFrom: task.delegatedFrom ? { id: task.delegatedFrom, name: '' } : undefined,
      delegatedAt: task.delegatedAt?.toISOString(),
      delegationReason: task.delegationReason || undefined,
      lastReminderTime: task.lastReminderTime?.toISOString(),
      reminderCount: task.reminderCount || 0,
      // 表单相关字段（仅表单类型审批有值）
      formKey,
      formDefinitionId,
      formVersionId,
    };
  }

  async getProcessStatus(instanceId: string): Promise<ProcessStatusResponse> {
    const instance = await this.prisma.approvalInstance.findUnique({
      where: { id: instanceId },
      include: {
        nodeInstances: true,
        approvalTasks: {
          where: { status: { in: ['PENDING', 'CLAIMED', 'ASSIGNED'] } },
          include: {
            nodeInstance: true,
          },
        },
      },
    });

    if (!instance) {
      throw new NotFoundException('流程实例不存在');
    }

    const completedNodes = instance.nodeInstances.filter(
      (n: any) => n.status === NodeStatus.COMPLETED,
    ).length;
    const totalNodes = instance.nodeInstances.length || 1;
    const progress = Math.round((completedNodes / totalNodes) * 100);

    const pendingApprovers = await Promise.all(
      instance.approvalTasks
        .filter((t) => t.assignee)
        .map((t) => this.getUserInfo(t.assignee!)),
    );

    const currentNode = (instance as any).nodeInstances?.find((n: any) => n.status === NodeStatus.ACTIVE);

    return {
        id: instance.id,
      status: instance.status as any,
      currentNodeId: currentNode?.nodeId,
      currentNodeName: currentNode?.nodeName,
      currentAssignees: pendingApprovers.filter(Boolean) as UserInfo[],
      startTime: instance.startTime.toISOString(),
      endTime: instance.endTime?.toISOString(),
      endReason: instance.endReason || undefined,
      progress,
    };
  }

  async getProcessHistory(instanceId: string): Promise<ApprovalHistoryResponse> {
    // ⭐ 查询流程实例（用于返回最新状态和终止原因）
    const instance = await this.prisma.approvalInstance.findUnique({
      where: { id: instanceId },
      select: { 
        status: true,
        endReason: true,  // ⭐ 添加：返回撤回/拒绝/终止的原因
      },
    });

    if (!instance) {
      throw new NotFoundException(`Approval instance not found: ${instanceId}`);
    }

    // 获取节点实例及其任务
    const nodeInstances = await this.prisma.approvalNodeInstance.findMany({
      where: { instanceId },
      include: {
        approvalTasks: {
          include: {
            actionLogs: {
              include: {
                operator: true,
                // ⭐ 加载目标用户信息（用于转交、退回等操作）
                targetUser: {
                  select: {
                    id: true,
                    username: true,
                    displayName: true,
                    email: true,
                    avatar: true,
                  },
                },
              },
              orderBy: { actionTime: 'asc' },
            },
          },
        },
      },
      orderBy: { startTime: 'asc' },
    });

    // 获取审批人信息（包括当前审批人和原审批人）
    const assigneeIds = [...new Set(nodeInstances.flatMap((ni: any) => 
      ni.approvalTasks.flatMap((t: any) => [t.assignee, t.delegatedFrom].filter(Boolean))
    ))];
    const assigneeUsers = await this.prisma.user.findMany({
      where: { id: { in: assigneeIds as string[] } },
    });
    const assigneeMap = new Map(assigneeUsers.map(u => [u.id, u]));

    // 按 API 文档格式构建响应
    const items = nodeInstances.map((node: any) => ({
      nodeId: node.nodeId,
      nodeName: node.nodeName,
      nodeType: node.nodeType as any,
      status: node.status as any,
      startTime: node.startTime.toISOString(),
      endTime: node.endTime?.toISOString(),
      tasks: node.approvalTasks.map((task: any) => ({
        id: task.id,
        assignee: task.assignee ? this.mapUserInfo(assigneeMap.get(task.assignee)) : undefined,
        status: task.status as any,
        autoApproved: task.actionLogs.some((l: any) => l.action === 'AUTO_APPROVE'),
        autoApproveReason: task.actionLogs.find((l: any) => l.action === 'AUTO_APPROVE')?.comment || undefined,
        // ⭐ 添加委托/重新分配信息
        isDelegated: task.isDelegated || false,
        delegatedFrom: task.delegatedFrom ? this.mapUserInfo(assigneeMap.get(task.delegatedFrom)) : undefined,
        delegationReason: task.delegationReason || undefined,
        actions: task.actionLogs.map((log: any) => ({
          id: log.id,
          action: log.action as any,
          operator: this.mapUserInfo(log.operator),
          // ⭐ 添加目标用户信息（转交、退回等操作）
          targetUser: log.targetUser ? this.mapUserInfo(log.targetUser) : undefined,
          comment: log.comment || undefined,
          actionTime: log.actionTime.toISOString(),
          formDataChanges: log.formDataChanges as any,
        })),
      })),
    }));

    // ⭐ 返回流程实例的最新状态和终止原因（用于前端同步更新列表项和显示原因）
    return { 
      items,
      status: instance.status as any,
      endReason: instance.endReason || undefined,  // ⭐ 添加：撤回/拒绝/终止的原因
    };
  }

  async getProcessByBusiness(businessType: string, businessId: string): Promise<ProcessDetailResponse> {
    const instance = await this.prisma.approvalInstance.findFirst({
      where: {
        businessType,
        businessId,
      },
    });

    if (!instance) {
      throw new NotFoundException('流程实例不存在');
    }

    return this.getProcessDetail(instance.id);
  }

  async searchProcesses(query: ProcessSearchDto): Promise<PaginatedTasksResponse> {
    const where: Prisma.ApprovalInstanceWhereInput = {};

    if (query.keyword) {
      where.OR = [
        { businessKey: { contains: query.keyword, mode: 'insensitive' } },
        { businessId: { contains: query.keyword, mode: 'insensitive' } },
      ];
    }

    if (query.businessType) {
      where.businessType = query.businessType;
    }

    if (query.status) {
      where.status = query.status as any;
    }

    if (query.initiatorId) {
      where.initiatorId = query.initiatorId;
    }

    if (query.startTimeFrom || query.startTimeTo) {
      where.startTime = {};
      if (query.startTimeFrom) {
        where.startTime.gte = new Date(query.startTimeFrom);
      }
      if (query.startTimeTo) {
        where.startTime.lte = new Date(query.startTimeTo);
      }
    }

    const [instances, total] = await Promise.all([
      this.prisma.approvalInstance.findMany({
        where,
        include: {
          initiator: true,
          nodeInstances: {
            where: { status: NodeStatus.ACTIVE },
            take: 1,
          },
        },
        orderBy: { startTime: 'desc' },
        skip: ((query.page || 1) - 1) * (query.limit || 20),
        take: query.limit || 20,
      }),
      this.prisma.approvalInstance.count({ where }),
    ]);

    const items: ApprovalTaskItem[] = instances.map((instance) => {
      // 从 variables 中提取表单相关字段
      const variables = instance.variables as Record<string, any> || {};
      const formKey = variables.formKey;
      const formDefinitionId = variables.formDefinitionId;
      const formVersionId = variables.formVersionId;

      return {
        taskId: '',
        instanceId: instance.id,
        businessType: instance.businessType,
        businessId: instance.businessId,
        businessKey: instance.businessKey,
        title: instance.businessKey,
        status: instance.status as any,
        priority: instance.priority,
        initiator: this.mapUserInfo(instance.initiator),
        nodeName: instance.nodeInstances[0]?.nodeName || '',
        nodeType: instance.nodeInstances[0]?.nodeType || '',
        createTime: instance.startTime.toISOString(),
        isOverdue: false,
        isDelegated: false,
        // 表单相关字段（仅表单类型审批有值）
        formKey,
        formDefinitionId,
        formVersionId,
      };
    });

    const totalPages = Math.ceil(total / (query.limit || 20));

      return {
      items,
      total,
      page: query.page || 1,
      limit: query.limit || 20,
      totalPages,
      hasNext: (query.page || 1) < totalPages,
      hasPrev: (query.page || 1) > 1,
    };
  }

  // ==================== 流程图数据 ====================

  async getDiagram(instanceId: string): Promise<DiagramResponse> {
    const instance = await this.prisma.approvalInstance.findUnique({
      where: { id: instanceId },
      include: {
        version: true,
        nodeInstances: {
          include: {
            approvalTasks: {
              where: { status: { in: [ApprovalTaskStatus.PENDING, ApprovalTaskStatus.CLAIMED, ApprovalTaskStatus.ASSIGNED] } },
          },
        },
      },
      },
    });

    if (!instance) {
      throw new NotFoundException('流程实例不存在');
    }

    const processModel = instance.version.processModel as any;
    const nodes = processModel.nodes || [];
    const edges = processModel.edges || [];

    const diagramNodes = await Promise.all(
      nodes.map(async (node: any) => {
        const nodeInstance = instance.nodeInstances.find(
          (ni) => ni.nodeId === node.id,
        );

        const assignees = nodeInstance?.approvalTasks?.length
          ? await Promise.all(
              nodeInstance.approvalTasks
                .filter((t) => t.assignee)
                .map((t) => this.getUserInfo(t.assignee!)),
            )
          : [];

        return {
          id: node.id,
          type: node.type,
          name: node.name,
          status: nodeInstance?.status || 'PENDING',
          x: node.position?.x || 0,
          y: node.position?.y || 0,
          width: node.size?.width || 100,
          height: node.size?.height || 60,
          assignees: assignees.filter(Boolean) as UserInfo[],
          startTime: nodeInstance?.startTime?.toISOString(),
          endTime: nodeInstance?.endTime?.toISOString(),
          isCurrent: nodeInstance?.status === NodeStatus.ACTIVE,
        };
      }),
    );

    const activeNodeIds = instance.nodeInstances
      .filter((ni: any) => ni.status === NodeStatus.ACTIVE || ni.status === NodeStatus.COMPLETED)
      .map((ni) => ni.nodeId);

    const diagramEdges = edges.map((edge: any) => ({
      id: edge.id,
      source: edge.source,
      target: edge.target,
      label: edge.label,
      isActive:
        activeNodeIds.includes(edge.source) || activeNodeIds.includes(edge.target),
    }));

    const currentNode = (instance as any).nodeInstances?.find((ni: any) => ni.status === NodeStatus.ACTIVE);

    return {
      instanceId,
      nodes: diagramNodes,
      edges: diagramEdges,
      currentNodeId: currentNode?.nodeId,
    };
  }

  async getDiagramByBusiness(businessType: string, businessId: string): Promise<DiagramResponse> {
    const instance = await this.prisma.approvalInstance.findFirst({
      where: {
        businessType,
        businessId,
      },
    });

    if (!instance) {
      throw new NotFoundException('流程实例不存在');
    }

    return this.getDiagram(instance.id);
  }

  // ==================== 催办 ====================

  async remind(instanceId: string, dto: RemindDto, operatorId: string): Promise<RemindResponse> {
    const task = await this.prisma.approvalTask.findUnique({
      where: { id: dto.taskId },
      include: { instance: true },
    });

    if (!task) {
      throw new NotFoundException('任务不存在');
    }

    // 只有发起人可以催办
    if (task.instance.initiatorId !== operatorId) {
      throw new ForbiddenException('只有发起人可以催办');
    }

    // 发送催办 Signal
    await this.temporalService.sendSignal(task.instance.workflowId, 'remind', {
      taskId: dto.taskId,
      operatorId,
      message: dto.message,
    });

    // 更新催办计数
    const updatedTask = await this.prisma.approvalTask.update({
      where: { id: dto.taskId },
      data: {
        reminderCount: { increment: 1 },
        lastReminderAt: new Date(),
      },
    });

    await this.createActionLog(dto.taskId, instanceId, 'REMIND', operatorId, {
      comment: dto.message,
    });

    return {
      success: true,
      taskId: dto.taskId,
      reminderCount: updatedTask.reminderCount,
      lastReminderTime: updatedTask.lastReminderAt!.toISOString(),
      message: '催办成功',
    };
  }

  async batchRemind(instanceId: string, dto: BatchRemindDto, operatorId: string): Promise<BatchRemindResponse> {
    const results: { taskId: string; success: boolean; message?: string }[] = [];
    let successCount = 0;

    for (const taskId of dto.taskIds) {
      try {
        await this.remind(instanceId, { taskId, message: dto.message }, operatorId);
        results.push({ taskId, success: true });
        successCount++;
      } catch (error: any) {
        results.push({ taskId, success: false, message: error.message });
      }
    }

    return {
      total: dto.taskIds.length,
      success: successCount,
      failed: dto.taskIds.length - successCount,
      results,
    };
  }

  // ==================== 辅助方法 ====================

  private async getTaskWithValidation(taskId: string, operatorId: string) {
    // 验证 UUID 格式
    if (!this.isValidUUID(taskId)) {
      throw new BusinessException('任务不存在', 'TASK_NOT_FOUND', HttpStatus.NOT_FOUND);
    }

    const task = await this.prisma.approvalTask.findUnique({
      where: { id: taskId },
      include: {
        instance: true,
        nodeInstance: true,
      },
    });

    if (!task) {
      throw new BusinessException('任务不存在', 'TASK_NOT_FOUND', HttpStatus.NOT_FOUND);
    }

    // 检查权限
    const hasPermission =
      task.assignee === operatorId ||
      task.candidateUsers.includes(operatorId);

    if (!hasPermission) {
      throw new ForbiddenException('无权操作此任务');
    }

    return task;
  }

  private validateTaskForAction(task: any, action: string) {
    const validStatuses = [ApprovalTaskStatus.PENDING, ApprovalTaskStatus.CLAIMED, ApprovalTaskStatus.ASSIGNED, ApprovalTaskStatus.IN_PROGRESS];
    if (!validStatuses.includes(task.status)) {
      throw new BadRequestException(`任务状态 ${task.status} 不允许执行 ${action} 操作`);
    }

    if (task.instance.status !== InstanceStatus.RUNNING) {
      throw new BadRequestException('流程已结束，无法执行操作');
    }
  }

  private async createActionLog(
    taskId: string,
    instanceId: string,
    action: ApprovalTaskAction,
    operatorId: string,
    data: {
      comment?: string;
      targetUserId?: string;
      targetNodeId?: string;
      addSignUsers?: string[];
      formData?: any;
      attachments?: any;
      riskLevel?: 'LOW' | 'MEDIUM' | 'HIGH';
      adminReason?: string;
      requestId?: string;
    },
  ) {
    await this.prisma.approvalTaskLog.create({
      data: {
        taskId,
        instanceId,
        action,
        operatorId,
        comment: data.comment,
        targetUserId: data.targetUserId,
        targetNodeId: data.targetNodeId,
        addSignUsers: data.addSignUsers || [],
        formDataChanges: data.formData,
        attachments: data.attachments,
        riskLevel: data.riskLevel,
        adminReason: data.adminReason,
        requestId: data.requestId,
      },
    });
  }

  private async getCurrentNodeInfo(instanceId: string) {
    const nodeInstance = await this.prisma.approvalNodeInstance.findFirst({
      where: { instanceId, status: NodeStatus.ACTIVE },
      include: {
        approvalTasks: {
          where: { status: { in: ['PENDING', 'CLAIMED', 'ASSIGNED'] } },
        },
      },
    });

    if (!nodeInstance) {
      return null;
    }

    const assignees = await Promise.all(
      nodeInstance.approvalTasks
        .filter((t) => t.assignee)
        .map((t) => this.getUserInfo(t.assignee!)),
    );

    return {
      nodeId: nodeInstance.nodeId,
      nodeName: nodeInstance.nodeName,
      assignees: assignees.filter(Boolean) as UserInfo[],
    };
  }

  private async getUserInfo(userId: string): Promise<UserInfo | null> {
    const user: any = await this.prisma.user.findUnique({
      where: { id: userId },
      include: {
        departmentMemberships: {
          where: { isPrimary: true },
          include: {
            department: true,
            position: true,
          },
        },
      } as any,
    });

    if (!user) {
      return null;
    }

    const primaryMembership = user.departmentMemberships?.[0];
    return {
      id: user.id,
      name: user.displayName,
      avatar: user.avatar || undefined,
      department: primaryMembership?.department?.name,
      position: primaryMembership?.position?.name,
    };
  }

  private mapUserInfo(user: any): UserInfo {
    return {
      id: user.id,
      name: user.displayName,
      avatar: user.avatar || undefined,
      department: user.department?.name,
      position: user.position?.name,
    };
  }

  private getAvailableActions(task: any, userId: string): AvailableAction[] {
    const actions: AvailableAction[] = [];
    const isAssignee = task.assignee === userId;
    const isCandidate = task.candidateUsers.includes(userId);
    const pendingStatuses: ApprovalTaskStatus[] = [ApprovalTaskStatus.PENDING, ApprovalTaskStatus.CLAIMED, ApprovalTaskStatus.ASSIGNED];
    const isPending = pendingStatuses.includes(task.status);

    if (isPending && (isAssignee || isCandidate)) {
      actions.push({
        action: 'APPROVE',
        label: '通过',
        enabled: true,
      });

      actions.push({
        action: 'REJECT',
        label: '驳回',
        enabled: true,
      });

      actions.push({
        action: 'RETURN',
        label: '退回',
        enabled: true,
      });

      actions.push({
        action: 'FORWARD',
        label: '转发',
        enabled: true,
      });

      actions.push({
        action: 'ADD_SIGN',
        label: '加签',
        enabled: true,
      });
    }

    if (task.status === ApprovalTaskStatus.PENDING && isCandidate && !isAssignee) {
      actions.push({
        action: 'CLAIM',
        label: '认领',
        enabled: true,
      });
    }

    if (task.status === ApprovalTaskStatus.CLAIMED && isAssignee) {
      actions.push({
        action: 'UNCLAIM',
        label: '取消认领',
        enabled: true,
      });
    }

    return actions;
  }

  // ==================== 催办记录 ====================

  async getReminders(instanceId: string) {
    const reminders = await this.prisma.reminderQueue.findMany({
      where: { instanceId },
      orderBy: { createdAt: 'desc' },
    });

    return {
      items: reminders.map(r => ({
        id: r.id,
        instanceId: r.instanceId,
        taskId: r.taskId,
        reminderType: r.reminderType,
        channels: r.channels,
        status: r.status,
        scheduledAt: r.scheduledAt.toISOString(),
        executedAt: r.executedAt?.toISOString(),
        createdAt: r.createdAt.toISOString(),
      })),
    };
  }

  async getMyReminders(userId: string, query: PendingTasksQueryDto) {
    const page = query.page || 1;
    const limit = query.limit || 20;

    const [reminders, total] = await Promise.all([
      this.prisma.reminderQueue.findMany({
        where: {
          task: {
            OR: [
              { assignee: userId },
              { candidateUsers: { has: userId } },
            ],
          },
        },
        include: {
          task: {
            include: {
              instance: true,
            },
          },
        },
        orderBy: { createdAt: 'desc' },
        skip: (page - 1) * limit,
        take: limit,
      }),
      this.prisma.reminderQueue.count({
        where: {
          task: {
            OR: [
              { assignee: userId },
              { candidateUsers: { has: userId } },
            ],
          },
        },
      }),
    ]);

      return {
      items: reminders.map(r => ({
        id: r.id,
        instanceId: r.instanceId,
        taskId: r.taskId,
        taskName: r.task?.name,
        businessKey: r.task?.instance?.businessKey,
        reminderType: r.reminderType,
        channels: r.channels,
        status: r.status,
        scheduledAt: r.scheduledAt.toISOString(),
        executedAt: r.executedAt?.toISOString(),
        createdAt: r.createdAt.toISOString(),
      })),
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
      hasNext: page * limit < total,
      hasPrev: page > 1,
    };
  }

  // ==================== 辅助方法 ====================

  /**
   * 验证 UUID 格式
   */
  private isValidUUID(str: string): boolean {
    if (!str) return false;
    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
    return uuidRegex.test(str);
  }
}
