// src/server/shared/plugins/rbac.ts
// Role-Based Access Control (RBAC) plugin with permission registry

import fp from 'fastify-plugin';
import { FastifyRequest, FastifyReply } from 'fastify';
import { UserRole, UserInfo } from '../lib/auth';
import { AuthorizationError } from '../lib/auth';
import { USER_ROLES } from '@shared/constants/roles';

/**
 * Application permissions enum
 * 
 * Naming convention: 'resource:action'
 * - resource: The entity being accessed (e.g., rooms, notes, tasks)
 * - action: The operation being performed (read, write, delete, manage)
 */
export enum AppPermission {
  // Rooms permissions
  ROOMS_READ = 'rooms:read',
  ROOMS_WRITE = 'rooms:write',
  ROOMS_DELETE = 'rooms:delete',
  ROOMS_MANAGE = 'rooms:manage',

  // Notes permissions
  NOTES_READ = 'notes:read',
  NOTES_WRITE = 'notes:write',
  NOTES_DELETE = 'notes:delete',
  NOTES_MANAGE = 'notes:manage',

  // Tasks permissions
  TASKS_READ = 'tasks:read',
  TASKS_WRITE = 'tasks:write',
  TASKS_DELETE = 'tasks:delete',
  TASKS_MANAGE = 'tasks:manage',

  // Schedule permissions
  SCHEDULE_READ = 'schedule:read',
  SCHEDULE_WRITE = 'schedule:write',
  SCHEDULE_DELETE = 'schedule:delete',
  SCHEDULE_MANAGE = 'schedule:manage',

  // Personnel permissions
  PERSONNEL_READ = 'personnel:read',
  PERSONNEL_WRITE = 'personnel:write',
  PERSONNEL_DELETE = 'personnel:delete',
  PERSONNEL_MANAGE = 'personnel:manage',

  // Paymaster permissions
  PAYMASTER_READ = 'paymaster:read',
  PAYMASTER_WRITE = 'paymaster:write',
  PAYMASTER_DELETE = 'paymaster:delete',
  PAYMASTER_MANAGE = 'paymaster:manage',

  // Operations permissions
  OPERATIONS_READ = 'operations:read',
  OPERATIONS_WRITE = 'operations:write',
  OPERATIONS_DELETE = 'operations:delete',
  OPERATIONS_MANAGE = 'operations:manage',

  // Reference books permissions
  REFBOOKS_READ = 'refbooks:read',
  REFBOOKS_WRITE = 'refbooks:write',
  REFBOOKS_DELETE = 'refbooks:delete',
  REFBOOKS_MANAGE = 'refbooks:manage',

  // Approval permissions
  APPROVAL_READ = 'approval:read',
  APPROVAL_WRITE = 'approval:write',
  APPROVAL_DELETE = 'approval:delete',
  APPROVAL_MANAGE = 'approval:manage',

  // Notifications permissions
  NOTIFICATIONS_READ = 'notifications:read',
  NOTIFICATIONS_WRITE = 'notifications:write',
  NOTIFICATIONS_DELETE = 'notifications:delete',
  NOTIFICATIONS_MANAGE = 'notifications:manage',
}

/**
 * Role to permissions mapping
 *
 * ADMIN: All permissions
 * MANAGER: Most permissions except personnel:delete and paymaster:delete
 * MAID: Read-only permissions for most resources, write for notes and tasks
 * GOD: All permissions (bypasses checks in hasPermission functions)
 */
const ROLE_PERMISSIONS: Record<UserRole, AppPermission[]> = {
  ADMIN: Object.values(AppPermission),
  GOD: Object.values(AppPermission),
  MANAGER: [
    // Rooms
    AppPermission.ROOMS_READ,
    AppPermission.ROOMS_WRITE,
    AppPermission.ROOMS_MANAGE,
    // Notes
    AppPermission.NOTES_READ,
    AppPermission.NOTES_WRITE,
    AppPermission.NOTES_MANAGE,
    // Tasks
    AppPermission.TASKS_READ,
    AppPermission.TASKS_WRITE,
    AppPermission.TASKS_MANAGE,
    // Schedule
    AppPermission.SCHEDULE_READ,
    AppPermission.SCHEDULE_WRITE,
    AppPermission.SCHEDULE_MANAGE,
    // Personnel
    AppPermission.PERSONNEL_READ,
    AppPermission.PERSONNEL_WRITE,
    AppPermission.PERSONNEL_MANAGE,
    // Paymaster
    AppPermission.PAYMASTER_READ,
    AppPermission.PAYMASTER_WRITE,
    AppPermission.PAYMASTER_MANAGE,
    // Operations
    AppPermission.OPERATIONS_READ,
    AppPermission.OPERATIONS_WRITE,
    AppPermission.OPERATIONS_MANAGE,
    // Reference books
    AppPermission.REFBOOKS_READ,
    AppPermission.REFBOOKS_WRITE,
    AppPermission.REFBOOKS_MANAGE,
    // Approval
    AppPermission.APPROVAL_READ,
    AppPermission.APPROVAL_WRITE,
    AppPermission.APPROVAL_MANAGE,
    // Notifications
    AppPermission.NOTIFICATIONS_READ,
    AppPermission.NOTIFICATIONS_WRITE,
    AppPermission.NOTIFICATIONS_MANAGE,
  ],
  MAID: [
    // Rooms
    AppPermission.ROOMS_READ,
    // Notes
    AppPermission.NOTES_READ,
    AppPermission.NOTES_WRITE,
    // Tasks
    AppPermission.TASKS_READ,
    AppPermission.TASKS_WRITE,
    // Schedule
    AppPermission.SCHEDULE_READ,
    // Operations
    AppPermission.OPERATIONS_READ,
  ],
};

