import { Test, TestingModule } from '@nestjs/testing';
import { NotFoundException, BadRequestException } from '@nestjs/common';
import { UsersService } from '@/modules/organization/users/users.service';
import { PrismaService } from '@/core/database/prisma/prisma.service';
import { UserDepartmentsService } from '@/modules/organization/user-departments/user-departments.service';
import {
  IamUserEmailExistsException,
  IamUsernameExistsException,
  IamEmployeeIdExistsException,
  IamUserAlreadyActiveException,
  IamTerminatedUserCannotActivateException,
} from '@/modules/organization/exceptions';
import { CreateUserDto, UpdateUserDto, UserStatus, TerminateUserDto, ActivateUserDto } from '@/modules/organization/users/dto/user.dto';

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

  // Mock PrismaService
  const mockPrismaService: any = {
    user: {
      findFirst: jest.fn(),
      findUnique: jest.fn(),
      findMany: jest.fn(),
      create: jest.fn(),
      update: jest.fn(),
      count: jest.fn(),
    },
    userRole: {
      findMany: jest.fn(),
      create: jest.fn(),
      createMany: jest.fn(),
      deleteMany: jest.fn(),
      findFirst: jest.fn(),
      count: jest.fn(), // 新增：用于最后管理员检查
    },
    userDepartment: {
      count: jest.fn(),
    },
    role: {
      findMany: jest.fn(),
    },
    permission: {
      findMany: jest.fn(),
    },
    $transaction: jest.fn((callback: any) => callback(mockPrismaService)),
  };

  // Mock UserDepartmentsService
  const mockUserDepartmentsService = {
    create: jest.fn(),
    findAll: jest.fn(),
    findOne: jest.fn(),
    update: jest.fn(),
    remove: jest.fn(),
    addUserToDepartment: jest.fn(),
    removeUserFromDepartment: jest.fn(),
    getUserDepartments: jest.fn(),
  };

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

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

    // Clear all mocks before each test
    jest.clearAllMocks();
  });

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

  describe('create', () => {
    const createUserDto: CreateUserDto = {
      username: 'testuser',
      email: 'test@example.com',
      password: 'Test@1234',
      displayName: 'Test User',
      employeeId: 'EMP001',
      region: 'CN',
    };

    const mockUser = {
      id: 'user-id-1',
      username: 'testuser',
      email: 'test@example.com',
      displayName: 'Test User',
      employeeId: 'EMP001',
      defaultRegion: 'CN',
      status: 'ACTIVE',
      source: 'LOCAL',
      departmentMemberships: [],
      createdAt: new Date(),
      updatedAt: new Date(),
      deletedAt: null,
    };

    it('[测试场景 1.1.1] 应该成功创建本地用户', async () => {
      mockPrismaService.user.findFirst
        .mockResolvedValueOnce(null) // username check
        .mockResolvedValueOnce(null) // email check
        .mockResolvedValueOnce(null); // employeeId check

      mockPrismaService.user.create.mockResolvedValue(mockUser);

      const result = await service.create(createUserDto);

      expect(result).toEqual(mockUser);
      expect(result.id).toBeDefined();
      expect(result.username).toBe('testuser');
      expect(result.email).toBe('test@example.com');
      expect(result.status).toBe('ACTIVE');
      expect(result.source).toBe('LOCAL');
      expect(mockPrismaService.user.findFirst).toHaveBeenCalledTimes(3);
      expect(mockPrismaService.user.create).toHaveBeenCalledWith({
        data: expect.objectContaining({
          username: createUserDto.username,
          email: createUserDto.email,
          displayName: createUserDto.displayName,
          employeeId: createUserDto.employeeId,
          defaultRegion: 'CN',
        }),
        include: expect.any(Object),
      });
    });

    it('[测试场景 1.1.2] 应该成功创建 LDAP 用户（v2.1.1）', async () => {
      // LDAP 用户通过外部同步创建，这里模拟已创建的 LDAP 用户
      const ldapUser = {
        ...mockUser,
        username: 'ldapuser',
        email: 'ldap@example.com',
        source: 'LDAP',
        ldapDn: 'CN=LDAP User,OU=Users,DC=company,DC=com',
        passwordHash: null,
      };

      // findOne uses findUnique, not findFirst
      mockPrismaService.user.findUnique.mockResolvedValue(ldapUser as any);

      const result = await service.findOne(ldapUser.id);

      expect(result).toBeDefined();
      if (result) {
        expect(result.source).toBe('LDAP');
        expect((result as any).ldapDn).toBeDefined();
      }
    });

    it('[测试场景 1.1.3] 应该成功创建 Entra ID 用户（v2.1.1）', async () => {
      // Entra ID 用户通过外部同步创建，这里模拟已创建的 Entra 用户
      const entraUser = {
        ...mockUser,
        username: 'entrauser',
        email: 'entra@example.com',
        source: 'ENTRA',
        externalId: 'entra-id-12345',
        externalSource: 'ENTRA_ID',
        passwordHash: null,
      };

      // findOne uses findUnique, not findFirst
      mockPrismaService.user.findUnique.mockResolvedValue(entraUser as any);

      const result = await service.findOne(entraUser.id);

      expect(result).toBeDefined();
      if (result) {
        expect(result.source).toBe('ENTRA');
        expect((result as any).externalId).toBeDefined();
      }
    });

    it('[测试场景 1.1.4] 用户名重复应抛出异常', async () => {
      mockPrismaService.user.findFirst.mockResolvedValueOnce(mockUser);

      await expect(service.create(createUserDto)).rejects.toThrow(
        IamUsernameExistsException,
      );
      expect(mockPrismaService.user.create).not.toHaveBeenCalled();
    });

    it('[测试场景 1.1.5] 邮箱重复应抛出异常', async () => {
      mockPrismaService.user.findFirst
        .mockResolvedValueOnce(null) // username check passes
        .mockResolvedValueOnce(mockUser); // email check fails

      await expect(service.create(createUserDto)).rejects.toThrow(
        IamUserEmailExistsException,
      );
      expect(mockPrismaService.user.create).not.toHaveBeenCalled();
    });

    it('should throw IamEmployeeIdExistsException if employeeId exists', async () => {
      mockPrismaService.user.findFirst
        .mockResolvedValueOnce(null) // username check
        .mockResolvedValueOnce(null) // email check
        .mockResolvedValueOnce(mockUser); // employeeId check fails

      await expect(service.create(createUserDto)).rejects.toThrow(
        IamEmployeeIdExistsException,
      );
      expect(mockPrismaService.user.create).not.toHaveBeenCalled();
    });

    it('[测试场景 1.1.6] LDAP 用户不能有密码（v2.1.1）', async () => {
      const invalidDto: CreateUserDto = {
        username: 'ldapuser',
        email: 'ldap@example.com',
        displayName: 'LDAP用户',
        password: 'some-password', // LDAP用户不应该有密码
        source: 'LDAP' as any,
        region: 'CN',
      };

      await expect(service.create(invalidDto)).rejects.toThrow(BadRequestException);
      await expect(service.create(invalidDto)).rejects.toThrow('LDAP users cannot have a password');
    });

    it('ENTRA 用户不能有密码（v2.1.1）', async () => {
      const invalidDto: CreateUserDto = {
        username: 'entrauser',
        email: 'entra@example.com',
        displayName: 'ENTRA用户',
        password: 'some-password',
        source: 'ENTRA' as any,
        region: 'CN',
      };

      await expect(service.create(invalidDto)).rejects.toThrow(BadRequestException);
      await expect(service.create(invalidDto)).rejects.toThrow('ENTRA users cannot have a password');
    });

    it('LOCAL 用户必须有密码（v2.1.1）', async () => {
      const invalidDto: Partial<CreateUserDto> = {
        username: 'localuser',
        email: 'local@example.com',
        displayName: 'LOCAL用户',
        // password 缺失
        source: 'LOCAL' as any,
        region: 'CN',
      };

      await expect(service.create(invalidDto as CreateUserDto)).rejects.toThrow(BadRequestException);
      await expect(service.create(invalidDto as CreateUserDto)).rejects.toThrow('LOCAL users must have a password');
    });
  });

  describe('findAll', () => {
    const mockUsers = [
      {
        id: 'user-id-1',
        username: 'user1',
        email: 'user1@example.com',
        displayName: 'User 1',
        status: 'ACTIVE',
        defaultRegion: 'CN',
        departmentMemberships: [],
        deletedAt: null,
      },
      {
        id: 'user-id-2',
        username: 'user2',
        email: 'user2@example.com',
        displayName: 'User 2',
        status: 'ACTIVE',
        defaultRegion: 'US',
        departmentMemberships: [],
        deletedAt: null,
      },
    ];

    it('should return paginated users', async () => {
      mockPrismaService.user.findMany.mockResolvedValue(mockUsers);
      mockPrismaService.user.count.mockResolvedValue(2);

      const result = await service.findAll({ page: 1, pageSize: 10 });

      expect(result.items).toHaveLength(2);
      expect(result.total).toBe(2);
      expect(result.page).toBe(1);
      expect(result.limit).toBe(10);
      expect(result).toHaveProperty('totalPages');
      expect(result).toHaveProperty('hasNext');
      expect(result).toHaveProperty('hasPrev');
    });

    it('should filter by keyword', async () => {
      const keyword = 'user1';
      mockPrismaService.user.findMany.mockResolvedValue([mockUsers[0]]);
      mockPrismaService.user.count.mockResolvedValue(1);

      await service.findAll({ keyword, page: 1, pageSize: 10 });

      expect(mockPrismaService.user.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({
            OR: expect.any(Array),
          }),
        }),
      );
    });

    it('should filter by status', async () => {
      mockPrismaService.user.findMany.mockResolvedValue([mockUsers[0]]);
      mockPrismaService.user.count.mockResolvedValue(1);

      await service.findAll({ status: UserStatus.ACTIVE, page: 1, pageSize: 10 });

      expect(mockPrismaService.user.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({
            status: UserStatus.ACTIVE,
          }),
        }),
      );
    });

    it('should filter by region', async () => {
      const region = 'CN';
      mockPrismaService.user.findMany.mockResolvedValue([mockUsers[0]]);
      mockPrismaService.user.count.mockResolvedValue(1);

      await service.findAll({ region, page: 1, pageSize: 10 });

      expect(mockPrismaService.user.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({
            defaultRegion: region,
          }),
        }),
      );
    });
  });

  describe('findOne', () => {
    const mockUser = {
      id: 'user-id-1',
      username: 'testuser',
      email: 'test@example.com',
      displayName: 'Test User',
      status: 'ACTIVE',
      departmentMemberships: [],
      deletedAt: null,
    };

    it('should return a user by id', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(mockUser);

      const result = await service.findOne('user-id-1');

      expect(result).toEqual(mockUser);
      expect(mockPrismaService.user.findUnique).toHaveBeenCalledWith(
        expect.objectContaining({
          where: { id: 'user-id-1', deletedAt: null },
        }),
      );
    });

    it('should throw NotFoundException if user not found', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(null);

      await expect(service.findOne('non-existent-id')).rejects.toThrow(
        NotFoundException,
      );
    });
  });

  describe('update', () => {
    const updateUserDto: UpdateUserDto = {
      displayName: 'Updated Name',
      phone: '1234567890',
    };

    const mockExistingUser = {
      id: 'user-id-1',
      username: 'testuser',
      email: 'test@example.com',
      displayName: 'Test User',
      employeeId: 'EMP001',
      status: 'ACTIVE',
      departmentMemberships: [],
      deletedAt: null,
    };

    const mockUpdatedUser = {
      ...mockExistingUser,
      displayName: 'Updated Name',
      phone: '1234567890',
    };

    it('should update user successfully', async () => {
      // Clear previous mocks
      jest.clearAllMocks();
      
      mockPrismaService.user.findUnique.mockResolvedValueOnce(mockExistingUser);
      mockPrismaService.user.findFirst.mockResolvedValueOnce(null); // no conflicts
      mockPrismaService.user.update.mockResolvedValueOnce(mockUpdatedUser);

      const result = await service.update('user-id-1', updateUserDto);

      expect(result).toEqual(mockUpdatedUser);
      expect(mockPrismaService.user.update).toHaveBeenCalledWith(
        expect.objectContaining({
          where: { id: 'user-id-1' },
          data: updateUserDto,
        }),
      );
    });

    it('should throw NotFoundException if user not found', async () => {
      // Clear previous mocks
      jest.clearAllMocks();
      
      mockPrismaService.user.findUnique.mockResolvedValueOnce(null);

      await expect(service.update('non-existent-id', updateUserDto)).rejects.toThrow(
        NotFoundException,
      );
      expect(mockPrismaService.user.update).not.toHaveBeenCalled();
    });

    it('should throw IamUserEmailExistsException if email is taken by another user', async () => {
      const updateWithEmail = { ...updateUserDto, email: 'newemail@example.com' };
      const anotherUser = { ...mockExistingUser, id: 'another-user-id', email: 'newemail@example.com' };

      // Clear previous mocks completely
      jest.restoreAllMocks();
      jest.clearAllMocks();
      
      // Mock findUnique for user existence check (line 335)
      mockPrismaService.user.findUnique = jest.fn().mockResolvedValue(mockExistingUser);
      
      // Mock findFirst for email conflict check (line 345)
      // This should return anotherUser to indicate email conflict
      mockPrismaService.user.findFirst = jest.fn().mockResolvedValue(anotherUser);

      await expect(service.update('user-id-1', updateWithEmail)).rejects.toThrow(
        IamUserEmailExistsException,
      );
      
      // Verify that update was not called
      expect(mockPrismaService.user.update).not.toHaveBeenCalled();
      
      // Verify that findFirst was called to check email conflict
      expect(mockPrismaService.user.findFirst).toHaveBeenCalledWith({
        where: { 
          email: 'newemail@example.com', 
          deletedAt: null, 
          id: { not: 'user-id-1' } 
        },
      });
    });
  });

  describe('remove (soft delete)', () => {
    const mockUser = {
      id: 'user-id-1',
      username: 'testuser',
      email: 'test@example.com',
      status: 'ACTIVE',
      departmentMemberships: [],
      deletedAt: null,
      roles: [], // 新增：用于最后管理员检查
    };

    const mockDeletedUser = {
      ...mockUser,
      status: 'INACTIVE',
      deletedAt: new Date(),
    };

    it('[测试场景 1.3.1] should soft delete a user', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(mockUser);
      mockPrismaService.userRole.count.mockResolvedValue(0); // 非管理员或有其他管理员
      mockPrismaService.userDepartment.count.mockResolvedValue(0); // 无下属
      mockPrismaService.user.update.mockResolvedValue(mockDeletedUser);

      const result = await service.remove('user-id-1');

      expect(result).toEqual({ message: 'User deleted successfully' });
      expect(mockPrismaService.user.update).toHaveBeenCalledWith(
        expect.objectContaining({
          where: { id: 'user-id-1' },
          data: expect.objectContaining({
            deletedAt: expect.any(Date),
            status: 'TERMINATED',
          }),
        }),
      );
    });

    it('[测试场景 1.3.2] should prevent deleting user with subordinates', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(mockUser);
      mockPrismaService.userRole.count.mockResolvedValue(0);
      mockPrismaService.userDepartment.count.mockResolvedValue(3); // 有 3 个下属

      await expect(service.remove('user-id-1')).rejects.toThrow(BadRequestException);
      await expect(service.remove('user-id-1')).rejects.toThrow('manager of 3 employees');

      expect(mockPrismaService.user.update).not.toHaveBeenCalled();
    });

    it('should throw NotFoundException if user not found', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(null);

      await expect(service.remove('non-existent-id')).rejects.toThrow(
        NotFoundException,
      );
      expect(mockPrismaService.user.update).not.toHaveBeenCalled();
    });
  });

  describe('assignRoles', () => {
    const mockUser = {
      id: 'user-id-1',
      username: 'testuser',
      status: 'ACTIVE',
      departmentMemberships: [],
      deletedAt: null,
    };

    const roleIds = ['role-id-1', 'role-id-2'];

    it('should assign roles to user', async () => {
      const mockRoles = [
        { id: 'role-id-1', code: 'ADMIN', name: 'Admin' },
        { id: 'role-id-2', code: 'USER', name: 'User' },
      ];
      
      const mockAssignedRoles = [
        { 
          role: { id: 'role-id-1', code: 'ADMIN', name: 'Admin' },
          organizationId: null,
        },
        { 
          role: { id: 'role-id-2', code: 'USER', name: 'User' },
          organizationId: null,
        },
      ];
      
      mockPrismaService.user.findUnique.mockResolvedValue(mockUser);
      mockPrismaService.role.findMany.mockResolvedValue(mockRoles);
      mockPrismaService.$transaction.mockResolvedValue(undefined);
      mockPrismaService.userRole.findMany.mockResolvedValue(mockAssignedRoles);

      const result = await service.assignRoles('user-id-1', { roleIds });

      expect(result).toEqual({
        userId: 'user-id-1',
        assignedRoles: [
          { 
            roleId: 'role-id-1', 
            roleCode: 'ADMIN', 
            roleName: 'Admin', 
            organizationId: null,
            isGlobal: true,
            organization: null,
          },
          { 
            roleId: 'role-id-2', 
            roleCode: 'USER', 
            roleName: 'User', 
            organizationId: null,
            isGlobal: true,
            organization: null,
          },
        ],
      });
      expect(mockPrismaService.role.findMany).toHaveBeenCalledWith({
        where: { id: { in: roleIds } },
      });
      expect(mockPrismaService.$transaction).toHaveBeenCalled();
      expect(mockPrismaService.userRole.findMany).toHaveBeenCalledWith({
        where: { userId: 'user-id-1' },
        include: { 
          role: true,
          organization: {
            select: {
              id: true,
              name: true,
              code: true,
            },
          },
        },
      });
    });

    it('should throw NotFoundException if user not found', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(null);

      await expect(service.assignRoles('non-existent-id', { roleIds })).rejects.toThrow(
        NotFoundException,
      );
    });
  });

  describe('removeRole', () => {
    const mockUser = {
      id: 'user-id-1',
      username: 'testuser',
      deletedAt: null,
    };

    it('should remove a role from user', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(mockUser);
      mockPrismaService.userRole.deleteMany.mockResolvedValue({ count: 1 });

      await service.removeRole('user-id-1', 'role-id-1');

      expect(mockPrismaService.userRole.deleteMany).toHaveBeenCalledWith({
        where: {
          userId: 'user-id-1',
          roleId: 'role-id-1',
        },
      });
    });

    it('should throw NotFoundException if user not found', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(null);

      await expect(service.removeRole('non-existent-id', 'role-id-1')).rejects.toThrow(
        NotFoundException,
      );
    });

    it('should throw NotFoundException if role assignment not found', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(mockUser);
      mockPrismaService.userRole.deleteMany.mockResolvedValue({ count: 0 });

      await expect(service.removeRole('user-id-1', 'role-id-1')).rejects.toThrow(
        NotFoundException,
      );
    });
  });

  describe('getUserPermissions', () => {
    const mockUserWithRoles = {
      id: 'user-id-1',
      username: 'testuser',
      deletedAt: null,
      roles: [
        {
          role: {
            enabled: true,
            permissions: [
              {
                permission: {
                  code: 'user:read',
                  name: 'Read Users',
                },
              },
              {
                permission: {
                  code: 'user:write',
                  name: 'Write Users',
                },
              },
            ],
          },
        },
      ],
    };

    const mockUserWithNoRoles = {
      id: 'user-id-2',
      username: 'testuser2',
      deletedAt: null,
      roles: [],
    };

    it('should return aggregated user permissions', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(mockUserWithRoles);

      const result = await service.getUserPermissions('user-id-1');

      expect(result).toHaveProperty('userId', 'user-id-1');
      expect(result).toHaveProperty('permissions');
      expect(result).toHaveProperty('roles');
      expect(Array.isArray(result.permissions)).toBe(true);
      expect(Array.isArray(result.roles)).toBe(true);
      expect(mockPrismaService.user.findUnique).toHaveBeenCalledWith(
        expect.objectContaining({
          where: { id: 'user-id-1', deletedAt: null },
          include: expect.any(Object),
        }),
      );
    });

    it('should return empty permissions array if user has no roles', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(mockUserWithNoRoles);

      const result = await service.getUserPermissions('user-id-2');

      expect(result).toEqual({
        userId: 'user-id-2',
        organizationId: null,
        permissions: [],
        roles: [],
      });
    });

    it('should throw NotFoundException if user not found', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(null);

      await expect(service.getUserPermissions('non-existent-id')).rejects.toThrow(
        NotFoundException,
      );
    });
  });

  describe('validateEmployeeId', () => {
    it('should pass if employeeId is unique', async () => {
      mockPrismaService.user.findFirst.mockResolvedValue(null);

      await expect(service.validateEmployeeId('EMP001')).resolves.not.toThrow();
    });

    it('should throw IamEmployeeIdExistsException if employeeId exists', async () => {
      mockPrismaService.user.findFirst.mockResolvedValue({
        id: 'existing-user-id',
        employeeId: 'EMP001',
      });

      await expect(service.validateEmployeeId('EMP001')).rejects.toThrow(
        IamEmployeeIdExistsException,
      );
    });

    it('should pass if employeeId is null', async () => {
      await expect(service.validateEmployeeId(null)).resolves.not.toThrow();
      expect(mockPrismaService.user.findFirst).not.toHaveBeenCalled();
    });

    it('should exclude specified user when checking uniqueness', async () => {
      mockPrismaService.user.findFirst.mockResolvedValue(null);

      await service.validateEmployeeId('EMP001', 'user-id-to-exclude');

      expect(mockPrismaService.user.findFirst).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({
            employeeId: 'EMP001',
            id: { not: 'user-id-to-exclude' },
          }),
        }),
      );
    });
  });

  describe('terminate', () => {
    const mockUser = {
      id: 'user-id-1',
      username: 'testuser',
      status: 'ACTIVE',
      deletedAt: null,
      metadata: {},
    };

    const mockTerminatedUser = {
      ...mockUser,
      status: 'TERMINATED',
      terminatedAt: new Date(),
    };

    it('should terminate an active user', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(mockUser);
      mockPrismaService.user.update.mockResolvedValue(mockTerminatedUser);

      const result = await service.terminate('user-id-1', { reason: 'Test reason' });

      expect(result).toEqual({
        userId: mockTerminatedUser.id,
        status: 'TERMINATED',
        terminatedAt: mockTerminatedUser.terminatedAt,
      });
      expect(mockPrismaService.user.update).toHaveBeenCalledWith({
        where: { id: 'user-id-1' },
        data: expect.objectContaining({
          status: 'TERMINATED',
          terminatedAt: expect.any(Date),
        }),
      });
    });

    it('should throw NotFoundException if user not found', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(null);

      await expect(service.terminate('non-existent-id', { reason: 'Test' })).rejects.toThrow(
        NotFoundException,
      );
    });
  });

  describe('activate', () => {
    const mockUser = {
      id: 'user-id-1',
      username: 'testuser',
      status: 'INACTIVE',
      deletedAt: null,
      metadata: {},
    };

    const mockActivatedUser = {
      ...mockUser,
      status: 'ACTIVE',
    };

    it('should activate an inactive user', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(mockUser);
      mockPrismaService.user.update.mockResolvedValue(mockActivatedUser);

      const result = await service.activate('user-id-1', {});

      expect(result).toEqual({
        userId: 'user-id-1',
        status: 'ACTIVE',
        activatedAt: expect.any(Date),
      });
      expect(mockPrismaService.user.update).toHaveBeenCalledWith({
        where: { id: 'user-id-1' },
        data: expect.objectContaining({
          status: 'ACTIVE',
        }),
      });
    });

    it('should throw NotFoundException if user not found', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(null);

      await expect(service.activate('non-existent-id', {})).rejects.toThrow(
        NotFoundException,
      );
    });

    it('should throw error if user is already active', async () => {
      const activeUser = { ...mockUser, status: 'ACTIVE' };
      mockPrismaService.user.findUnique.mockResolvedValue(activeUser);

      await expect(service.activate('user-id-1', {})).rejects.toThrow(
        IamUserAlreadyActiveException,
      );
    });

    it('should throw error if trying to activate terminated user', async () => {
      const terminatedUser = { ...mockUser, status: 'TERMINATED' };
      mockPrismaService.user.findUnique.mockResolvedValue(terminatedUser);

      await expect(service.activate('user-id-1', {})).rejects.toThrow(
        IamTerminatedUserCannotActivateException,
      );
    });
  });

  // ==================== v2.1.1 新功能测试 ====================
  
  describe('updateStatus (v2.1.1 新增)', () => {
    const mockUser = {
      id: 'user-id-1',
      username: 'testuser',
      status: 'ACTIVE',
      deletedAt: null,
      roles: [],
    };

    it('[测试场景 1.2.1] 应该成功切换 ACTIVE → INACTIVE', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(mockUser);
      mockPrismaService.user.update.mockResolvedValue({
        ...mockUser,
        status: 'INACTIVE',
      });

      const result = await service.updateStatus('user-id-1', {
        status: 'INACTIVE' as any,
        reason: '长期休假',
      });

      expect(result.status).toBe('INACTIVE');
      expect(mockPrismaService.user.update).toHaveBeenCalled();
    });

    it('应该成功切换 INACTIVE → ACTIVE', async () => {
      const inactiveUser = { ...mockUser, status: 'INACTIVE' };
      mockPrismaService.user.findUnique.mockResolvedValue(inactiveUser);
      mockPrismaService.user.update.mockResolvedValue({
        ...inactiveUser,
        status: 'ACTIVE',
      });

      const result = await service.updateStatus('user-id-1', {
        status: 'ACTIVE' as any,
        reason: '恢复工作',
      });

      expect(result.status).toBe('ACTIVE');
    });

    it('应该拒绝无效的状态转换', async () => {
      const terminatedUser = { ...mockUser, status: 'TERMINATED', roles: [] };
      mockPrismaService.user.findUnique.mockResolvedValue(terminatedUser);

      await expect(
        service.updateStatus('user-id-1', {
          status: 'ACTIVE' as any,
        })
      ).rejects.toThrow(BadRequestException);
      await expect(
        service.updateStatus('user-id-1', {
          status: 'ACTIVE' as any,
        })
      ).rejects.toThrow('Invalid status transition');
    });

    it('[测试场景 1.2.2] 应该防止停用最后一个管理员', async () => {
      const adminUser = {
        ...mockUser,
        roles: [{
          role: { code: 'ADMIN', enabled: true },
        }],
      };

      mockPrismaService.user.findUnique.mockResolvedValue(adminUser);
      mockPrismaService.userRole.count.mockResolvedValue(0); // 没有其他管理员

      await expect(
        service.updateStatus('user-id-1', {
          status: 'INACTIVE' as any,
        })
      ).rejects.toThrow(BadRequestException);
      await expect(
        service.updateStatus('user-id-1', {
          status: 'INACTIVE' as any,
        })
      ).rejects.toThrow('last active administrator');
    });

    it('应该允许停用管理员（有其他管理员时）', async () => {
      const adminUser = {
        ...mockUser,
        roles: [{
          role: { code: 'ADMIN', enabled: true },
        }],
      };

      mockPrismaService.user.findUnique.mockResolvedValue(adminUser);
      mockPrismaService.userRole.count.mockResolvedValue(2); // 有其他管理员
      mockPrismaService.user.update.mockResolvedValue({
        ...adminUser,
        status: 'INACTIVE',
      });

      const result = await service.updateStatus('user-id-1', {
        status: 'INACTIVE' as any,
      });

      expect(result.status).toBe('INACTIVE');
    });
  });

  describe('最后管理员保护 (v2.1.1 新增)', () => {
    const adminUser = {
      id: 'admin-id-1',
      username: 'admin',
      status: 'ACTIVE',
      deletedAt: null,
      roles: [{
        role: { code: 'ADMIN', enabled: true },
      }],
    };

    it('应该防止删除最后一个管理员', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(adminUser);
      mockPrismaService.userRole.count.mockResolvedValue(0); // 没有其他管理员
      mockPrismaService.userDepartment.count.mockResolvedValue(0);

      await expect(service.remove('admin-id-1')).rejects.toThrow(BadRequestException);
      await expect(service.remove('admin-id-1')).rejects.toThrow('last active administrator');
    });

    it('应该允许删除管理员（有其他管理员时）', async () => {
      mockPrismaService.user.findUnique.mockResolvedValue(adminUser);
      mockPrismaService.userRole.count.mockResolvedValue(2); // 有其他管理员
      mockPrismaService.userDepartment.count.mockResolvedValue(0);
      mockPrismaService.user.update.mockResolvedValue({
        ...adminUser,
        status: 'TERMINATED',
        deletedAt: new Date(),
      });

      const result = await service.remove('admin-id-1');

      expect(result.message).toBe('User deleted successfully');
    });
  });
});
