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 = {
  CONTENT_TEXTBOOK_CREATE: "CONTENT_TEXTBOOK_CREATE" as const,
  CONTENT_TEXTBOOK_READ: "CONTENT_TEXTBOOK_READ" as const,
  CONTENT_TEXTBOOK_UPDATE: "CONTENT_TEXTBOOK_UPDATE" as const,
  CONTENT_TEXTBOOK_DELETE: "CONTENT_TEXTBOOK_DELETE" as const,
  CONTENT_CHAPTER_CREATE: "CONTENT_CHAPTER_CREATE" as const,
  CONTENT_CHAPTER_READ: "CONTENT_CHAPTER_READ" as const,
  CONTENT_CHAPTER_UPDATE: "CONTENT_CHAPTER_UPDATE" as const,
  CONTENT_CHAPTER_DELETE: "CONTENT_CHAPTER_DELETE" as const,
  CONTENT_QUESTION_CREATE: "CONTENT_QUESTION_CREATE" as const,
  CONTENT_QUESTION_READ: "CONTENT_QUESTION_READ" as const,
  CONTENT_QUESTION_UPDATE: "CONTENT_QUESTION_UPDATE" as const,
  CONTENT_QUESTION_DELETE: "CONTENT_QUESTION_DELETE" as const,
  CONTENT_KNOWLEDGE_POINT_CREATE: "CONTENT_KNOWLEDGE_POINT_CREATE" as const,
  CONTENT_KNOWLEDGE_POINT_READ: "CONTENT_KNOWLEDGE_POINT_READ" as const,
  CONTENT_KNOWLEDGE_POINT_UPDATE: "CONTENT_KNOWLEDGE_POINT_UPDATE" as const,
  CONTENT_KNOWLEDGE_POINT_DELETE: "CONTENT_KNOWLEDGE_POINT_DELETE" 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.CONTENT_TEXTBOOK_CREATE,
    Permissions.CONTENT_TEXTBOOK_READ,
    Permissions.CONTENT_TEXTBOOK_UPDATE,
    Permissions.CONTENT_TEXTBOOK_DELETE,
    Permissions.CONTENT_CHAPTER_CREATE,
    Permissions.CONTENT_CHAPTER_READ,
    Permissions.CONTENT_CHAPTER_UPDATE,
    Permissions.CONTENT_CHAPTER_DELETE,
    Permissions.CONTENT_QUESTION_CREATE,
    Permissions.CONTENT_QUESTION_READ,
    Permissions.CONTENT_QUESTION_UPDATE,
    Permissions.CONTENT_QUESTION_DELETE,
    Permissions.CONTENT_KNOWLEDGE_POINT_CREATE,
    Permissions.CONTENT_KNOWLEDGE_POINT_READ,
    Permissions.CONTENT_KNOWLEDGE_POINT_UPDATE,
    Permissions.CONTENT_KNOWLEDGE_POINT_DELETE,
  ],
  teacher: [
    Permissions.CONTENT_TEXTBOOK_READ,
    Permissions.CONTENT_CHAPTER_CREATE,
    Permissions.CONTENT_CHAPTER_READ,
    Permissions.CONTENT_CHAPTER_UPDATE,
    Permissions.CONTENT_QUESTION_CREATE,
    Permissions.CONTENT_QUESTION_READ,
    Permissions.CONTENT_QUESTION_UPDATE,
    Permissions.CONTENT_KNOWLEDGE_POINT_CREATE,
    Permissions.CONTENT_KNOWLEDGE_POINT_READ,
    Permissions.CONTENT_KNOWLEDGE_POINT_UPDATE,
  ],
  student: [
    Permissions.CONTENT_TEXTBOOK_READ,
    Permissions.CONTENT_CHAPTER_READ,
    Permissions.CONTENT_QUESTION_READ,
    Permissions.CONTENT_KNOWLEDGE_POINT_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(", "));
  }
}