/**
 * Check if a user has a specific permission
 * 
 * @param user - User info from JWT
 * @param permission - Permission to check
 * @returns True if user has the permission
 */
export function hasPermission(
  user: UserInfo | undefined,
  permission: AppPermission
): boolean {
  // GOD role has all permissions (global bypass)
  if (user?.role === USER_ROLES.GOD) return true;

  if (!user) {
    return false;
  }

  const permissions = ROLE_PERMISSIONS[user.role] || [];
  return permissions.includes(permission);
}

/**
 * Check if a user has any of the specified permissions
 * 
 * @param user - User info from JWT
 * @param permissions - Permissions to check
 * @returns True if user has any of the permissions
 */
export function hasAnyPermission(
  user: UserInfo | undefined,
  permissions: AppPermission[]
): boolean {
  // GOD role has all permissions (global bypass)
  if (user?.role === USER_ROLES.GOD) return true;

  if (!user) {
    return false;
  }

  const userPermissions = ROLE_PERMISSIONS[user.role] || [];
  return permissions.some((permission) => userPermissions.includes(permission));
}

/**
 * Check if a user has all of the specified permissions
 * 
 * @param user - User info from JWT
 * @param permissions - Permissions to check
 * @returns True if user has all of the permissions
 */
export function hasAllPermissions(
  user: UserInfo | undefined,
  permissions: AppPermission[]
): boolean {
  // GOD role has all permissions (global bypass)
  if (user?.role === USER_ROLES.GOD) return true;

  if (!user) {
    return false;
  }

  const userPermissions = ROLE_PERMISSIONS[user.role] || [];
  return permissions.every((permission) => userPermissions.includes(permission));
}

/**
 * RBAC plugin for Fastify
 * 
 * Adds a `.can()` decorator to check permissions
 */
export default fp(async (fastify) => {
  /**
   * Decorator to check if current user has a specific permission
   * 
   * @param permission - Permission to check
   * @returns True if user has the permission
   * 
   * @example
   * ```ts
   * fastify.get('/api/rooms', { onRequest: [fastify.authenticate, fastify.can(AppPermission.ROOMS_READ)] }, async (request, reply) => {
   *   // Handler logic
   * });
   * ```
   */
  // @ts-expect-error - Changed return type from boolean to Promise<void> to ensure hook completes
  fastify.decorate('can', (permission: string) => {
    return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
      // Get user from request (set by JWT plugin)
      const user = request.user as UserInfo | undefined;

      if (!user) {
        throw new AuthorizationError('Authentication required', 401);
      }

      const allowed = hasPermission(user, permission as AppPermission);
      if (!allowed) {
        throw new AuthorizationError(`Missing permission: ${permission}`, 403);
      }
      // Явный возврат из async функции разрешает запрос
      return;
    };
  });

  /**
   * Decorator to check if current user has any of the specified permissions
   * 
   * @param permissions - Permissions to check
   * @returns True if user has any of the permissions
   * 
   * @example
   * ```ts
   * fastify.get('/api/notes', { onRequest: [fastify.authenticate, fastify.canAny(AppPermission.NOTES_READ, AppPermission.NOTIFICATIONS_READ)] }, async (request, reply) => {
   *   // Handler logic
   * });
   * ```
   */
  // @ts-expect-error - Changed return type from boolean to Promise<void> to ensure hook completes
  fastify.decorate('canAny', (...permissions: AppPermission[]) => {
    return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
      const user = request.user as UserInfo | undefined;

      if (!user) {
        throw new AuthorizationError('Authentication required', 401);
      }

      const allowed = hasAnyPermission(user, permissions);
      if (!allowed) {
        throw new AuthorizationError(`Missing one of permissions: ${permissions.join(', ')}`, 403);
      }
      // Явный возврат из async функции разрешает запрос
      return;
    };
  });

  /**
   * Decorator to check if current user has all of the specified permissions
   * 
   * @param permissions - Permissions to check
   * @returns True if user has all of the permissions
   * 
   * @example
   * ```ts
   * fastify.post('/api/rooms', { onRequest: [fastify.authenticate, fastify.canAll(AppPermission.ROOMS_WRITE, AppPermission.OPERATIONS_WRITE)] }, async (request, reply) => {
   *   // Handler logic
   * });
   * ```
   */
  // @ts-expect-error - Changed return type from boolean to Promise<void> to ensure hook completes
  fastify.decorate('canAll', (...permissions: AppPermission[]) => {
    return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
      const user = request.user as UserInfo | undefined;

      if (!user) {
        throw new AuthorizationError('Authentication required', 401);
      }

      const allowed = hasAllPermissions(user, permissions);
      if (!allowed) {
        throw new AuthorizationError(`Missing all required permissions: ${permissions.join(', ')}`, 403);
      }
      // Явный возврат из async функции разрешает запрос
      return;
    };
  });

  /**
   * Helper method to check permissions without throwing errors
   * 
   * @param permission - Permission to check
   * @returns True if user has the permission
   * 
   * @example
   * ```ts
   * const canDelete = fastify.checkPermission(AppPermission.ROOMS_DELETE);
   * if (canDelete) {
   *   // Show delete button
   * }
   * ```
   */
  fastify.decorate('checkPermission', (permission: AppPermission): boolean => {
    // This is intended for use in route handlers where request.user is available
    // For use outside of request context, pass user explicitly
    return true; // Placeholder - actual check requires user context
  });
}, { name: 'rbac' });
