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

export type Permission =
  | "IAM_USER_CREATE"
  | "IAM_USER_READ"
  | "IAM_USER_UPDATE"
  | "IAM_USER_DELETE"
  | "IAM_ROLE_MANAGE";

export const Permissions = {
  IAM_USER_CREATE: "IAM_USER_CREATE" as const,
  IAM_USER_READ: "IAM_USER_READ" as const,
  IAM_USER_UPDATE: "IAM_USER_UPDATE" as const,
  IAM_USER_DELETE: "IAM_USER_DELETE" as const,
  IAM_ROLE_MANAGE: "IAM_ROLE_MANAGE" as const,
};

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

const ROLE_PERMISSIONS: Record<string, Permission[]> = {
  admin: [
    Permissions.IAM_USER_CREATE,
    Permissions.IAM_USER_READ,
    Permissions.IAM_USER_UPDATE,
    Permissions.IAM_USER_DELETE,
    Permissions.IAM_ROLE_MANAGE,
  ],
  teacher: [Permissions.IAM_USER_READ],
};

@Injectable()
export class PermissionGuard implements CanActivate {
  constructor(private readonly reflector: Reflector) {}

  canActivate(context: ExecutionContext): 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 roles = request.userRoles ?? [];

    for (const role of roles) {
      const perms = ROLE_PERMISSIONS[role];
      if (perms && requiredPermissions.some((p) => perms.includes(p))) {
        return true;
      }
    }

    throw new PermissionDeniedError(requiredPermissions.join(", "));
  }
}
