/**
 * 角色 / 权限码判定通用函数（IAM 内部多服务复用）
 *
 * 规则对齐：docs/standards/09-iam-security.md §四（RBAC）
 * - Administrator 角色名统一为 'Administrator'（兼容历史 'ADMIN' 大小写）
 * - 权限码 `resource:action`，支持通配符 `resource:*` / `*:*`
 */

/** 系统级 Administrator role code（不区分大小写比较）。 */
export const ADMINISTRATOR_ROLE_CODE = 'Administrator';

/** 系统级 admin 权限码（前后端共享，避免裸字符串散落）。 */
export const SYSTEM_ADMIN_PERMISSION = 'system:admin';

interface UserAuthLike {
  roles?: string[];
  organizationRoles?: Record<string, string[]>;
}

/**
 * 把 user.roles（系统级 + 当前 org 切片）和 user.organizationRoles（所有 org 维度）
 * 展平成一个去重 code 列表。供"是否系统/组织级 X"这种跨 org 资格判定使用。
 */
export function collectAllRoleCodes(user: UserAuthLike | null | undefined): string[] {
  if (!user) return [];
  const codes = new Set<string>();
  if (user.roles?.length) {
    for (const c of user.roles) if (c) codes.add(c);
  }
  if (user.organizationRoles) {
    for (const orgRoles of Object.values(user.organizationRoles)) {
      for (const c of orgRoles) if (c) codes.add(c);
    }
  }
  return Array.from(codes);
}

/** 给定一组 role code 字符串，判定是否包含 Administrator（大小写无关）。 */
export function isAdministratorByCodes(codes: ReadonlyArray<string>): boolean {
  if (!codes.length) return false;
  const target = ADMINISTRATOR_ROLE_CODE.toUpperCase();
  return codes.some((r) => r?.toUpperCase() === target);
}

/**
 * 给定 user 对象，跨 org 判定是否 Administrator（参见 §4.5）。
 *
 * 在请求未带 X-Organization-Id 时，sliceAuthPayload 切出来的 `user.roles` 只含系统级，
 * org-bound Administrator 不在其中——必须同时检查 `organizationRoles` 才能识别。
 */
export function isAdministrator(user: UserAuthLike | null | undefined): boolean {
  return isAdministratorByCodes(collectAllRoleCodes(user));
}

export function hasPermissionCode(
  userPermissions: string[] | undefined | null,
  required: string,
): boolean {
  if (!userPermissions?.length) return false;
  if (userPermissions.includes(required)) return true;
  if (userPermissions.includes('*:*')) return true;
  const [resource] = required.split(':');
  if (resource && userPermissions.includes(`${resource}:*`)) return true;
  return false;
}
