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 const Permissions = {
  EXAM_CREATE: "CORE_EDU_EXAM_CREATE" as const,
  EXAM_READ: "CORE_EDU_EXAM_READ" as const,
  EXAM_UPDATE: "CORE_EDU_EXAM_UPDATE" as const,
  EXAM_DELETE: "CORE_EDU_EXAM_DELETE" as const,
  HOMEWORK_CREATE: "CORE_EDU_HOMEWORK_CREATE" as const,
  HOMEWORK_READ: "CORE_EDU_HOMEWORK_READ" as const,
  HOMEWORK_UPDATE: "CORE_EDU_HOMEWORK_UPDATE" as const,
  HOMEWORK_SUBMIT: "CORE_EDU_HOMEWORK_SUBMIT" as const,
  GRADE_CREATE: "CORE_EDU_GRADE_CREATE" as const,
  GRADE_READ: "CORE_EDU_GRADE_READ" as const,
} as const;

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

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

const ROLE_PERMISSIONS: Record<string, Permission[]> = {
  admin: [
    Permissions.EXAM_CREATE,
    Permissions.EXAM_READ,
    Permissions.EXAM_UPDATE,
    Permissions.EXAM_DELETE,
    Permissions.HOMEWORK_CREATE,
    Permissions.HOMEWORK_READ,
    Permissions.HOMEWORK_UPDATE,
    Permissions.HOMEWORK_SUBMIT,
    Permissions.GRADE_CREATE,
    Permissions.GRADE_READ,
  ],
  teacher: [
    Permissions.EXAM_CREATE,
    Permissions.EXAM_READ,
    Permissions.EXAM_UPDATE,
    Permissions.HOMEWORK_CREATE,
    Permissions.HOMEWORK_READ,
    Permissions.HOMEWORK_UPDATE,
    Permissions.HOMEWORK_SUBMIT,
    Permissions.GRADE_CREATE,
    Permissions.GRADE_READ,
  ],
  student: [
    Permissions.EXAM_READ,
    Permissions.HOMEWORK_READ,
    Permissions.HOMEWORK_SUBMIT,
    Permissions.GRADE_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(", "));
  }
}
