import { Injectable, Inject, ForbiddenException } from "@nestjs/common";
import type { CanActivate, ExecutionContext } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { Reflector } from "@nestjs/core";
import type { Request } from "express";

import { REQUIRED_PERMISSIONS_KEY } from "../../../decorators/permissions.decorator.js";
import { hasAnyPermission, type Permission } from "@customer-portal/domain/auth";

interface UserWithRole {
  id: string;
  role?: string;
}

type RequestWithUser = Request & { user?: UserWithRole };

/**
 * Guard to enforce permission-based access control.
 *
 * This guard checks if the authenticated user has any of the required
 * permissions specified via the @RequirePermissions decorator.
 *
 * Note: This guard must be used after the GlobalAuthGuard, which
 * attaches the user to the request.
 */
@Injectable()
export class PermissionsGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredPermissions = this.reflector.getAllAndOverride<Permission[] | undefined>(
      REQUIRED_PERMISSIONS_KEY,
      [context.getHandler(), context.getClass()]
    );

    // No permissions required - allow access
    if (!requiredPermissions || requiredPermissions.length === 0) {
      return true;
    }

    const request = context.switchToHttp().getRequest<RequestWithUser>();
    const user = request.user;

    if (!user) {
      this.logger.warn("Permissions check failed - no user on request");
      throw new ForbiddenException("Access denied");
    }

    if (!user.role) {
      this.logger.warn("User has no role assigned", { userId: user.id });
      throw new ForbiddenException("User role not configured");
    }

    const hasAccess = hasAnyPermission(user.role, requiredPermissions);

    if (!hasAccess) {
      this.logger.warn("Permission denied", {
        userId: user.id,
        role: user.role,
        requiredPermissions,
      });
      throw new ForbiddenException("Insufficient permissions");
    }

    return true;
  }
}
