import { Injectable, NotFoundException, Logger, BadRequestException, Inject, forwardRef } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import {
  CreateUserDto,
  UpdateUserDto,
  AssignRolesDto,
  RoleAssignmentDto,
  TerminateUserDto,
  ActivateUserDto,
  RestoreUserDto,
  UpdateUserStatusDto,
  UserQueryDto,
  UserStatus,
  UserSource,
  ImportUserMembershipsDto,
  ImportUserMembershipDto,
} from './dto/user.dto';
import {
  IamUserEmailExistsException,
  IamUsernameExistsException,
  IamEmployeeIdExistsException,
  IamManagerLoopException,
  IamManagerSelfReferenceException,
  IamUserAlreadyActiveException,
  IamTerminatedUserCannotActivateException,
} from '../exceptions';
import { UserDepartmentsService } from '../user-departments/user-departments.service';
import { AuthCacheService } from '../auth/services/auth-cache.service';
import { SkipAssertAccess } from '@common/decorators/skip-assert-access.decorator';
import * as bcrypt from 'bcrypt';

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

  constructor(
    private prisma: PrismaService,
    @Inject(forwardRef(() => UserDepartmentsService))
    private userDepartmentsService: UserDepartmentsService,
    private authCache: AuthCacheService,
  ) {}

  /**
   * Validate manager relationship to prevent loops
   * NOTE: This method is deprecated. Manager validation is now handled by UserDepartmentService
   * for each department membership separately. Keeping this for backward compatibility.
   */
  async validateManagerRelation(userId: string, managerId: string | null): Promise<void> {
    // This method is deprecated and should not be used
    // Manager validation is now done in UserDepartmentService
    this.logger.warn('validateManagerRelation is deprecated. Use UserDepartmentService instead.');
  }

  /**
   * Validate nullable unique field (employeeId)
   */
  async validateEmployeeId(employeeId: string | null, excludeUserId?: string): Promise<void> {
    if (!employeeId) return;

    const existing = await this.prisma.user.findFirst({
      where: {
        employeeId,
        deletedAt: null,
        ...(excludeUserId ? { id: { not: excludeUserId } } : {}),
      },
    });

    if (existing) {
      throw new IamEmployeeIdExistsException(employeeId);
    }
  }

  /**
   * Create a new user
   * NOTE: Department, position, and manager are now managed via UserDepartment table.
   * Use UserDepartmentService.addUserDepartment() after creating the user to set these relationships.
   */
  async create(createUserDto: CreateUserDto) {
    const { 
      username, email, password, displayName, employeeId, region, hiredAt, phone, avatar,
      departmentId, positionId, managerId, title, source // ✅ v2.1.1: 提取身份源
    } = createUserDto;

    // v2.1.1: 验证身份源和密码的一致性
    const userSource = source || 'LOCAL';
    
    if ((userSource === 'LDAP' || userSource === 'ENTRA') && password) {
      throw new BadRequestException(
        `${userSource} users cannot have a password. Authentication is handled externally.`
      );
    }

    if (userSource === 'LOCAL' && !password) {
      throw new BadRequestException(
        'LOCAL users must have a password.'
      );
    }

    // Check if username exists
    const existingUsername = await this.prisma.user.findFirst({
      where: { username, deletedAt: null },
    });
    if (existingUsername) {
      throw new IamUsernameExistsException(username);
    }

    // Check if email exists
    const existingEmail = await this.prisma.user.findFirst({
      where: { email, deletedAt: null },
    });
    if (existingEmail) {
      throw new IamUserEmailExistsException(email);
    }

    // Validate employeeId uniqueness
    await this.validateEmployeeId(employeeId || null);

    // Hash password (only for LOCAL users)
    const passwordHash = password ? await bcrypt.hash(password, 10) : null;

    // Determine region: use provided or default
    const finalRegion = region || 'CN';

    const user = await this.prisma.user.create({
      data: {
        username,
        email,
        passwordHash,
        displayName,
        phone,
        avatar,
        employeeId,
        defaultRegion: finalRegion,
        source: userSource, // v2.1.1: 使用指定的身份源
        status: 'ACTIVE',
        hiredAt: hiredAt ? new Date(hiredAt) : null,
      } as any,
      include: {
        departmentMemberships: {
          include: {
            department: true,
            position: true,
            manager: {
              select: {
                id: true,
                username: true,
                displayName: true,
              },
            },
          },
        },
      } as any,
    });

    // ✅ 新增：如果提供了部门ID，创建主部门关联
    if (departmentId) {
      await this.userDepartmentsService.addUserDepartment(user.id, {
        departmentId,
        positionId: positionId || undefined,
        managerId: managerId || undefined,
        title: title || undefined,
        isPrimary: true, // 创建用户时的部门为主部门
      });
    }

    // ✅ 重新查询用户以包含新创建的部门关联
    const userWithDept = await this.prisma.user.findUnique({
      where: { id: user.id },
      include: {
        departmentMemberships: {
          where: { leftAt: null },
          include: {
            department: true,
            position: true,
            manager: {
              select: {
                id: true,
                username: true,
                displayName: true,
              },
            },
          },
        },
      } as any,
    });

    const { passwordHash: _, ...userWithoutPassword } = userWithDept || user;
    return userWithoutPassword;
  }

  /**
   * Batch import user memberships (user-department relationships)
   * 批量导入用户成员关系
   * 
   * 注意：用户应已通过Entra ID同步创建，此方法仅用于导入用户-部门关系
   */
  async importMemberships(importDto: ImportUserMembershipsDto) {
    const { users } = importDto;
    const results = {
      success: [] as any[],
      failed: [] as any[],
    };

    for (const userDto of users) {
      try {
        // 1. 根据email查找用户
        const user = await this.prisma.user.findUnique({
          where: { email: userDto.email, deletedAt: null },
        });

        if (!user) {
          results.failed.push({
            email: userDto.email,
            error: `User not found: ${userDto.email}. Please sync from Entra ID first.`,
          });
          continue;
        }

        // 2. 根据部门编码和组织ID查找部门
        const department = await this.prisma.department.findUnique({
          where: {
            organizationId_code: {
              organizationId: userDto.organizationId,
              code: userDto.departmentCode,
            },
            deletedAt: null,
          },
        });

        if (!department) {
          results.failed.push({
            email: userDto.email,
            error: `Department not found: ${userDto.departmentCode} in organization ${userDto.organizationId}`,
          });
          continue;
        }

        // 3. 可选：根据邮箱查找上级
        let managerId: string | undefined = undefined;
        if (userDto.managerEmail) {
          const manager = await this.prisma.user.findUnique({
            where: { email: userDto.managerEmail, deletedAt: null },
          });

          if (!manager) {
            this.logger.warn(`Manager not found: ${userDto.managerEmail}, skipping manager assignment for ${userDto.email}`);
          } else {
            managerId = manager.id;
          }
        }

        // 4. 检查用户是否已属于该部门
        const existing = await this.prisma.userDepartment.findUnique({
          where: {
            userId_departmentId: {
              userId: user.id,
              departmentId: department.id,
            },
          },
        });

        if (existing) {
          // 如果已存在，更新信息
          await this.userDepartmentsService.updateUserDepartment(user.id, department.id, {
            positionId: userDto.positionId || undefined,
            managerId: managerId || undefined,
            title: userDto.title || undefined,
          });

          results.success.push({
            email: userDto.email,
            userId: user.id,
            departmentId: department.id,
            action: 'updated',
          });
        } else {
          // 如果不存在，创建新的部门归属
          const isPrimary = userDto.isPrimary === 'true' || userDto.isPrimary === true;

          await this.userDepartmentsService.addUserDepartment(user.id, {
            departmentId: department.id,
            positionId: userDto.positionId || undefined,
            managerId: managerId || undefined,
            title: userDto.title || undefined,
            isPrimary,
          });

          results.success.push({
            email: userDto.email,
            userId: user.id,
            departmentId: department.id,
            action: 'created',
          });
        }
      } catch (error: any) {
        this.logger.error(`Failed to import membership for ${userDto.email}:`, error.message);
        results.failed.push({
          email: userDto.email,
          error: error.message || 'Unknown error',
        });
      }
    }

    return {
      total: users.length,
      successCount: results.success.length,
      failedCount: results.failed.length,
      results,
    };
  }

  /**
   * Find all users with pagination and filters
   */
  async findAll(query: UserQueryDto) {
    const {
      page = 1,
      pageSize: requestedPageSize = 20,
      keyword,
      status,
      departmentId,
      region,
      sortBy = 'createdAt',
      sortOrder = 'desc',
      includeDeleted = false, // ✅ 新增：是否包含已删除用户
      externalId,
    } = query;

    // 限制 pageSize 最大为 1000
    const pageSize = Math.min(requestedPageSize, 1000);

    const where: any = {};

    // ✅ 根据 includeDeleted 决定是否过滤已删除用户
    if (!includeDeleted) {
      where.deletedAt = null;
    } else {
      // 如果查询已删除用户，只返回已删除的
      where.deletedAt = { not: null };
    }

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

    // Filter by department via departmentMemberships
    if (departmentId) {
      where.departmentMemberships = {
        some: {
          departmentId: departmentId,
        },
      };
    }

    if (region) {
      where.defaultRegion = region;
    }

    if (keyword) {
      where.OR = [
        { displayName: { contains: keyword, mode: 'insensitive' } },
        { email: { contains: keyword, mode: 'insensitive' } },
        { employeeId: { contains: keyword, mode: 'insensitive' } },
        { username: { contains: keyword, mode: 'insensitive' } },
      ];
    }

    // externalId 精确匹配（供 OpenClaw 权限同步脚本从 AAD GUID 反查）
    if (externalId) {
      where.externalId = externalId;
    }

    const [users, total] = await Promise.all([
      this.prisma.user.findMany({
        where,
        skip: (page - 1) * pageSize,
        take: pageSize,
        include: {
          departmentMemberships: {
            include: {
              department: {
                select: { id: true, name: true, code: true },
              },
              position: {
                select: { id: true, name: true, level: true },
              },
              manager: {
                select: { id: true, displayName: true },
              },
            },
          },
        } as any,
        orderBy: { [sortBy]: sortOrder },
      }),
      this.prisma.user.count({ where }),
    ]);

    // Remove password hash and transform departmentMemberships to simplified structure
    const items = users.map((user) => {
      const { passwordHash, departmentMemberships, ...userWithoutPassword } = user as any;
      
      // ⭐ 提取主要部门信息（isPrimary=true 的部门，如果没有则取第一个）
      const primaryMembership = departmentMemberships?.find((m: any) => m.isPrimary) || departmentMemberships?.[0];
      
      return {
        ...userWithoutPassword,
        department: primaryMembership?.department || null,
        position: primaryMembership?.position || null,
        // 保留完整的 departmentMemberships 用于需要多部门信息的场景
        departmentMemberships,
      };
    });

    return {
      items,
      total,
      page,
      limit: pageSize,
      totalPages: Math.ceil(total / pageSize),
      hasNext: page * pageSize < total,
      hasPrev: page > 1,
    };
  }

  /**
   * Find user by ID
   */
  async findOne(id: string) {
    const user = await this.prisma.user.findUnique({
      where: { id, deletedAt: null },
      include: {
        departmentMemberships: {
          include: {
            department: true,
            position: true,
            manager: {
              select: {
                id: true,
                username: true,
                displayName: true,
                email: true,
              },
            },
          },
        },
        roles: {
          include: {
            role: {
              include: {
                permissions: {
                  include: {
                    permission: true,
                  },
                },
              },
            },
          },
        },
      } as any,
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    const { passwordHash, ...userWithoutPassword } = user;
    return userWithoutPassword;
  }

  /**
   * Find user with full permissions (for /me endpoint)
   */
  async findOneWithPermissions(id: string) {
    const user = await this.findOne(id);
    
    // Aggregate permissions from enabled roles
    const permissions = user.roles
      .filter((ur: any) => ur.role.enabled !== false)
      .flatMap((ur: any) => ur.role.permissions)
      .map((rp: any) => `${rp.permission.resource}:${rp.permission.action}`);
    
    return {
      ...user,
      permissions: [...new Set(permissions)],
    };
  }

  /**
   * Update user
   * NOTE: Department, position, and manager are now managed via UserDepartment table.
   * Use UserDepartmentService to update these relationships.
   */
  async update(id: string, updateUserDto: UpdateUserDto) {
    const user = await this.prisma.user.findUnique({
      where: { id, deletedAt: null },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    // Check email uniqueness if changing
    if (updateUserDto.email && updateUserDto.email !== user.email) {
      const existingEmail = await this.prisma.user.findFirst({
        where: { email: updateUserDto.email, deletedAt: null, id: { not: id } },
      });
      if (existingEmail) {
        throw new IamUserEmailExistsException(updateUserDto.email);
      }
    }

    // Validate employeeId uniqueness if changing
    if (updateUserDto.employeeId !== undefined) {
      await this.validateEmployeeId(updateUserDto.employeeId, id);
    }

    // Filter out deprecated fields (departmentId, positionId, managerId)
    // and map region to defaultRegion
    const {
      departmentId,
      positionId,
      managerId,
      region,      // ⭐ 前端发送的是 region，但 Prisma 字段名是 defaultRegion
      workCity,    // v1.2 单独处理：trim + 空串归 null
      ...allowedUpdates
    } = updateUserDto as any;

    // Map region to defaultRegion if provided
    const dataToUpdate: Record<string, unknown> = {
      ...allowedUpdates,
      ...(region !== undefined ? { defaultRegion: region } : {}),
    };
    if (workCity !== undefined) {
      const trimmed = typeof workCity === 'string' ? workCity.trim() : workCity;
      dataToUpdate.workCity = trimmed ? trimmed : null;
    }

    const updatedUser = await this.prisma.user.update({
      where: { id },
      data: dataToUpdate,
      include: {
        departmentMemberships: {
          include: {
            department: true,
            position: true,
            manager: {
              select: {
                id: true,
                username: true,
                displayName: true,
              },
            },
          },
        },
      } as any,
    });

    const { passwordHash, ...userWithoutPassword } = updatedUser;
    return userWithoutPassword;
  }

  /**
   * Delete user (soft delete)
   */
  async remove(id: string) {
    const user = await this.prisma.user.findUnique({
      where: { id, deletedAt: null },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    // 检查最后管理员保护
    await this.checkLastAdminProtection(id);

    // 检查是否有下属（通过 UserDepartment 中的 managerId 关联）
    const subordinatesCount = await this.prisma.userDepartment.count({
      where: {
        managerId: id,
        leftAt: null,
      },
    });

    if (subordinatesCount > 0) {
      throw new BadRequestException(
        `Cannot delete user: This user is the manager of ${subordinatesCount} employees. Please reassign them first.`
      );
    }

    await this.prisma.user.update({
      where: { id },
      data: {
        deletedAt: new Date(),
        status: 'TERMINATED',
      },
    });

    return { message: 'User deleted successfully' };
  }

  /**
   * Restore deleted user (永久可恢复)
   */
  async restore(userId: string, dto: RestoreUserDto) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    if (!user.deletedAt || user.status !== 'TERMINATED') {
      throw new BadRequestException('User is not deleted');
    }

    const updatedUser = await this.prisma.user.update({
      where: { id: userId },
      data: {
        deletedAt: null,
        status: 'ACTIVE',
        metadata: {
          ...(user.metadata as object || {}),
          restoreReason: dto.reason,
          restoredAt: new Date().toISOString(),
        },
      },
    });

    this.logger.log(`User ${userId} restored from deletion`);

    return {
      userId: updatedUser.id,
      status: updatedUser.status,
      restoredAt: new Date(),
    };
  }

  /**
   * Terminate user
   */
  @SkipAssertAccess('IAM 治理：用户终止状态变更的权限校验走 Controller 层 @RequirePermissions(user:manage)')
  async terminate(userId: string, dto: TerminateUserDto) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    const terminatedAt = dto.terminatedAt ? new Date(dto.terminatedAt) : new Date();

    const updatedUser = await this.prisma.user.update({
      where: { id: userId },
      data: {
        status: 'TERMINATED',
        terminatedAt,
        metadata: {
          ...(user.metadata as object || {}),
          terminationReason: dto.reason,
          terminatedBy: 'admin', // TODO: get from request context
        },
      },
    });

    this.logger.log(`User ${userId} terminated at ${terminatedAt}`);

    // 撤权 SLA：必须 invalidate auth cache，否则被 terminate 的用户在 5 分钟 cache TTL 内仍能用旧权限
    await this.authCache.invalidate(userId);

    // TODO: Invalidate user tokens (add to blacklist)

    return {
      userId: updatedUser.id,
      status: updatedUser.status,
      terminatedAt: updatedUser.terminatedAt,
    };
  }

  /**
   * Activate user
   */
  async activate(userId: string, dto: ActivateUserDto) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    if (user.status === 'ACTIVE') {
      throw new IamUserAlreadyActiveException();
    }

    if (user.status === 'TERMINATED') {
      throw new IamTerminatedUserCannotActivateException();
    }

    const updatedUser = await this.prisma.user.update({
      where: { id: userId },
      data: {
        status: 'ACTIVE',
        metadata: {
          ...(user.metadata as object || {}),
          activationReason: dto.reason,
          activatedAt: new Date().toISOString(),
        },
      },
    });

    this.logger.log(`User ${userId} activated`);

    return {
      userId: updatedUser.id,
      status: updatedUser.status,
      activatedAt: new Date(),
    };
  }

  /**
   * Update user status (v2.1.1 新增)
   * 支持 ACTIVE ↔ INACTIVE 状态切换
   */
  async updateStatus(userId: string, dto: UpdateUserStatusDto) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
      include: {
        roles: {
          include: {
            role: true,
          },
        },
      },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    // 验证状态转换合法性
    const validTransitions: Record<UserStatus, UserStatus[]> = {
      [UserStatus.ACTIVE]: [UserStatus.INACTIVE, UserStatus.SUSPENDED],
      [UserStatus.INACTIVE]: [UserStatus.ACTIVE],
      [UserStatus.SUSPENDED]: [UserStatus.ACTIVE, UserStatus.INACTIVE],
      [UserStatus.TERMINATED]: [], // TERMINATED 状态不可逆
    };

    const allowedStatuses = validTransitions[user.status as UserStatus] || [];
    if (!allowedStatuses.includes(dto.status)) {
      throw new BadRequestException(
        `Invalid status transition: ${user.status} → ${dto.status}`
      );
    }

    // 如果是停用管理员，检查是否是最后一个活跃管理员
    if (dto.status === UserStatus.INACTIVE || dto.status === UserStatus.SUSPENDED) {
      await this.checkLastAdminProtection(userId, user);
    }

    const updatedUser = await this.prisma.user.update({
      where: { id: userId },
      data: {
        status: dto.status,
        metadata: {
          ...(user.metadata as object || {}),
          statusChangeReason: dto.reason,
          statusChangedAt: new Date().toISOString(),
          previousStatus: user.status,
        },
      },
    });

    this.logger.log(`User ${userId} status updated: ${user.status} → ${dto.status}`);

    return {
      userId: updatedUser.id,
      status: updatedUser.status,
      updatedAt: new Date(),
    };
  }

  /**
   * 检查最后管理员保护
   * 防止停用或删除系统中最后一个活跃的管理员
   */
  private async checkLastAdminProtection(
    userId: string,
    user?: { roles: Array<{ role: { code: string; enabled: boolean } }> },
  ) {
    // 如果没有传入 user，则查询
    if (!user) {
      const userWithRoles = await this.prisma.user.findUnique({
        where: { id: userId, deletedAt: null },
        include: {
          roles: {
            include: {
              role: true,
            },
          },
        },
      });

      if (!userWithRoles) {
        return; // 用户不存在，后续会抛出 NotFoundException
      }

      user = userWithRoles;
    }

    // 检查用户是否有管理员角色
    // 规则 §五、4.3：统一使用 Administrator；ADMIN 视为历史遗留兼容
    const adminRoleCodes = new Set(['Administrator', 'ADMIN']);
    const hasAdminRole = user.roles.some(
      (ur) => adminRoleCodes.has(ur.role.code) && ur.role.enabled
    );

    if (!hasAdminRole) {
      return; // 不是管理员，无需检查
    }

    // 统计其他活跃的管理员数量
    const otherActiveAdminsCount = await this.prisma.userRole.count({
      where: {
        role: {
          code: { in: Array.from(adminRoleCodes) },
          enabled: true,
        },
        user: {
          status: 'ACTIVE',
          deletedAt: null,
          id: { not: userId },
        },
      },
    });

    if (otherActiveAdminsCount === 0) {
      throw new BadRequestException(
        'Cannot deactivate the last active administrator. Please assign another administrator first.'
      );
    }
  }

  /**
   * Get user roles (v2.1 支持组织级角色)
   * 返回用户的所有角色，包含组织信息
   */
  async getUserRoles(userId: string) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
      include: {
        roles: {
          include: {
            role: true,
            organization: {
              select: {
                id: true,
                code: true,
                name: true,
              },
            },
          },
        },
      },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    // v2.1: 返回角色列表，包含组织信息
    return user.roles.map((ur) => ({
      id: ur.role.id,
      code: ur.role.code,
      name: ur.role.name,
      description: ur.role.description,
      isBuiltIn: ur.role.isBuiltIn,
      enabled: ur.role.enabled,
      organizationId: (ur as any).organizationId,
      organization: (ur as any).organization
        ? {
            id: (ur as any).organization.id,
            code: (ur as any).organization.code,
            name: (ur as any).organization.name,
          }
        : null,
      isGlobal: (ur as any).organizationId === null,
      assignedAt: ur.createdAt,
    }));
  }

  /**
   * Assign roles to user (v2.1 支持组织级角色分配)
   *
   * 支持两种格式：
   * 1. 向后兼容：roleIds 数组（分配为全局角色）
   * 2. v2.1 新格式：assignments 数组（支持组织级角色）
   */
  @SkipAssertAccess('IAM 治理：UserRole 全替换的权限校验走 Controller 层 @RequirePermissions(role:manage)')
  async assignRoles(userId: string, dto: AssignRolesDto) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    let assignments: Array<{ roleId: string; organizationId: string | null }> = [];

    // v2.1 新格式：使用 assignments 数组
    if (dto.assignments && dto.assignments.length > 0) {
      // 验证所有角色存在
      const roleIds = [...new Set(dto.assignments.map(a => a.roleId))];
      const roles = await this.prisma.role.findMany({
        where: { id: { in: roleIds } },
      });

      if (roles.length !== roleIds.length) {
        throw new NotFoundException('Some roles do not exist');
      }

      // 验证所有组织存在（如果指定了 organizationId）
      const organizationIds = dto.assignments
        .map(a => a.organizationId)
        .filter((id): id is string => id !== null && id !== undefined);
      
      if (organizationIds.length > 0) {
        const organizations = await this.prisma.organization.findMany({
          where: { 
            id: { in: organizationIds },
            deletedAt: null,
          },
        });

        if (organizations.length !== organizationIds.length) {
          const foundIds = organizations.map(o => o.id);
          const missingIds = organizationIds.filter(id => !foundIds.includes(id));
          throw new NotFoundException(`Some organizations not found: ${missingIds.join(', ')}`);
        }
      }

      // 构建分配数据
      assignments = dto.assignments.map(a => ({
        roleId: a.roleId,
        organizationId: a.organizationId ?? null,
      }));
    }
    // 向后兼容：使用 roleIds 数组（分配为全局角色）
    else if (dto.roleIds && dto.roleIds.length > 0) {
      // Verify all roles exist
      const roles = await this.prisma.role.findMany({
        where: { id: { in: dto.roleIds } },
      });

      if (roles.length !== dto.roleIds.length) {
        throw new NotFoundException('Some roles do not exist');
      }

      // 构建分配数据（全局角色）
      assignments = dto.roleIds.map(roleId => ({
        roleId,
        organizationId: null, // 全局角色
      }));
    } else {
      throw new BadRequestException('Either roleIds or assignments must be provided');
    }

    // 删除现有角色并创建新的（PUT 语义：完全覆盖）
    // ⚠️ 注意：此方法是「全替换」语义。如果只想新增几个角色而保留其他，请用 addRoles()
    await this.prisma.$transaction([
      this.prisma.userRole.deleteMany({ where: { userId } }),
      this.prisma.userRole.createMany({
        data: assignments.map(a => ({
          userId,
          roleId: a.roleId,
          organizationId: a.organizationId,
        })),
      }),
    ]);

    // 撤权 SLA：必须 invalidate auth cache，否则用户在 5 分钟内还会命中旧角色（含撤掉的角色）
    await this.authCache.invalidate(userId);

    // 查询分配的角色详情
    const assignedRoles = await this.prisma.userRole.findMany({
      where: { userId },
      include: {
        role: true,
        organization: {
          select: {
            id: true,
            code: true,
            name: true,
          },
        },
      },
    });

    return {
      userId,
      assignedRoles: assignedRoles.map(ur => ({
        roleId: ur.role.id,
        roleCode: ur.role.code,
        roleName: ur.role.name,
        organizationId: (ur as any).organizationId,
        organization: (ur as any).organization
          ? {
              id: (ur as any).organization.id,
              code: (ur as any).organization.code,
              name: (ur as any).organization.name,
            }
          : null,
        isGlobal: (ur as any).organizationId === null,
      })),
    };
  }

  /**
   * 增量增加角色（保留用户已有的角色，已存在的跳过）
   *
   * 用于"将用户加到角色 X"的场景：只 add，不删任何旧的。
   * 同 (userId, roleId, organizationId) 已存在 → 幂等跳过，不抛错。
   *
   * 跟 assignRoles() 的区别：
   * - assignRoles 是 PUT 语义（全替换），用于「设置用户角色为这些」
   * - addRoles  是 POST 语义（增量加），用于「把这些追加到用户现有角色里」
   *
   * 修复 BUG：之前前端 addUsersToRole 调 assignRoles(userId, [roleId])，
   * 把"加 1 个"误传为"全替换为这 1 个"，导致用户其他角色被全删。
   */
  @SkipAssertAccess('IAM 治理：UserRole 增量加的权限校验走 Controller 层 @RequirePermissions(role:manage)')
  async addRoles(userId: string, dto: AssignRolesDto) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
    });
    if (!user) {
      throw new NotFoundException('User not found');
    }

    let assignments: Array<{ roleId: string; organizationId: string | null }> = [];

    if (dto.assignments && dto.assignments.length > 0) {
      const roleIds = [...new Set(dto.assignments.map((a) => a.roleId))];
      const roles = await this.prisma.role.findMany({ where: { id: { in: roleIds } } });
      if (roles.length !== roleIds.length) {
        throw new NotFoundException('Some roles do not exist');
      }

      const organizationIds = dto.assignments
        .map((a) => a.organizationId)
        .filter((id): id is string => id !== null && id !== undefined);
      if (organizationIds.length > 0) {
        const organizations = await this.prisma.organization.findMany({
          where: { id: { in: organizationIds }, deletedAt: null },
        });
        if (organizations.length !== organizationIds.length) {
          const foundIds = organizations.map((o) => o.id);
          const missingIds = organizationIds.filter((id) => !foundIds.includes(id));
          throw new NotFoundException(`Some organizations not found: ${missingIds.join(', ')}`);
        }
      }

      assignments = dto.assignments.map((a) => ({
        roleId: a.roleId,
        organizationId: a.organizationId ?? null,
      }));
    } else if (dto.roleIds && dto.roleIds.length > 0) {
      const roles = await this.prisma.role.findMany({ where: { id: { in: dto.roleIds } } });
      if (roles.length !== dto.roleIds.length) {
        throw new NotFoundException('Some roles do not exist');
      }
      assignments = dto.roleIds.map((roleId) => ({ roleId, organizationId: null }));
    } else {
      throw new BadRequestException('Either roleIds or assignments must be provided');
    }

    // 业务层显式去重：PostgreSQL unique 索引对 NULL 不去重（NULL != NULL），
    // 全局角色（organizationId=null）必须先查再插，否则会出现 (user, role, NULL) 重复行。
    const existing = await this.prisma.userRole.findMany({
      where: { userId },
      select: { roleId: true, organizationId: true },
    });
    const existingKeys = new Set(
      existing.map((e) => `${e.roleId}|${e.organizationId ?? 'NULL'}`),
    );

    const toInsert = assignments.filter(
      (a) => !existingKeys.has(`${a.roleId}|${a.organizationId ?? 'NULL'}`),
    );

    if (toInsert.length > 0) {
      await this.prisma.userRole.createMany({
        data: toInsert.map((a) => ({
          userId,
          roleId: a.roleId,
          organizationId: a.organizationId,
        })),
      });
      // 撤权 SLA：新增角色后必须 invalidate auth cache，否则用户的新权限要等 5 分钟才生效
      await this.authCache.invalidate(userId);
    }

    return this.getUserRoles(userId);
  }

  /**
   * 分配区域角色（完全替换）
   * 支持一次性设置用户在所有区域的角色
   */
  @SkipAssertAccess('IAM 治理：UserRole 全替换的权限校验走 Controller 层 @RequirePermissions(role:manage)')
  async assignRegionRoles(userId: string, roles: { roleId: string; region?: string | null }[]) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    // 验证所有角色存在
    const roleIds = [...new Set(roles.map(r => r.roleId))];
    const existingRoles = await this.prisma.role.findMany({
      where: { id: { in: roleIds } },
    });

    if (existingRoles.length !== roleIds.length) {
      throw new NotFoundException('Some roles do not exist');
    }

    // 验证区域有效性
    const validRegions = ['CN', 'US', 'UAE'];
    for (const role of roles) {
      if (role.region && !validRegions.includes(role.region)) {
        throw new BadRequestException(`Invalid region: ${role.region}`);
      }
    }

    // 删除现有角色并创建新的
    await this.prisma.$transaction([
      this.prisma.userRole.deleteMany({ where: { userId } }),
      this.prisma.userRole.createMany({
        data: roles.map(r => ({
          userId,
          roleId: r.roleId,
          organizationId: null, // v2.1: 区域角色暂时使用全局角色方式（TODO: 需要调整为 organizationId）
        })),
      }),
    ]);

    // 撤权 SLA：必须 invalidate auth cache
    await this.authCache.invalidate(userId);

    return this.getUserRoles(userId);
  }

  /**
   * 添加区域角色
   */
  @SkipAssertAccess('IAM 治理：UserRole 增量加的权限校验走 Controller 层 @RequirePermissions(role:manage)')
  async addRegionRole(userId: string, roleId: string, region?: string | null) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    const role = await this.prisma.role.findUnique({
      where: { id: roleId },
    });

    if (!role) {
      throw new NotFoundException('Role not found');
    }

    // 验证区域有效性
    const validRegions = ['CN', 'US', 'UAE'];
    if (region && !validRegions.includes(region)) {
      throw new BadRequestException(`Invalid region: ${region}`);
    }

    // 检查是否已存在（v2.1: 使用 organizationId 替代 region）
    const existing = await this.prisma.userRole.findFirst({
      where: {
        userId,
        roleId,
        organizationId: null, // v2.1: 全局角色
      } as any,
    });

    if (existing) {
      throw new BadRequestException('User already has this role');
    }

    // v2.1: 暂时不支持 region 参数，使用 organizationId=null 表示全局角色
    await this.prisma.userRole.create({
      data: {
        userId,
        roleId,
        organizationId: null, // v2.1: 全局角色
      } as any,
    });

    // 撤权 SLA：必须 invalidate auth cache
    await this.authCache.invalidate(userId);

    return this.getUserRoles(userId);
  }

  /**
   * 删除区域角色
   */
  @SkipAssertAccess('IAM 治理：删除 UserRole 的权限校验走 Controller 层 @RequirePermissions(role:manage)')
  async removeRegionRole(userId: string, roleId: string, region?: string | null) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    const userRole = await this.prisma.userRole.findFirst({
      where: {
        userId,
        roleId,
        organizationId: region || null,
      },
    });

    if (!userRole) {
      throw new NotFoundException('User does not have this role');
    }

    await this.prisma.userRole.delete({
      where: { id: userRole.id },
    });

    // 撤权 SLA：必须 invalidate auth cache，否则被移除角色的用户在 5 分钟内仍能用该角色权限（安全风险）
    await this.authCache.invalidate(userId);

    return this.getUserRoles(userId);
  }

  /**
   * Remove role from user - 兼容旧接口
   * 注意：此方法会删除用户该角色的所有区域关联
   */
  @SkipAssertAccess('IAM 治理：删除 UserRole 的权限校验走 Controller 层 @RequirePermissions(role:manage)')
  async removeRole(userId: string, roleId: string) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    // 删除该用户该角色的所有区域关联
    const result = await this.prisma.userRole.deleteMany({
      where: { userId, roleId },
    });

    if (result.count === 0) {
      throw new NotFoundException('User does not have this role');
    }

    // 撤权 SLA：必须 invalidate auth cache，否则被移除角色的用户在 5 分钟内仍能用该角色权限（安全风险）
    await this.authCache.invalidate(userId);

    return {
      message: 'Role removed from user successfully',
      deletedCount: result.count,
    };
  }

  /**
   * Get user permissions (aggregated from all enabled roles)
   * v2.1: Supports organizationId parameter for filtering
   * 
   * @param userId - User ID
   * @param organizationId - Optional organization ID for filtering. 
   *                         If provided, returns only permissions from roles in that organization.
   *                         If not provided, returns all permissions from all roles (global + all organizations).
   */
  async getUserPermissions(userId: string, organizationId?: string) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
      include: {
        roles: {
          include: {
            role: {
              include: {
                permissions: {
                  include: {
                    permission: true,
                  },
                },
              },
            },
            organization: {
              select: {
                id: true,
                code: true,
                name: true,
              },
            },
          },
        },
      },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    // Filter roles by organizationId if provided
    let relevantRoles = user.roles.filter((ur) => ur.role.enabled !== false);
    
    if (organizationId) {
      // 传入 organizationId 时，叠加该组织的角色 + 全局角色（organizationId === null，如 Administrator），
      // 否则切到具体组织上下文后全局角色权限会丢失。
      relevantRoles = relevantRoles.filter(
        (ur) =>
          (ur as any).organizationId === organizationId ||
          (ur as any).organizationId === null,
      );
    }

    // Aggregate permissions from filtered roles
    const permissionsMap = new Map();
    const permissionSources = new Map<string, string[]>();

    relevantRoles.forEach((ur) => {
      ur.role.permissions.forEach((rp) => {
        const permission = rp.permission;
        const permissionKey = `${permission.resource}:${permission.action}`;

        if (!permissionsMap.has(permissionKey)) {
          permissionsMap.set(permissionKey, permission);
          permissionSources.set(permissionKey, []);
        }

        permissionSources.get(permissionKey)!.push(ur.role.name);
      });
    });

    const permissions = Array.from(permissionsMap.values()).map((permission) => {
      const permissionKey = `${permission.resource}:${permission.action}`;
      return {
        ...permission,
        sources: permissionSources.get(permissionKey),
      };
    });

    const roles = relevantRoles.map((ur) => ({
      id: ur.role.id,
      code: ur.role.code,
      name: ur.role.name,
      organizationId: (ur as any).organizationId,
      organization: (ur as any).organization
        ? {
            id: (ur as any).organization.id,
            code: (ur as any).organization.code,
            name: (ur as any).organization.name,
          }
        : null,
      isGlobal: (ur as any).organizationId === null,
    }));

    return {
      userId,
      organizationId: organizationId || null,
      permissions: permissions.map((p) => ({
        resource: p.resource,
        action: p.action,
        code: `${p.resource}:${p.action}`,
        description: p.description,
        sources: p.sources || [],
      })),
      roles,
    };
  }

  /**
   * Get user subordinates
   * NOTE: Subordinates are now determined via UserDepartment.managerId
   */
  async getSubordinates(userId: string) {
    // Verify user exists
    const user = await this.prisma.user.findUnique({
      where: { id: userId, deletedAt: null },
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    // Find all UserDepartment records where this user is the manager
    const subordinateDepartments = await (this.prisma as any).userDepartment.findMany({
      where: {
        managerId: userId,
        leftAt: null, // ✅ 修复：只查询仍在职的下属
        user: {
          deletedAt: null,
        },
      },
      include: {
        user: {
          select: {
            id: true,
            username: true,
            displayName: true,
            email: true,
            status: true,
          },
        },
        department: {
          select: {
            id: true,
            name: true,
            code: true,
          },
        },
        position: {
          select: {
            id: true,
            name: true,
            level: true,
          },
        },
      },
    });

    // Transform to user-centric format with department context
    return subordinateDepartments.map((sd: any) => ({
      ...sd.user,
      department: sd.department,
      position: sd.position,
      isPrimary: sd.isPrimary,
    }));
  }
}
