import {
  Injectable,
  CanActivate,
  ExecutionContext,
  SetMetadata,
} from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { PermissionDeniedError } from "../shared/errors/application-error.js";
import { PermissionCacheService } from "../shared/cache/permission-cache.service.js";
import { IamRepository } from "../iam/iam.repository.js";
import type { AuthenticatedRequest } from "./auth.middleware.js";

/**
 * 权限点常量（对齐 iam-init.sql 种子数据）。
 *
 * 权限名格式：`<resource>:<action>`（如 `iam:user:read`）。
 * DB 中存储的权限名与 controller 装饰器声明的权限名一一对应。
 */
export const Permissions = {
  IAM_USER_READ: "iam:user:read",
  IAM_USER_MANAGE: "iam:user:manage",
  IAM_ROLE_MANAGE: "iam:role:manage",
  IAM_AUDIT_READ: "iam:audit:read",
  IAM_VIEWPORT_READ: "iam:viewport:read",
} as const;

export type Permission = (typeof Permissions)[keyof typeof Permissions];

export const PERMISSIONS_KEY = "permissions";
export const RequirePermission = (...permissions: Permission[]) =>
  SetMetadata(PERMISSIONS_KEY, permissions);

/**
 * 权限守卫（I3 裁决：DB 驱动 + Redis 缓存）。
 *
 * 校验流程：
 * 1. 从 req.userId 获取当前用户（由 AuthMiddleware 注入）
 * 2. 先查 Redis 缓存（TTL 5min）
 * 3. 未命中则查 DB（role_permissions JOIN），并回填缓存
 * 4. 检查用户权限列表是否包含所需权限
 *
 * admin 角色拥有全部权限（data_scope=all 时跳过 DB 查询直接放行）。
 */
@Injectable()
export class PermissionGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    private readonly permissionCache: PermissionCacheService,
    private readonly iamRepository: IamRepository,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    if (process.env.DEV_MODE === "true") {
      return true;
    }

    const requiredPermissions = this.reflector.getAllAndOverride<Permission[]>(
      PERMISSIONS_KEY,
      [context.getHandler(), context.getClass()],
    );

    if (!requiredPermissions || requiredPermissions.length === 0) {
      return true;
    }

    const request = context.switchToHttp().getRequest<AuthenticatedRequest>();
    const userId = request.userId;
    if (!userId) {
      throw new PermissionDeniedError("missing user identity");
    }

    // data_scope=all 的用户（admin）直接放行
    if (request.userDataScope === "all") {
      return true;
    }

    const userPermissions = await this.loadPermissions(userId);
    const hasPermission = requiredPermissions.some((p) =>
      userPermissions.includes(p),
    );
    if (!hasPermission) {
      throw new PermissionDeniedError(requiredPermissions.join(", "));
    }
    return true;
  }

  private async loadPermissions(userId: string): Promise<string[]> {
    const cached = await this.permissionCache.getPermissions(userId);
    if (cached) return cached;

    const perms = await this.iamRepository.getUserPermissions(userId);
    const names = perms.map((p) => p.name);
    await this.permissionCache.setPermissions(userId, names);
    return names;
  }
}
