import { Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { TemporalService } from './temporal/temporal.service';  // 使用审批引擎专用的 TemporalService
import { AuditService } from '@core/observability/audit/audit.service';  // ⭐ 引入全局审计服务
import { randomUUID as uuidv4 } from 'crypto';  // ⭐ 引入 uuid 生成器
import {
  AdminTerminateDto,
  AdminApproveDto,
  AdminRejectDto,
  AdminReassignDto,
  AdminResumeWithApproversDto,
  AuditLogsQueryDto,
} from './dto/approval.dto';
import {
  AdminActionResponse,
  PaginatedAuditLogsResponse,
  AuditLogItem,
  UserInfo,
} from './dto/approval-response.dto';
import { Prisma, ApprovalTaskType, ApprovalTaskStatus, AuditAction } from '@prisma/client';

interface AdminContext {
  adminReason?: string;
  requestId?: string;
  ipAddress?: string;
  userAgent?: string;
}

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

  constructor(
    private readonly prisma: PrismaService,
    private readonly temporalService: TemporalService,
    private readonly auditService: AuditService,  // ⭐ 注入全局审计服务
  ) {}

  /**
   * 辅助方法：验证 UUID 格式，如果无效则生成新的 UUID
   */
  private ensureValidUuid(value?: string): string {
    // UUID 正则表达式
    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
    
    if (value && uuidRegex.test(value)) {
      return value;
    }
    
    // 如果无效或不存在，生成新的 UUID
    return uuidv4();
  }

  // ==================== 管理员强制结束 ====================

  async terminate(
    instanceId: string,
    dto: AdminTerminateDto,
    operatorId: string,
    context: AdminContext,
  ): Promise<AdminActionResponse> {
    this.logger.warn(`Admin terminating process: ${instanceId} by ${operatorId}`);

    const instance = await this.prisma.approvalInstance.findUnique({
      where: { id: instanceId },
    });

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

    if (instance.status !== 'RUNNING' && instance.status !== 'SUSPENDED') {
      throw new BadRequestException('只有运行中或挂起的流程可以强制结束');
    }

    // 发送终止 Signal 到 Temporal
    await this.temporalService.sendSignal(instance.workflowId, 'adminTerminate', {
      operatorId,
      reason: dto.reason,
    });

    // 更新流程状态
    await this.prisma.approvalInstance.update({
      where: { id: instanceId },
      data: {
        status: 'TERMINATED',
        endTime: new Date(),
        endReason: dto.reason,
      },
    });

    // 取消所有待处理任务
    await this.prisma.approvalTask.updateMany({
      where: {
        instanceId,
        status: { in: ['PENDING', 'CLAIMED', 'ASSIGNED'] },
      },
      data: {
        status: 'CANCELLED',
        endTime: new Date(),
      },
    });

    // 创建审计日志
    const auditLogId = await this.createAuditLog({
      action: 'ADMIN_TERMINATE',
      riskLevel: 'HIGH',
      operatorId,
      instanceId,
      reason: dto.reason,
      attachments: dto.attachments,
      ...context,
    });

    // ⭐ 写入全局审计日志（合规审计）
    await this.writeGlobalAuditLog({
      action: 'ADMIN_TERMINATE',
      operatorId,
      instanceId,
      reason: dto.reason,
      ipAddress: context.ipAddress || 'unknown',
      userAgent: context.userAgent || 'unknown',
      requestId: this.ensureValidUuid(context.requestId),  // ⭐ 确保 requestId 是有效的 UUID
    });

    return {
      success: true,
      instanceId,
      status: 'TERMINATED',
      endReason: 'ADMIN_TERMINATED',
      action: 'ADMIN_TERMINATE',
      message: '流程已强制结束',
      auditLogId,
    };
  }

  // ==================== 管理员恢复挂起的流程（ERR-20260501-004） ====================

  /**
   * 节点审批人解析失败时 workflow 进入 SUSPENDED 状态，管理员通过此方法显式指派
   * 审批人列表，发送 resumeWithApprovers signal 让 workflow 继续。
   */
  async resumeWithApprovers(
    instanceId: string,
    dto: AdminResumeWithApproversDto,
    operatorId: string,
    context: AdminContext,
  ): Promise<AdminActionResponse> {
    this.logger.log(`Admin resuming suspended instance ${instanceId} with ${dto.approverIds.length} approvers`);

    const instance = await this.prisma.approvalInstance.findUnique({
      where: { id: instanceId },
    });
    if (!instance) {
      throw new NotFoundException('流程实例不存在');
    }
    if (instance.status !== 'SUSPENDED') {
      throw new BadRequestException('只有挂起状态的流程可以恢复');
    }
    if (!dto.approverIds.length) {
      throw new BadRequestException('必须指定至少一个审批人');
    }

    // 发送 signal 让 workflow 继续；workflow 端的 setHandler(resumeWithApproversSignal) 会
    // 把 approverIds 注入 state.resumedApprovers，触发 condition 退出，进入正常审批流程。
    await this.temporalService.sendSignal(instance.workflowId, 'resumeWithApprovers', {
      approverIds: dto.approverIds,
      resolvedBy: operatorId,
    });

    // 写审计日志
    const auditLogId = await this.createAuditLog({
      action: 'ADMIN_REASSIGN',
      riskLevel: 'HIGH',
      operatorId,
      instanceId,
      reason: `恢复挂起流程，指派审批人: ${dto.approverIds.join(', ')}. ${dto.reason}`,
      ...context,
    });

    return {
      success: true,
      instanceId,
      status: 'RUNNING',
      action: 'ADMIN_RESUME',
      message: '挂起流程已恢复',
      auditLogId,
    };
  }

  // ==================== 管理员代审批（通过） ====================

  async approve(
    instanceId: string,
    dto: AdminApproveDto,
    operatorId: string,
    context: AdminContext,
  ): Promise<AdminActionResponse> {
    this.logger.warn(`Admin approving task: ${dto.taskId} by ${operatorId}`);

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

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

    if (task.instanceId !== instanceId) {
      throw new BadRequestException('任务不属于该流程');
    }

    if (!['PENDING', 'CLAIMED', 'ASSIGNED'].includes(task.status)) {
      throw new BadRequestException('任务状态不允许审批');
    }

    // 发送 Signal 到 Temporal
    await this.temporalService.sendSignal(task.instance.workflowId, 'approve', {
      taskId: dto.taskId,
      operatorId,
      comment: dto.comment,
      isAdminAction: true,
    });

    // 创建审计日志
    const auditLogId = await this.createAuditLog({
      action: 'ADMIN_APPROVE',
      riskLevel: 'MEDIUM',
      operatorId,
      instanceId,
      taskId: dto.taskId,
      reason: dto.reason,
      comment: dto.comment,
      attachments: dto.attachments,
      ...context,
    });

    // ⭐ 写入全局审计日志（合规审计）
    await this.writeGlobalAuditLog({
      action: 'ADMIN_APPROVE',
      operatorId,
      instanceId,
      taskId: dto.taskId,
      reason: dto.reason,
      comment: dto.comment,
      ipAddress: context.ipAddress || 'unknown',
      userAgent: context.userAgent || 'unknown',
      requestId: this.ensureValidUuid(context.requestId),  // ⭐ 确保 requestId 是有效的 UUID
    });

    return {
      success: true,
      instanceId,
      taskId: dto.taskId,
      action: 'ADMIN_APPROVE',
      message: '代审批（通过）成功',
      auditLogId,
    };
  }

  // ==================== 管理员代审批（驳回） ====================

  async reject(
    instanceId: string,
    dto: AdminRejectDto,
    operatorId: string,
    context: AdminContext,
  ): Promise<AdminActionResponse> {
    this.logger.warn(`Admin rejecting task: ${dto.taskId} by ${operatorId}`);

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

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

    if (task.instanceId !== instanceId) {
      throw new BadRequestException('任务不属于该流程');
    }

    if (!['PENDING', 'CLAIMED', 'ASSIGNED'].includes(task.status)) {
      throw new BadRequestException('任务状态不允许驳回');
    }

    // 发送 Signal 到 Temporal
    await this.temporalService.sendSignal(task.instance.workflowId, 'reject', {
      taskId: dto.taskId,
      operatorId,
      comment: dto.comment,
      rejectCode: dto.rejectCode,
      isAdminAction: true,
    });

    // 创建审计日志
    const auditLogId = await this.createAuditLog({
      action: 'ADMIN_REJECT',
      riskLevel: 'MEDIUM',
      operatorId,
      instanceId,
      taskId: dto.taskId,
      reason: dto.reason,
      comment: dto.comment,
      attachments: dto.attachments,
      ...context,
    });

    // ⭐ 写入全局审计日志（合规审计）
    await this.writeGlobalAuditLog({
      action: 'ADMIN_REJECT',
      operatorId,
      instanceId,
      taskId: dto.taskId,
      reason: dto.reason,
      comment: dto.comment,
      ipAddress: context.ipAddress || 'unknown',
      userAgent: context.userAgent || 'unknown',
      requestId: this.ensureValidUuid(context.requestId),  // ⭐ 确保 requestId 是有效的 UUID
    });

    return {
      success: true,
      instanceId,
      taskId: dto.taskId,
      action: 'ADMIN_REJECT',
      message: '代审批（驳回）成功',
      auditLogId,
    };
  }

  // ==================== 管理员重新分配任务 ====================

  async reassign(
    instanceId: string,
    dto: AdminReassignDto,
    operatorId: string,
    context: AdminContext,
  ): Promise<AdminActionResponse> {
    this.logger.warn(`Admin reassigning task: ${dto.taskId} to ${dto.newAssigneeId} by ${operatorId}`);

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

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

    if (task.instanceId !== instanceId) {
      throw new BadRequestException('任务不属于该流程');
    }

    if (!['PENDING', 'CLAIMED', 'ASSIGNED'].includes(task.status)) {
      throw new BadRequestException('任务状态不允许重新分配');
    }

    const originalAssignee = task.assignee;

    // 更新任务
    await this.prisma.approvalTask.update({
      where: { id: dto.taskId },
      data: {
        assignee: dto.newAssigneeId,
        status: 'ASSIGNED',
        isDelegated: true,
        delegatedFrom: originalAssignee,
        delegatedAt: new Date(),
        delegationReason: dto.reason,
        delegationType: 'MANUAL',
      },
    });

    // 创建审计日志
    const auditLogId = await this.createAuditLog({
      action: 'ADMIN_REASSIGN',
      riskLevel: 'LOW',
      operatorId,
      instanceId,
      taskId: dto.taskId,
      reason: dto.reason,
      originalAssignee,
      newAssignee: dto.newAssigneeId,
      targetUserId: dto.newAssigneeId,  // ⭐ 记录目标用户ID，用于前端展示
      ...context,
    });

    // ⭐ 写入全局审计日志（合规审计）
    await this.writeGlobalAuditLog({
      action: 'ADMIN_REASSIGN',
      operatorId,
      instanceId,
      taskId: dto.taskId,
      reason: dto.reason,
      ipAddress: context.ipAddress || 'unknown',
      userAgent: context.userAgent || 'unknown',
      requestId: this.ensureValidUuid(context.requestId),  // ⭐ 确保 requestId 是有效的 UUID
    });

    return {
      success: true,
      instanceId,
      taskId: dto.taskId,
      action: 'ADMIN_REASSIGN',
      message: '任务重新分配成功',
      auditLogId,
    };
  }

  // ==================== 审计日志查询 ====================

  async getAuditLogs(query: AuditLogsQueryDto): Promise<PaginatedAuditLogsResponse> {
    const where: Prisma.ApprovalTaskLogWhereInput = {
      action: {
        in: ['ADMIN_APPROVE', 'ADMIN_REJECT', 'ADMIN_TERMINATE', 'ADMIN_REASSIGN'],
      },
    };

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

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

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

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

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

    // 排序
    let orderBy: Prisma.ApprovalTaskLogOrderByWithRelationInput = { actionTime: 'desc' };
    if (query.sortBy === 'riskLevel') {
      orderBy = { riskLevel: query.sortOrder || 'desc' };
    }

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

    const items: AuditLogItem[] = await Promise.all(
      logs.map(async (log) => {
        const originalAssignee = log.metadata && (log.metadata as any).originalAssignee
          ? await this.getUserInfo((log.metadata as any).originalAssignee)
          : undefined;

        const newAssignee = log.metadata && (log.metadata as any).newAssignee
          ? await this.getUserInfo((log.metadata as any).newAssignee)
          : undefined;

        return {
          id: log.id,
          action: log.action as any,
          riskLevel: (log.riskLevel || 'MEDIUM') as any,
          operator: this.mapUserInfo(log.operator),
          instanceId: log.instanceId,
          taskId: log.taskId,
          businessType: log.task.instance.businessType,
          businessId: log.task.instance.businessId,
          businessKey: log.task.instance.businessKey,
          processTitle: log.task.instance.businessKey,
          reason: log.comment || '',
          adminReason: log.adminReason || undefined,
          comment: log.comment || undefined,
          attachments: log.attachments as any,
          originalAssignee,
          newAssignee,
          requestId: log.requestId || '',
          ipAddress: log.ipAddress || '',
          userAgent: log.userAgent || '',
          actionTime: log.actionTime.toISOString(),
        };
      }),
    );

    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,
    };
  }

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

  /**
   * 写入全局审计日志（合规审计）
   */
  private async writeGlobalAuditLog(data: {
    action: 'ADMIN_APPROVE' | 'ADMIN_REJECT' | 'ADMIN_TERMINATE' | 'ADMIN_REASSIGN';
    operatorId: string;
    instanceId: string;
    taskId?: string;
    reason: string;
    comment?: string;
    ipAddress: string;
    userAgent: string;
    requestId: string;
  }): Promise<void> {
    try {
      // 获取流程实例信息（用于 businessType 和 businessKey）
      const instance = await this.prisma.approvalInstance.findUnique({
        where: { id: data.instanceId },
        select: {
          businessType: true,
          businessKey: true,
          businessId: true,
          id: true,
        },
      });

      if (!instance) {
        this.logger.warn(`Instance ${data.instanceId} not found for global audit log`);
        return;
      }

      // 映射管理员操作到审计日志 action
      const actionMap: Record<string, AuditAction> = {
        ADMIN_APPROVE: AuditAction.APPROVE,
        ADMIN_REJECT: AuditAction.REJECT,
        ADMIN_TERMINATE: AuditAction.DELETE, // 强制终止映射为 DELETE
        ADMIN_REASSIGN: AuditAction.UPDATE, // 重新分配映射为 UPDATE
      };

      const auditAction = actionMap[data.action] || AuditAction.UPDATE;

      // 确定风险等级
      const riskLevel = data.action === 'ADMIN_TERMINATE' ? 'HIGH' : 'MEDIUM';

      // ⭐ 写入全局审计日志
      this.logger.debug(`[writeGlobalAuditLog] Attempting to write global audit log for ${data.action}`);
      this.logger.debug(`[writeGlobalAuditLog] AuditService available: ${!!this.auditService}`);
      
      const auditData = {
        // 多租户（默认值）
        region: 'cn',
        tenantId: 'default',

        // 5W1H
        who: data.operatorId,
        what: `管理员${this.getActionLabel(data.action)}流程 ${instance.businessKey || instance.id}`,
        where: data.ipAddress,
        why: data.reason,
        how: 'ADMIN_FORCE',

        // 操作详情
        module: 'APPROVAL',
        action: auditAction,
        entityType: 'APPROVAL_INSTANCE',
        entityId: data.instanceId,

        // 上下文
        userId: data.operatorId,
        requestId: data.requestId,

        // 环境
        ipAddress: data.ipAddress,
        userAgent: data.userAgent,

        // 业务
        businessType: instance.businessType,
        businessKey: instance.businessKey || instance.businessId,

        // 合规
        isSensitive: true,
        isFinancial: instance.businessType?.includes('FINANCE') || instance.businessType?.includes('PAYMENT'),
        riskLevel: riskLevel as any,
      };
      
      this.logger.debug(`[writeGlobalAuditLog] Audit data prepared: ${JSON.stringify(auditData, null, 2)}`);
      
      await this.auditService.log(auditData);

      this.logger.log(`✅ Global audit log created for ${data.action} by ${data.operatorId}`);
    } catch (error) {
      this.logger.error(`❌ Failed to write global audit log for ${data.action}:`, error);
      this.logger.error(`Error stack: ${error.stack}`);
      this.logger.error(`Error details: ${JSON.stringify(error, null, 2)}`);
      // 审计日志失败不影响主业务
    }
  }

  /**
   * 获取操作标签（用于审计日志描述）
   */
  private getActionLabel(action: string): string {
    const labels: Record<string, string> = {
      ADMIN_APPROVE: '代审批通过',
      ADMIN_REJECT: '代审批驳回',
      ADMIN_TERMINATE: '强制终止',
      ADMIN_REASSIGN: '重新分配',
    };
    return labels[action] || action;
  }

  private async createAuditLog(data: {
    action: 'ADMIN_APPROVE' | 'ADMIN_REJECT' | 'ADMIN_TERMINATE' | 'ADMIN_REASSIGN';
    riskLevel: 'LOW' | 'MEDIUM' | 'HIGH';
    operatorId: string;
    instanceId: string;
    taskId?: string;
    reason: string;
    comment?: string;
    attachments?: any;
    adminReason?: string;
    requestId?: string;
    ipAddress?: string;
    userAgent?: string;
    originalAssignee?: string | null;
    newAssignee?: string;
    targetUserId?: string;  // ⭐ 目标用户ID（用于转交、重新分配等操作）
  }): Promise<string> {
    // 如果没有 taskId，创建一个虚拟任务记录
    let taskId = data.taskId;
    if (!taskId) {
      // 查找该流程的任意一个任务
      const task = await this.prisma.approvalTask.findFirst({
        where: { instanceId: data.instanceId },
      });
      taskId = task?.id;
    }

    // 如果仍然没有找到任务，为管理员操作创建一个虚拟任务和审计日志
    // 这种情况可能发生在流程刚创建还没有任务时
    if (!taskId) {
      this.logger.warn(`No task found for instance ${data.instanceId}, creating virtual task for audit log`);
      
      // 查找节点实例
      const nodeInstance = await this.prisma.approvalNodeInstance.findFirst({
        where: { instanceId: data.instanceId },
      });

      if (!nodeInstance) {
        // 如果连节点实例都没有，创建一个虚拟的
        const createdNodeInstance = await this.prisma.approvalNodeInstance.create({
          data: {
            instanceId: data.instanceId,
            nodeId: 'admin-action',
            nodeName: 'Admin Action',
            nodeType: 'USER_TASK',
            status: 'COMPLETED',
          },
        });
        
        const task = await this.prisma.approvalTask.create({
          data: {
            instanceId: data.instanceId,
            nodeInstanceId: createdNodeInstance.id,
            name: 'Admin Action',
            type: ApprovalTaskType.APPROVAL,
            status: ApprovalTaskStatus.CANCELLED,
          },
        });
        taskId = task.id;
      } else {
        const task = await this.prisma.approvalTask.create({
          data: {
            instanceId: data.instanceId,
            nodeInstanceId: nodeInstance.id,
            name: 'Admin Action',
            type: ApprovalTaskType.APPROVAL,
            status: ApprovalTaskStatus.CANCELLED,
          },
        });
        taskId = task.id;
      }
    }

    const log = await this.prisma.approvalTaskLog.create({
      data: {
        taskId,
        instanceId: data.instanceId,
        action: data.action,
        operatorId: data.operatorId,
        comment: data.comment || data.reason,
        targetUserId: data.targetUserId,  // ⭐ 记录目标用户ID
        riskLevel: data.riskLevel,
        adminReason: data.adminReason,
        requestId: data.requestId,
        ipAddress: data.ipAddress,
        userAgent: data.userAgent,
        attachments: data.attachments,
        metadata: {
          originalAssignee: data.originalAssignee,
          newAssignee: data.newAssignee,
        },
      },
    });

    return log.id;
  }

  private async getUserInfo(userId: string): Promise<UserInfo | undefined> {
    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 undefined;
    }

    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,
    };
  }
}

