/**
 * WorkflowRolesService Unit Tests
 * 
 * 测试流程角色管理服务
 * 
 * 测试覆盖:
 * - 创建流程角色
 * - 更新流程角色
 * - 删除流程角色
 * - 流程角色解析 (resolve)
 * - 用户分配（FIXED_USERS 类型）
 * 
 * 基于文档: docs/modules/organization/09-test-scenarios.md
 */

import { Test, TestingModule } from '@nestjs/testing';
import { NotFoundException, BadRequestException, ConflictException } from '@nestjs/common';
import { WorkflowRolesService } from '@/modules/organization/workflow-roles/workflow-roles.service';
import { PrismaService } from '@/core/database/prisma/prisma.service';
import {
  CreateWorkflowRoleDto,
  UpdateWorkflowRoleDto,
  WorkflowRuleType,
  ResolveWorkflowRoleDto,
} from '@/modules/organization/workflow-roles/dto/workflow-role.dto';
import { 
  IamWorkflowRoleResolveEmptyException,
  IamUserNotInDepartmentException,
} from '@/modules/organization/exceptions';

describe('WorkflowRolesService', () => {
  let service: WorkflowRolesService;
  let prisma: PrismaService;

  const mockPrismaService = {
    workflowRole: {
      findUnique: jest.fn(),
      findFirst: jest.fn(),
      findMany: jest.fn(),
      create: jest.fn(),
      update: jest.fn(),
      delete: jest.fn(),
    },
    user: {
      findUnique: jest.fn(),
      findFirst: jest.fn(),
      findMany: jest.fn(),
    },
    userDepartment: {
      findUnique: jest.fn(),
      findFirst: jest.fn(),
      findMany: jest.fn(),
    },
    department: {
      findUnique: jest.fn(),
    },
    role: {
      findFirst: jest.fn(),
    },
    workflowRoleUser: {
      findMany: jest.fn(),
      findFirst: jest.fn(),
      createMany: jest.fn(),
      delete: jest.fn(),
    },
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        WorkflowRolesService,
        {
          provide: PrismaService,
          useValue: mockPrismaService,
        },
      ],
    }).compile();

    service = module.get<WorkflowRolesService>(WorkflowRolesService);
    prisma = module.get<PrismaService>(PrismaService);

    jest.clearAllMocks();
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  // ==================== 6.1 创建流程角色 ====================

  describe('create', () => {
    it('[测试场景 6.1.1] 应该成功创建组织关系类型流程角色', async () => {
      const createDto: CreateWorkflowRoleDto = {
        name: '直属上级',
        code: 'WF_DIRECT_MANAGER',
        description: '解析发起人的直属上级',
        ruleType: WorkflowRuleType.ORGANIZATION_RELATION,
        ruleConfig: {
          relation: 'manager',
          fallbackType: 'UP_CHAIN',
          fallbackConfig: { maxLevel: 2 },
        },
      };

      const mockWorkflowRole = {
        id: 'wf-role-id-1',
        code: 'WF_DIRECT_MANAGER',
        name: '直属上级',
        description: '解析发起人的直属上级',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: {
          relation: 'manager',
          fallbackType: 'UP_CHAIN',
          fallbackConfig: { maxLevel: 2 },
        },
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      mockPrismaService.workflowRole.findFirst.mockResolvedValue(null);
      mockPrismaService.workflowRole.create.mockResolvedValue(mockWorkflowRole);

      const result = await service.create(createDto);

      expect(result.id).toBeDefined();
      expect(result.code).toBe('WF_DIRECT_MANAGER');
      expect(result.ruleType).toBe('ORGANIZATION_RELATION');
      expect(result.ruleConfig).toMatchObject({
        relation: 'manager',
        fallbackType: 'UP_CHAIN',
      });
    });

    it('[测试场景 6.1.2] 应该成功创建固定用户类型流程角色', async () => {
      const createDto: CreateWorkflowRoleDto = {
        name: '财务审批人',
        code: 'WF_FINANCE_APPROVER',
        description: '财务部门固定审批人',
        ruleType: WorkflowRuleType.FIXED_USERS,
        ruleConfig: {},
      };

      const mockWorkflowRole = {
        id: 'wf-role-id-2',
        ...createDto,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      mockPrismaService.workflowRole.findFirst.mockResolvedValue(null);
      mockPrismaService.workflowRole.create.mockResolvedValue(mockWorkflowRole);

      const result = await service.create(createDto);

      expect(result.id).toBeDefined();
      expect(result.ruleType).toBe('FIXED_USERS');
    });

    it('[测试场景 6.1.3] code 重复应抛出异常', async () => {
      const createDto: CreateWorkflowRoleDto = {
        name: '直属上级',
        code: 'WF_DIRECT_MANAGER',
        ruleType: WorkflowRuleType.ORGANIZATION_RELATION,
        ruleConfig: { relation: 'manager' },
      };

      mockPrismaService.workflowRole.findFirst.mockResolvedValue({
        id: 'existing-id',
        code: 'WF_DIRECT_MANAGER',
        name: '其他名称',
      });

      await expect(service.create(createDto)).rejects.toThrow(ConflictException);
      await expect(service.create(createDto)).rejects.toThrow(
        "code 'WF_DIRECT_MANAGER' already exists",
      );
    });

    it('[测试场景 6.1.4] name 重复应抛出异常', async () => {
      const createDto: CreateWorkflowRoleDto = {
        name: '直属上级',
        code: 'WF_DIRECT_MANAGER',
        ruleType: WorkflowRuleType.ORGANIZATION_RELATION,
        ruleConfig: { relation: 'manager' },
      };

      mockPrismaService.workflowRole.findFirst.mockResolvedValue({
        id: 'existing-id',
        code: 'OTHER_CODE',
        name: '直属上级',
      });

      await expect(service.create(createDto)).rejects.toThrow(ConflictException);
      await expect(service.create(createDto)).rejects.toThrow("name '直属上级' already exists");
    });
  });

  // ==================== 6.2 流程角色解析 (resolve) ====================

  describe('resolve', () => {
    it('[测试场景 6.2.1] 应该成功解析直属上级（使用主部门）', async () => {
      const manager = {
        id: 'manager-id',
        displayName: '经理',
        email: 'manager@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DIRECT_MANAGER',
        name: '直属上级',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: { relation: 'manager' },
        userAssignments: [],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findUnique.mockResolvedValue({
        id: 'employee-id',
        deletedAt: null,
      });
      mockPrismaService.userDepartment.findFirst.mockResolvedValue({
        userId: 'employee-id',
        departmentId: 'dept-id',
        isPrimary: true,
        managerId: 'manager-id',
        manager,
      });

      const result = await service.resolve({
        workflowRoleCode: 'WF_DIRECT_MANAGER',
        context: {
          initiatorUserId: 'employee-id',
        },
      });

      expect(result.users).toHaveLength(1);
      expect(result.users[0].userId).toBe('manager-id');
      expect(result.resolvedBy).toBe('ORGANIZATION_RELATION');
      expect(result.fallbackUsed).toBe(false);
    });

    it('[测试场景 6.2.2] 应该成功解析直属上级（使用指定部门）', async () => {
      const manager1 = {
        id: 'manager1-id',
        displayName: '经理1',
        email: 'manager1@ff.com',
        status: 'ACTIVE',
      };
      const manager2 = {
        id: 'manager2-id',
        displayName: '经理2',
        email: 'manager2@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DIRECT_MANAGER',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: { relation: 'manager' },
        userAssignments: [],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findUnique.mockResolvedValue({
        id: 'employee-id',
        deletedAt: null,
      });

      // 指定部门2，应返回部门2的上级
      mockPrismaService.userDepartment.findUnique.mockResolvedValue({
        userId: 'employee-id',
        departmentId: 'dept2-id',
        isPrimary: false,
        managerId: 'manager2-id',
        manager: manager2,
      });

      const result = await service.resolve({
        workflowRoleCode: 'WF_DIRECT_MANAGER',
        context: {
          initiatorUserId: 'employee-id',
          formData: {
            departmentId: 'dept2-id',
          },
        },
      });

      expect(result.users).toHaveLength(1);
      expect(result.users[0].userId).toBe('manager2-id');
    });

    it('[测试场景 6.2.3] 应该成功解析部门主管', async () => {
      const deptHead = {
        id: 'head-id',
        displayName: '部门主管',
        email: 'head@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DEPT_HEAD',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: { relation: 'departmentHead' },
        userAssignments: [],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findUnique.mockResolvedValue({
        id: 'employee-id',
        deletedAt: null,
      });
      mockPrismaService.userDepartment.findFirst.mockResolvedValue({
        userId: 'employee-id',
        departmentId: 'dept-id',
        isPrimary: true,
      });
      mockPrismaService.department.findUnique.mockResolvedValue({
        id: 'dept-id',
        headId: 'head-id',
        parentId: null,
        deletedAt: null,
      });
      mockPrismaService.user.findUnique.mockResolvedValueOnce({
        id: 'employee-id',
        deletedAt: null,
      }).mockResolvedValueOnce(deptHead);

      const result = await service.resolve({
        workflowRoleCode: 'WF_DEPT_HEAD',
        context: {
          initiatorUserId: 'employee-id',
        },
      });

      expect(result.users).toHaveLength(1);
      expect(result.users[0].userId).toBe('head-id');
    });

    it('[测试场景 6.2.3.1] 应该成功解析连续部门主管链', async () => {
      const manager = {
        id: 'manager-id',
        displayName: '部门主管',
        email: 'manager@ff.com',
        status: 'ACTIVE',
      };
      const director = {
        id: 'director-id',
        displayName: '总监',
        email: 'director@ff.com',
        status: 'ACTIVE',
      };
      const vp = {
        id: 'vp-id',
        displayName: 'VP',
        email: 'vp@ff.com',
        status: 'ACTIVE',
      };
      const ceo = {
        id: 'ceo-id',
        displayName: 'CEO',
        email: 'ceo@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DEPT_HEAD_CHAIN',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: { relation: 'departmentHeadChain' },
        userAssignments: [],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findUnique.mockResolvedValue({
        id: 'employee-id',
        deletedAt: null,
      });
      mockPrismaService.userDepartment.findFirst.mockResolvedValue({
        userId: 'employee-id',
        departmentId: 'team-id',
        isPrimary: true,
      });

      // 模拟4层部门结构
      mockPrismaService.department.findUnique
        .mockResolvedValueOnce({
          id: 'team-id',
          name: '开发组',
          headId: 'manager-id',
          parentId: 'dept-id',
          deletedAt: null,
        })
        .mockResolvedValueOnce({
          id: 'dept-id',
          name: '技术部',
          headId: 'director-id',
          parentId: 'division-id',
          deletedAt: null,
        })
        .mockResolvedValueOnce({
          id: 'division-id',
          name: '事业部',
          headId: 'vp-id',
          parentId: 'company-id',
          deletedAt: null,
        })
        .mockResolvedValueOnce({
          id: 'company-id',
          name: '公司',
          headId: 'ceo-id',
          parentId: null,
          deletedAt: null,
        });

      mockPrismaService.user.findUnique
        .mockResolvedValueOnce({ id: 'employee-id', deletedAt: null })
        .mockResolvedValueOnce(manager)
        .mockResolvedValueOnce(director)
        .mockResolvedValueOnce(vp)
        .mockResolvedValueOnce(ceo);

      const result = await service.resolve({
        workflowRoleCode: 'WF_DEPT_HEAD_CHAIN',
        context: {
          initiatorUserId: 'employee-id',
        },
      });

      expect(result.users).toHaveLength(4);
      expect(result.users[0].userId).toBe('manager-id');
      expect(result.users[1].userId).toBe('director-id');
      expect(result.users[2].userId).toBe('vp-id');
      expect(result.users[3].userId).toBe('ceo-id');
      expect(result.strategy).toBe('SEQUENTIAL');
      expect(result.resolvedBy).toBe('ORGANIZATION_RELATION');
    });

    it('[测试场景 6.2.3.2] 应该跳过没有主管的部门', async () => {
      const manager = {
        id: 'manager-id',
        displayName: '部门主管',
        email: 'manager@ff.com',
        status: 'ACTIVE',
      };
      const ceo = {
        id: 'ceo-id',
        displayName: 'CEO',
        email: 'ceo@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DEPT_HEAD_CHAIN',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: { relation: 'departmentHeadChain' },
        userAssignments: [],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findUnique.mockResolvedValue({
        id: 'employee-id',
        deletedAt: null,
      });
      mockPrismaService.userDepartment.findFirst.mockResolvedValue({
        userId: 'employee-id',
        departmentId: 'team-id',
        isPrimary: true,
      });

      // 模拟3层结构，中间层没有主管
      mockPrismaService.department.findUnique
        .mockResolvedValueOnce({
          id: 'team-id',
          headId: 'manager-id',
          parentId: 'division-id',
          deletedAt: null,
        })
        .mockResolvedValueOnce({
          id: 'division-id',
          headId: null, // 没有主管
          parentId: 'company-id',
          deletedAt: null,
        })
        .mockResolvedValueOnce({
          id: 'company-id',
          headId: 'ceo-id',
          parentId: null,
          deletedAt: null,
        });

      mockPrismaService.user.findUnique
        .mockResolvedValueOnce({ id: 'employee-id', deletedAt: null })
        .mockResolvedValueOnce(manager)
        .mockResolvedValueOnce(ceo);

      const result = await service.resolve({
        workflowRoleCode: 'WF_DEPT_HEAD_CHAIN',
        context: {
          initiatorUserId: 'employee-id',
        },
      });

      expect(result.users).toHaveLength(2);
      expect(result.users[0].userId).toBe('manager-id');
      expect(result.users[1].userId).toBe('ceo-id');
    });

    it('[测试场景 6.2.3.3] 应该避免重复的主管', async () => {
      const boss = {
        id: 'boss-id',
        displayName: '老板',
        email: 'boss@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DEPT_HEAD_CHAIN',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: { relation: 'departmentHeadChain' },
        userAssignments: [],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findUnique.mockResolvedValue({
        id: 'employee-id',
        deletedAt: null,
      });
      mockPrismaService.userDepartment.findFirst.mockResolvedValue({
        userId: 'employee-id',
        departmentId: 'team-id',
        isPrimary: true,
      });

      // 同一个人担任所有部门的主管
      mockPrismaService.department.findUnique
        .mockResolvedValueOnce({
          id: 'team-id',
          headId: 'boss-id',
          parentId: 'division-id',
          deletedAt: null,
        })
        .mockResolvedValueOnce({
          id: 'division-id',
          headId: 'boss-id',
          parentId: 'company-id',
          deletedAt: null,
        })
        .mockResolvedValueOnce({
          id: 'company-id',
          headId: 'boss-id',
          parentId: null,
          deletedAt: null,
        });

      mockPrismaService.user.findUnique
        .mockResolvedValueOnce({ id: 'employee-id', deletedAt: null })
        .mockResolvedValueOnce(boss)
        .mockResolvedValueOnce(boss)
        .mockResolvedValueOnce(boss);

      const result = await service.resolve({
        workflowRoleCode: 'WF_DEPT_HEAD_CHAIN',
        context: {
          initiatorUserId: 'employee-id',
        },
      });

      // 应该只返回1个（去重）
      expect(result.users).toHaveLength(1);
      expect(result.users[0].userId).toBe('boss-id');
    });

    it('[测试场景 6.2.3.4] 应该在指定级别终止解析 (v2.1.18 ⭐)', async () => {
      const manager = {
        id: 'manager-id',
        displayName: '小组长',
        email: 'manager@ff.com',
        status: 'ACTIVE',
      };
      const director = {
        id: 'director-id',
        displayName: '部门主管',
        email: 'director@ff.com',
        status: 'ACTIVE',
      };
      const vp = {
        id: 'vp-id',
        displayName: '事业部总监',
        email: 'vp@ff.com',
        status: 'ACTIVE',
      };
      const ceo = {
        id: 'ceo-id',
        displayName: 'CEO',
        email: 'ceo@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DEPT_CHAIN_L1',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: { 
          relation: 'departmentHeadChain',
          stopAtLevel: 1  // 在一级部门停止
        },
        userAssignments: [],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findUnique.mockResolvedValue({
        id: 'employee-id',
        deletedAt: null,
      });
      mockPrismaService.userDepartment.findFirst.mockResolvedValue({
        userId: 'employee-id',
        departmentId: 'team-id',
        isPrimary: true,
      });

      // 模拟4层部门结构，从下往上依次计算 level
      // team (level=3) -> dept (level=2) -> division (level=1) -> company (level=0)
      // 每次调用 findUnique 会先查询当前部门信息，然后在 calculateDepartmentLevel 中向上追溯
      
      // 第一次循环：team-id (level=3)
      mockPrismaService.department.findUnique
        .mockResolvedValueOnce({  // 查询 team 的信息
          id: 'team-id',
          headId: 'manager-id',
          parentId: 'dept-id',
          deletedAt: null,
        })
        // calculateDepartmentLevel 会递归查询 parentId 直到找到 root
        .mockResolvedValueOnce({ parentId: 'division-id', deletedAt: null })  // dept -> division
        .mockResolvedValueOnce({ parentId: 'company-id', deletedAt: null })   // division -> company
        .mockResolvedValueOnce({ parentId: null, deletedAt: null })           // company (root, level=0)
        
        // 第二次循环：dept-id (level=2)
        .mockResolvedValueOnce({  // 查询 dept 的信息
          id: 'dept-id',
          headId: 'director-id',
          parentId: 'division-id',
          deletedAt: null,
        })
        .mockResolvedValueOnce({ parentId: 'company-id', deletedAt: null })  // division -> company
        .mockResolvedValueOnce({ parentId: null, deletedAt: null })          // company (root, level=0)
        
        // 第三次循环：division-id (level=1)
        .mockResolvedValueOnce({  // 查询 division 的信息
          id: 'division-id',
          headId: 'vp-id',
          parentId: 'company-id',
          deletedAt: null,
        })
        .mockResolvedValueOnce({ parentId: null, deletedAt: null });         // company (root, level=0)
      
      // 第四次循环：因为 level < stopAtLevel (0 < 1)，所以不会查询 company

      mockPrismaService.user.findUnique
        .mockResolvedValueOnce({ id: 'employee-id', deletedAt: null })
        .mockResolvedValueOnce(manager)
        .mockResolvedValueOnce(director)
        .mockResolvedValueOnce(vp);

      const result = await service.resolve({
        workflowRoleCode: 'WF_DEPT_CHAIN_L1',
        context: {
          initiatorUserId: 'employee-id',
        },
      });

      // 应该返回3个主管（不包括 CEO）
      expect(result.users).toHaveLength(3);
      expect(result.users[0].userId).toBe('manager-id');
      expect(result.users[1].userId).toBe('director-id');
      expect(result.users[2].userId).toBe('vp-id');
      expect(result.users.map((u: any) => u.userId)).not.toContain('ceo-id');
      expect(result.strategy).toBe('SEQUENTIAL');
    });

    it('[测试场景 6.2.3.5] stopAtLevel 为 0 应该包含顶级部门 (v2.1.18 ⭐)', async () => {
      const ceo = {
        id: 'ceo-id',
        displayName: 'CEO',
        email: 'ceo@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DEPT_CHAIN_L0',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: { 
          relation: 'departmentHeadChain',
          stopAtLevel: 0  // 追溯到顶级部门
        },
        userAssignments: [],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findUnique.mockResolvedValue({
        id: 'employee-id',
        deletedAt: null,
      });
      mockPrismaService.userDepartment.findFirst.mockResolvedValue({
        userId: 'employee-id',
        departmentId: 'company-id',
        isPrimary: true,
      });

      // 模拟只有顶级部门的情况 (level=0)
      mockPrismaService.department.findUnique
        .mockResolvedValueOnce({
          id: 'company-id',
          headId: 'ceo-id',
          parentId: null,  // 顶级部门
          deletedAt: null,
        })
        .mockResolvedValueOnce({ parentId: null, deletedAt: null });  // calculateLevel: 已经是 root

      mockPrismaService.user.findUnique
        .mockResolvedValueOnce({ id: 'employee-id', deletedAt: null })
        .mockResolvedValueOnce(ceo);

      const result = await service.resolve({
        workflowRoleCode: 'WF_DEPT_CHAIN_L0',
        context: {
          initiatorUserId: 'employee-id',
        },
      });

      // 应该返回 CEO（包括顶级部门）
      expect(result.users).toHaveLength(1);
      expect(result.users[0].userId).toBe('ceo-id');
    });

    it('[测试场景 6.2.4] 应该成功解析固定用户列表', async () => {
      const approver1 = {
        id: 'approver1-id',
        displayName: '审批人1',
        email: 'approver1@ff.com',
        status: 'ACTIVE',
      };
      const approver2 = {
        id: 'approver2-id',
        displayName: '审批人2',
        email: 'approver2@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_FINANCE_APPROVER',
        ruleType: 'FIXED_USERS',
        ruleConfig: {},
        userAssignments: [
          { user: approver1 },
          { user: approver2 },
        ],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);

      const result = await service.resolve({
        workflowRoleCode: 'WF_FINANCE_APPROVER',
        context: {
          initiatorUserId: 'initiator-id',
        },
      });

      expect(result.users).toHaveLength(2);
      expect(result.users.map((u) => u.userId)).toContain('approver1-id');
      expect(result.users.map((u) => u.userId)).toContain('approver2-id');
      expect(result.resolvedBy).toBe('FIXED_USERS');
    });

    it('[测试场景 6.2.5] 用户不在指定部门应抛出异常', async () => {
      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DIRECT_MANAGER',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: { relation: 'manager' },
        userAssignments: [],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findUnique.mockResolvedValue({
        id: 'employee-id',
        deletedAt: null,
      });
      // 用户不在指定部门
      mockPrismaService.userDepartment.findUnique.mockResolvedValue(null);

      await expect(
        service.resolve({
          workflowRoleCode: 'WF_DIRECT_MANAGER',
          context: {
            initiatorUserId: 'employee-id',
            formData: {
              departmentId: 'dept2-id', // 用户不属于这个部门
            },
          },
        }),
      ).rejects.toThrow(IamUserNotInDepartmentException);
    });

    it('[测试场景 6.2.6] 解析结果为空应抛出异常', async () => {
      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DIRECT_MANAGER',
        ruleType: 'ORGANIZATION_RELATION',
        ruleConfig: {
          relation: 'manager',
          // 无兜底策略
        },
        userAssignments: [],
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findUnique.mockResolvedValue({
        id: 'employee-id',
        deletedAt: null,
      });
      // 员工在部门但没有上级
      mockPrismaService.userDepartment.findFirst.mockResolvedValue({
        userId: 'employee-id',
        departmentId: 'dept-id',
        isPrimary: true,
        managerId: null,
        manager: null,
      });

      await expect(
        service.resolve({
          workflowRoleCode: 'WF_DIRECT_MANAGER',
          context: {
            initiatorUserId: 'employee-id',
          },
        }),
      ).rejects.toThrow(IamWorkflowRoleResolveEmptyException);
    });

    it('[测试场景 6.2.7] 流程角色不存在应抛出异常', async () => {
      mockPrismaService.workflowRole.findUnique.mockResolvedValue(null);

      await expect(
        service.resolve({
          workflowRoleCode: 'WF_NON_EXISTENT',
          context: {
            initiatorUserId: 'employee-id',
          },
        }),
      ).rejects.toThrow(NotFoundException);
      await expect(
        service.resolve({
          workflowRoleCode: 'WF_NON_EXISTENT',
          context: {
            initiatorUserId: 'employee-id',
          },
        }),
      ).rejects.toThrow("Workflow role with code 'WF_NON_EXISTENT' not found");
    });
  });

  // ==================== 6.3 用户分配 ====================

  describe('assignUsers', () => {
    it('[测试场景 6.3.1] 应该成功分配用户到 FIXED_USERS 类型', async () => {
      const user1 = {
        id: 'user1-id',
        username: 'user1',
        displayName: '用户1',
        email: 'user1@ff.com',
        status: 'ACTIVE',
      };
      const user2 = {
        id: 'user2-id',
        username: 'user2',
        displayName: '用户2',
        email: 'user2@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_FIXED_APPROVER',
        ruleType: 'FIXED_USERS',
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findMany.mockResolvedValue([user1, user2]);
      mockPrismaService.workflowRoleUser.findMany.mockResolvedValue([]);
      mockPrismaService.workflowRoleUser.createMany.mockResolvedValue({ count: 2 });
      mockPrismaService.workflowRole.findUnique.mockResolvedValueOnce(workflowRole).mockResolvedValueOnce({
        ...workflowRole,
        userAssignments: [
          { user: user1 },
          { user: user2 },
        ],
      });

      const result = await service.assignUsers('wf-role-id', ['user1-id', 'user2-id']);

      expect(result).toHaveLength(2);
      expect(result.map((u: any) => u.id)).toContain('user1-id');
      expect(result.map((u: any) => u.id)).toContain('user2-id');
    });

    it('[测试场景 6.3.2] 非 FIXED_USERS 类型不能分配用户', async () => {
      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_DIRECT_MANAGER',
        ruleType: 'ORGANIZATION_RELATION',
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);

      await expect(service.assignUsers('wf-role-id', ['user-id'])).rejects.toThrow(
        BadRequestException,
      );
      await expect(service.assignUsers('wf-role-id', ['user-id'])).rejects.toThrow(
        "Only 'FIXED_USERS' type supports user assignment",
      );
    });

    it('[测试场景 6.3.3] 重复分配用户应幂等', async () => {
      const user = {
        id: 'user-id',
        username: 'user',
        displayName: '用户',
        email: 'user@ff.com',
        status: 'ACTIVE',
      };

      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_FIXED_APPROVER',
        ruleType: 'FIXED_USERS',
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.user.findMany.mockResolvedValue([user]);
      // 用户已存在
      mockPrismaService.workflowRoleUser.findMany.mockResolvedValue([
        {
          workflowRoleId: 'wf-role-id',
          userId: 'user-id',
        },
      ]);
      mockPrismaService.workflowRole.findUnique.mockResolvedValueOnce(workflowRole).mockResolvedValueOnce({
        ...workflowRole,
        userAssignments: [{ user }],
      });

      const result = await service.assignUsers('wf-role-id', ['user-id']);

      expect(result).toHaveLength(1);
      expect(result[0].id).toBe('user-id');
      // 不应该调用 createMany
      expect(mockPrismaService.workflowRoleUser.createMany).not.toHaveBeenCalled();
    });
  });

  // ==================== 6.4 更新和删除 ====================

  describe('update', () => {
    it('[测试场景 6.4.1] 应该成功更新流程角色', async () => {
      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_TEST',
        name: '原名称',
        ruleType: 'FIXED_USERS',
        ruleConfig: {},
      };

      const updatedWorkflowRole = {
        ...workflowRole,
        name: '新名称',
        description: '新描述',
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValue(workflowRole);
      mockPrismaService.workflowRole.findFirst.mockResolvedValue(null);
      mockPrismaService.workflowRole.update.mockResolvedValue(updatedWorkflowRole);

      const result = await service.update('wf-role-id', {
        name: '新名称',
        description: '新描述',
      });

      expect(result.name).toBe('新名称');
      expect(result.description).toBe('新描述');
      expect(result.code).toBe('WF_TEST'); // code 不变
    });
  });

  describe('remove', () => {
    it('[测试场景 6.4.2] 应该成功删除流程角色', async () => {
      const workflowRole = {
        id: 'wf-role-id',
        code: 'WF_TEST',
        name: '测试角色',
      };

      mockPrismaService.workflowRole.findUnique.mockResolvedValueOnce(workflowRole).mockResolvedValueOnce(null);
      mockPrismaService.workflowRole.delete.mockResolvedValue(workflowRole);

      const result = await service.remove('wf-role-id');

      expect(result.message).toBe('Workflow role deleted successfully');

      await expect(service.findOne('wf-role-id')).rejects.toThrow(NotFoundException);
    });
  });

  describe('findAll', () => {
    it('应该返回所有流程角色', async () => {
      const mockWorkflowRoles = [
        {
          id: 'wf-role-1',
          code: 'WF_ROLE_001',
          name: 'Role 1',
          ruleType: 'FIXED_USERS',
          _count: { userAssignments: 5 },
        },
        {
          id: 'wf-role-2',
          code: 'WF_ROLE_002',
          name: 'Role 2',
          ruleType: 'ORGANIZATION_RELATION',
          _count: { userAssignments: 0 },
        },
      ];

      mockPrismaService.workflowRole.findMany.mockResolvedValue(mockWorkflowRoles);

      const result = await service.findAll({});

      expect(Array.isArray(result)).toBe(true);
      expect(result.length).toBe(2);
      expect(result[0]).toHaveProperty('userCount');
    });
  });
});
