import {
  Injectable,
  CanActivate,
  ExecutionContext,
  ForbiddenException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Permission, DEFAULT_ROLE_PERMISSIONS } from './permissions.enum';
import { PERMISSIONS_KEY, PERMISSIONS_ALL_KEY } from './permissions.decorator';
import { JwtPayload } from '../interfaces/jwt-payload.interface';

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

  canActivate(context: ExecutionContext): boolean {
    // Check for ANY permission match
    const requiredPermissions = this.reflector.getAllAndOverride<Permission[]>(
      PERMISSIONS_KEY,
      [context.getHandler(), context.getClass()],
    );

    // Check for ALL permissions match
    const requiredAllPermissions = this.reflector.getAllAndOverride<Permission[]>(
      PERMISSIONS_ALL_KEY,
      [context.getHandler(), context.getClass()],
    );

    // If no permissions are required, allow access
    if (
      (!requiredPermissions || requiredPermissions.length === 0) &&
      (!requiredAllPermissions || requiredAllPermissions.length === 0)
    ) {
      return true;
    }

    const request = context.switchToHttp().getRequest();
    const user = request.user as JwtPayload;

    if (!user || !user.roles) {
      throw new ForbiddenException('Access denied: No roles assigned');
    }

    // Collect all permissions from user's roles
    const userPermissions = this.getUserPermissions(user.roles);

    // Check ANY permission (at least one must match)
    if (requiredPermissions && requiredPermissions.length > 0) {
      const hasAnyPermission = requiredPermissions.some((permission) =>
        userPermissions.has(permission),
      );

      if (!hasAnyPermission) {
        throw new ForbiddenException(
          `Access denied: Requires one of: ${requiredPermissions.join(', ')}`,
        );
      }
    }

    // Check ALL permissions (all must match)
    if (requiredAllPermissions && requiredAllPermissions.length > 0) {
      const hasAllPermissions = requiredAllPermissions.every((permission) =>
        userPermissions.has(permission),
      );

      if (!hasAllPermissions) {
        throw new ForbiddenException(
          `Access denied: Requires all of: ${requiredAllPermissions.join(', ')}`,
        );
      }
    }

    return true;
  }

  /**
   * Get all permissions for a user based on their roles
   */
  private getUserPermissions(roles: string[]): Set<Permission> {
    const permissions = new Set<Permission>();

    for (const role of roles) {
      const rolePermissions = DEFAULT_ROLE_PERMISSIONS[role];
      if (rolePermissions) {
        rolePermissions.forEach((permission) => permissions.add(permission));
      }
    }

    return permissions;
  }
}
