import { Injectable, Logger } from '@nestjs/common';
import { RedisService } from '@core/cache/redis/redis.service';

export interface OrgAuthSlice {
  permissions: string[];
  dataScopes: Array<{ resource: string; scopeType: string }>;
  roles: string[];
}

export interface CachedUserAuth {
  userId: string;
  systemRoles: OrgAuthSlice; // 全局角色（organizationId = null）
  orgRoles: Record<string, OrgAuthSlice>; // 按组织分层
  defaultRegion?: string;
  email?: string;
  username?: string;
  // 前端兼容字段：按 region 切片的权限/角色（来自旧版 validateUser）
  // 新代码应该用 organizationPermissions / organizationRoles；这里保留是为前端渐进迁移
  regionPermissions?: Record<string, string[]>;
  regionRoles?: Record<string, string[]>;
  // §5.3.14：本用户作为受托人接收的活跃委托 scope。
  // 缓存这字段把"每请求查一次 permission_delegations 表"降为"每 5 分钟一次"，
  // 与 cache 同 TTL（撤权 SLA）。委托 create/revoke 时通过 invalidate(toUserId) 触发刷新。
  inboundDelegationScopes?: Array<{ resource: string; scopeType: string }>;
}

/**
 * 用户权限与 DataScope 的 Redis 缓存。
 *
 * 规则对齐（09-iam-security.md §5.3.5）：
 * - TTL = 5 分钟（撤权 SLA）
 * - 按组织分层存储，支持"仅当前组织合并"语义
 * - Redis 故障时上层必须 fail secure，不得跳过检查
 * - 配置变更时 `DEL user:${userId}:auth` 主动失效
 */
@Injectable()
export class AuthCacheService {
  private readonly logger = new Logger(AuthCacheService.name);
  private readonly ttlSec = 5 * 60;

  constructor(private readonly redis: RedisService) {}

  private key(userId: string): string {
    return `user:${userId}:auth`;
  }

  async get(userId: string): Promise<CachedUserAuth | null> {
    return this.redis.getJson<CachedUserAuth>(this.key(userId));
  }

  async set(auth: CachedUserAuth): Promise<void> {
    await this.redis.setJson(this.key(auth.userId), this.ttlSec, auth);
  }

  /**
   * 失效用户缓存。权限配置 / 角色变更 / scope 配置变更时调用。
   */
  async invalidate(userId: string): Promise<void> {
    await this.redis.del(this.key(userId));
    this.logger.debug(`已失效用户缓存：${userId}`);
  }

  /**
   * 批量失效（配置变更影响多人时）。
   */
  async invalidateMany(userIds: string[]): Promise<void> {
    if (userIds.length === 0) return;
    const keys = userIds.map((id) => this.key(id));
    await this.redis.del(...keys);
    this.logger.debug(`批量失效用户缓存：${userIds.length} 个`);
  }
}
