import {
  CanActivate,
  ExecutionContext,
  ForbiddenException,
  Injectable,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { PERMISSIONS_KEY } from '../permissions.decorator';
import { UsersService } from '../../users/users.service';

/**
 * Authorisation is always resolved from the database - permissions presented
 * by the client (or embedded in the JWT) are never trusted.
 */
@Injectable()
export class PermissionsGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    private readonly usersService: UsersService,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const required = this.reflector.getAllAndOverride<string[]>(PERMISSIONS_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (!required || required.length === 0) return true;

    const request = context.switchToHttp().getRequest();
    const user = request.user;
    if (!user?.id) throw new ForbiddenException('Not authenticated');

    const full = await this.usersService.findById(user.id);
    if (!full || !full.isActive) {
      throw new ForbiddenException('Account is deactivated');
    }

    const granted = new Set(
      (full.roles || []).flatMap((role) =>
        (role.permissions || []).map((permission) => permission.name),
      ),
    );

    const missing = required.filter((permission) => !granted.has(permission));
    if (missing.length > 0) {
      throw new ForbiddenException(`Missing permission: ${missing.join(', ')}`);
    }

    // Downstream handlers can rely on the resolved permission set.
    request.user.permissions = Array.from(granted);
    return true;
  }
}
